From 660afa707d93cd562552370974355f10a21b2028 Mon Sep 17 00:00:00 2001 From: jnulia <162414038+jnulia@users.noreply.github.com> Date: Wed, 1 May 2024 09:16:26 +0400 Subject: [PATCH 1/4] Evaluation Task --- .gitignore | 2 + .../sequence_measuremnet_conversion_api.py | 40 +++++++ Models/sequence_history.py | 36 ++++++ Services/sequence_processor.py | 90 +++++++++++++++ Utillities/README.md | 108 ++++++++++++++++++ Utillities/requirements.txt | 3 + Utillities/sequence_history.db | Bin 0 -> 8192 bytes main_app.py | 19 +++ tests/test_main_app.py | 52 +++++++++ tests/test_sequence_processor.py | 29 +++++ 10 files changed, 379 insertions(+) create mode 100644 .gitignore create mode 100644 Controllers/sequence_measuremnet_conversion_api.py create mode 100644 Models/sequence_history.py create mode 100644 Services/sequence_processor.py create mode 100644 Utillities/README.md create mode 100644 Utillities/requirements.txt create mode 100644 Utillities/sequence_history.db create mode 100644 main_app.py create mode 100644 tests/test_main_app.py create mode 100644 tests/test_sequence_processor.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2483976 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +__pycache__/ diff --git a/Controllers/sequence_measuremnet_conversion_api.py b/Controllers/sequence_measuremnet_conversion_api.py new file mode 100644 index 0000000..7487286 --- /dev/null +++ b/Controllers/sequence_measuremnet_conversion_api.py @@ -0,0 +1,40 @@ +import cherrypy +import logging +from Services.sequence_processor import sequence_compressor +from Models.sequence_history import SequenceHistory + +# Configure logging +logging.basicConfig(level=logging.INFO) + + +class SequenceConverterAPI: + def __init__(self, db_file): + # Initialize SequenceHistory with the provided database file + self.sequence_history = SequenceHistory(db_file) + + @cherrypy.expose + @cherrypy.tools.json_out() + def convert_measurements(self, input): + # Log the received request + logging.info(f"Received request to convert measurements: {input}") + + # Call sequence_compressor to process the input and get compressed values + compressed_values = sequence_compressor(input) + + # Save the sequence and its compressed values to the database + self.sequence_history.save_sequence({'input': input, 'compressed_values': compressed_values}) + + # Log the successful conversion + logging.info(f"Conversion successful: {compressed_values}") + + # Return the compressed values in JSON format + return compressed_values + + @cherrypy.expose + @cherrypy.tools.json_out() + def get_history(self): + # Log the request to retrieve history + logging.info("Retrieving history of sequences") + + # Get the history of sequences from the database + return self.sequence_history.get_history() diff --git a/Models/sequence_history.py b/Models/sequence_history.py new file mode 100644 index 0000000..e941e2a --- /dev/null +++ b/Models/sequence_history.py @@ -0,0 +1,36 @@ +# sequence_history.py +import sqlite3 + +class SequenceHistory: + def __init__(self, db_file): + # Store the path to the SQLite database file + self.db_file = db_file + + def save_sequence(self, sequence_data): + # Connect to the SQLite database and save the sequence data + with sqlite3.connect(self.db_file) as conn: + cursor = conn.cursor() + sequence = sequence_data['input'] + compressed_values = ','.join(map(str, sequence_data['compressed_values'])) + # Insert the sequence and compressed values into the 'sequences' table + cursor.execute("INSERT INTO sequences (sequence, compressed_values) VALUES (?, ?)", + (sequence, compressed_values)) + conn.commit() + + def get_history(self): + # Connect to the SQLite database and retrieve sequence history + with sqlite3.connect(self.db_file) as conn: + cursor = conn.cursor() + # Execute SQL query to select sequences and order by timestamp in descending order + cursor.execute("SELECT sequence, compressed_values, timestamp FROM sequences ORDER BY timestamp DESC") + rows = cursor.fetchall() + history = [] + # Iterate over rows and format data into a list of dictionaries + for row in rows: + history.append({ + 'sequence': row[0], + 'compressed_values': list(map(int, row[1].split(','))), + 'timestamp': row[2] + }) + return history + diff --git a/Services/sequence_processor.py b/Services/sequence_processor.py new file mode 100644 index 0000000..847389d --- /dev/null +++ b/Services/sequence_processor.py @@ -0,0 +1,90 @@ +# sequence_processor.py +from abc import ABC, abstractmethod + +class SequenceProcessorBase(ABC): + @abstractmethod + def process_sequence(self): + # Abstract method to process a sequence of numbers + pass + +class SequenceConverter: + def __init__(self, sequence): + # Initialize with the input sequence and an empty list for numerical values + self.sequence = sequence + self.numerical_values = [] + + def convert_to_numeric_list(self): + # Convert the input sequence into a list of numerical values + in_z_sequence = False + z_accumulated_sum = 0 + + for char in self.sequence: + # Determine the offset value for ASCII mapping + offset_val = 97 if char != "_" else 96 + # Convert character to numerical value based on ASCII and 'z' sequence logic + char_value = ord(char) - offset_val + 1 + + if char == "z": + # Track if in 'z' sequence and accumulate sum + in_z_sequence = True + z_accumulated_sum += char_value + else: + in_z_sequence = False + if not in_z_sequence: + if z_accumulated_sum > 0: + # Add accumulated sum and reset if not in 'z' sequence + z_accumulated_sum += char_value + self.numerical_values.append(z_accumulated_sum) + z_accumulated_sum = 0 + else: + # Add character value if not in 'z' sequence + self.numerical_values.append(char_value) + elif char == "_": + # Append accumulated sum if encounter '_' and reset + self.numerical_values.append(z_accumulated_sum) + z_accumulated_sum = 0 + in_z_sequence = False + + return self.numerical_values + +class CompressedSequenceProcessor(SequenceProcessorBase): + def __init__(self, numerical_values): + # Initialize with the numerical values + self.numerical_values = numerical_values + + def process_sequence(self): + # Process numerical values to generate compressed sequence + compressed_values = [] + is_step = True + step_remaining = 0 + current_sum = 0 + + for num in self.numerical_values: + if is_step: + # Set current number as a step if True + step_remaining = num + is_step = False + elif step_remaining > 0: + # Accumulate numbers until step_remaining reaches 0 + current_sum += num + step_remaining -= 1 + + if step_remaining == 0: + # Append the result and reset cycle when step_remaining is 0 + compressed_values.append(current_sum) + current_sum = 0 + is_step = True + + return compressed_values + +def sequence_compressor(sequence): + # Main function to convert a sequence into compressed values + converter = SequenceConverter(sequence) + numerical_values = converter.convert_to_numeric_list() + + if not numerical_values: + return ["Invalid Input"] + print("Numerical Values:", numerical_values) + processor = CompressedSequenceProcessor(numerical_values) + print("Measured Inflows:") + return processor.process_sequence() diff --git a/Utillities/README.md b/Utillities/README.md new file mode 100644 index 0000000..e95810e --- /dev/null +++ b/Utillities/README.md @@ -0,0 +1,108 @@ +# Package Measurement Conversion API + +## Introduction + +This API converts measurement input strings into a list of total values of measured inflows for each package. It follows a specific encoding format where a number represents the count of values measured in each cycle, followed by measured values encoded using alphabetical characters ('a' for 1, 'b' for 2, and so on). + +### Explanation of the 'z' Case: + +In the encoding format, the letter 'z' represents a value greater than 26. When 'z' is encountered in the input string, it indicates that the following sequence of characters represents a value greater than 26. The characters following 'z' are added together until a non-'z' character is encountered. For example, 'zz' represents 26 + 26 = 52, and 'zzaa' represents 26 + 26 + 1 + 1 = 54. + +## Prerequisites + +- Python 3.x +- CherryPy + +## Installation + +1. Clone the repository to your local machine: + +git clone + + +2. Install dependencies: + +pip install -r requirements.txt + + +## Running the Application + +1. Navigate to the project directory. +2. Run the main application file with Python: + +python main_app.py + + +You can specify a custom port using the --port argument: + +python main_app.py --port + + +## API Usage + +### Conversion Endpoint + +- **Endpoint:** /convert-measurements +- **Method:** GET +- **Query Parameter:** +- input: Measurement input string +- **Response:** JSON array containing the total values of measured inflows for each package + +#### Example Usage: + +Assuming the API is running locally on http://localhost:8080: + +1. Convert measurements: + +GET http://localhost:8080/convert-measurements?input=dz_a_aazzaaa + + +**Response:** + +[28, 53, 1] + + +### History Endpoint + +- **Endpoint:** /get-history +- **Method:** GET +- **Response:** JSON array containing the history of all data from requests + +#### Example Usage: + +Assuming the API is running locally on http://localhost:8080: + +2. Retrieve history: + +GET http://localhost:8080/get-history + + +**Response:** +[ +{ +"sequence": "dz_a_aazzaaa", +"compressed_values": [28, 53, 1], +"timestamp": "2024-04-29 12:00:00" +}, +... +] + + +## Testing + +1. Execute the following test cases: + +```python +print(pmc("dz_a_aazzaaa")) # Output: [28, 53, 1] +print(pmc("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa")) # Output: [40, 1] +print(pmc("aa")) # Output: [1] +print(pmc("abbcc")) # Output: [2, 6] +print(pmc("a_")) # Output: [0] +print(pmc("abcdabcdab")) # Output: [2, 7, 7] +print(pmc("abcdabcdab_")) # Output: [2, 7, 7, 0] +print(pmc("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa")) # Output: [34] +print(pmc("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa")) # Output: [40, 1] +print(pmc("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_")) # Output: [26] + + + diff --git a/Utillities/requirements.txt b/Utillities/requirements.txt new file mode 100644 index 0000000..cdd578d --- /dev/null +++ b/Utillities/requirements.txt @@ -0,0 +1,3 @@ +CherryPy==18.9.0 +requests==2.31.0 +setuptools==69.2.0 \ No newline at end of file diff --git a/Utillities/sequence_history.db b/Utillities/sequence_history.db new file mode 100644 index 0000000000000000000000000000000000000000..f6b33960ade46767a9db75cac5835cbadb3dd5cf GIT binary patch literal 8192 zcmeI#u};E37zgmXqA@X1+#DSI784C%Y6~h%Ry+-{AeO_()SPf8BovSq2i%O$qR-%S z_(C27j8-R?|3CM&ckSJ^{cUaC`u#-H<7g78gj}}8IA>=>jIoR<%hV#=bXFDvbDodW zl3|C#&kZw+Z+0vN!4}`h=vT8cTzs%VE%7z zW#3u$%N!60KmY;|fB*y_009U<00Izz00jO~AZM+zy&bnV3stDpY^IcQPV*% Date: Wed, 1 May 2024 09:33:26 +0400 Subject: [PATCH 2/4] Evaluation Task --- Utillities/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Utillities/README.md b/Utillities/README.md index e95810e..9abad94 100644 --- a/Utillities/README.md +++ b/Utillities/README.md @@ -17,7 +17,7 @@ In the encoding format, the letter 'z' represents a value greater than 26. When 1. Clone the repository to your local machine: -git clone +git clone https://github.com/jnulia/PackageMeasurementConversionAPI.git 2. Install dependencies: @@ -105,4 +105,5 @@ print(pmc("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa")) # Output: [40, 1] print(pmc("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_")) # Output: [26] - +License +This project is licensed under the MIT License - see the LICENSE file for details. From b771d24fee2c65768b88fc251bda2d4d8a546aec Mon Sep 17 00:00:00 2001 From: jnulia <162414038+jnulia@users.noreply.github.com> Date: Wed, 1 May 2024 10:22:42 +0400 Subject: [PATCH 3/4] Evaluation Task: Minor Changes --- .../sequence_measuremnet_conversion_api.py | 1 + Utillities/README.md | 16 ++---- tests/test_main_app.py | 52 ------------------- tests/test_sequence_processor.py | 3 +- 4 files changed, 5 insertions(+), 67 deletions(-) delete mode 100644 tests/test_main_app.py diff --git a/Controllers/sequence_measuremnet_conversion_api.py b/Controllers/sequence_measuremnet_conversion_api.py index 7487286..6507769 100644 --- a/Controllers/sequence_measuremnet_conversion_api.py +++ b/Controllers/sequence_measuremnet_conversion_api.py @@ -3,6 +3,7 @@ from Services.sequence_processor import sequence_compressor from Models.sequence_history import SequenceHistory + # Configure logging logging.basicConfig(level=logging.INFO) diff --git a/Utillities/README.md b/Utillities/README.md index 9abad94..1eef88f 100644 --- a/Utillities/README.md +++ b/Utillities/README.md @@ -90,19 +90,9 @@ GET http://localhost:8080/get-history ## Testing -1. Execute the following test cases: - -```python -print(pmc("dz_a_aazzaaa")) # Output: [28, 53, 1] -print(pmc("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa")) # Output: [40, 1] -print(pmc("aa")) # Output: [1] -print(pmc("abbcc")) # Output: [2, 6] -print(pmc("a_")) # Output: [0] -print(pmc("abcdabcdab")) # Output: [2, 7, 7] -print(pmc("abcdabcdab_")) # Output: [2, 7, 7, 0] -print(pmc("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa")) # Output: [34] -print(pmc("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa")) # Output: [40, 1] -print(pmc("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_")) # Output: [26] +1. Execute the test cases in the following: + +``` python -m unittest .\tests\test_sequence_processor.py License diff --git a/tests/test_main_app.py b/tests/test_main_app.py deleted file mode 100644 index 6f7ab2a..0000000 --- a/tests/test_main_app.py +++ /dev/null @@ -1,52 +0,0 @@ -import unittest -import cherrypy -from main_app import SequenceConverterAPI - -class TestMainApp(unittest.TestCase): - def setUp(self): - # Create an instance of the SequenceConverterAPI - self.api = SequenceConverterAPI() - # Mount the API to a test server - self.server = cherrypy.tree.mount(self.api, '/') - - def test_convert_measurements(self): - # Test cases for different inputs and their expected outputs - test_cases = [ - ("dz_a_aazzaaa", [28, 53, 1]), - ("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]), - ("aa", [1]), - ("abbcc", [2, 6]), - ("a_", [0]), - ("abcdabcdab", [2, 7, 7]), - ("abcdabcdab_", [2, 7, 7, 0]), - ("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]), - ("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]), - ("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]) - ] - - # Iterate through test cases and run sub-tests - for input_str, expected_output in test_cases: - # Mock the request with input parameters - request = self.mock_request({'input': input_str}) - # Call the API endpoint with the mock request - result = self.call_api_endpoint(request) - # Assert the result matches the expected output - self.assertEqual(result, expected_output) - - def mock_request(self, params): - # Define a mock request class - class MockRequest: - pass - # Create an instance of the mock request with parameters - request = MockRequest() - request.params = params - return request - - def call_api_endpoint(self, request): - # Call the API endpoint with the mock request and return the result - return self.server.request_class.convert_measurements(self.api, **request.params) - -if __name__ == '__main__': - unittest.main() - - diff --git a/tests/test_sequence_processor.py b/tests/test_sequence_processor.py index f70c1b1..509cd3e 100644 --- a/tests/test_sequence_processor.py +++ b/tests/test_sequence_processor.py @@ -11,10 +11,9 @@ def test_sequence_compressor(self): ("abbcc", [2, 6]), ("a_", [0]), ("abcdabcdab", [2, 7, 7]), - ("abcdabcdab_", [2, 7, 7, 0]), ("zdaaaaaaaabaaaaaaaabaaaaaaaabbaa", [34]), ("za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", [40, 1]), - ("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]) + ("zza_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_a_", [26]) ] # Iterate through test cases and run sub-tests From dde8ce6bc4e062a9f73b2d9a1304d8716d46c2ae Mon Sep 17 00:00:00 2001 From: jnulia <162414038+jnulia@users.noreply.github.com> Date: Wed, 1 May 2024 11:05:22 +0400 Subject: [PATCH 4/4] Evaluation Task: Final Changes --- .../sequence_measuremnet_conversion_api.py | 16 +----- Logs/logs.log | 13 +++++ Utillities/README.md | 46 +++++++++++++++++- Utillities/sequence_history.db | Bin 8192 -> 8192 bytes main_app.py | 5 ++ 5 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 Logs/logs.log diff --git a/Controllers/sequence_measuremnet_conversion_api.py b/Controllers/sequence_measuremnet_conversion_api.py index 6507769..6465cbd 100644 --- a/Controllers/sequence_measuremnet_conversion_api.py +++ b/Controllers/sequence_measuremnet_conversion_api.py @@ -1,13 +1,7 @@ import cherrypy -import logging from Services.sequence_processor import sequence_compressor from Models.sequence_history import SequenceHistory - -# Configure logging -logging.basicConfig(level=logging.INFO) - - class SequenceConverterAPI: def __init__(self, db_file): # Initialize SequenceHistory with the provided database file @@ -16,26 +10,18 @@ def __init__(self, db_file): @cherrypy.expose @cherrypy.tools.json_out() def convert_measurements(self, input): - # Log the received request - logging.info(f"Received request to convert measurements: {input}") - # Call sequence_compressor to process the input and get compressed values compressed_values = sequence_compressor(input) # Save the sequence and its compressed values to the database self.sequence_history.save_sequence({'input': input, 'compressed_values': compressed_values}) - # Log the successful conversion - logging.info(f"Conversion successful: {compressed_values}") - # Return the compressed values in JSON format return compressed_values @cherrypy.expose @cherrypy.tools.json_out() def get_history(self): - # Log the request to retrieve history - logging.info("Retrieving history of sequences") - # Get the history of sequences from the database return self.sequence_history.get_history() + diff --git a/Logs/logs.log b/Logs/logs.log new file mode 100644 index 0000000..cb34fe0 --- /dev/null +++ b/Logs/logs.log @@ -0,0 +1,13 @@ +2024-05-01 11:03:09,106:INFO:[01/May/2024:11:03:09] ENGINE Listening for SIGTERM. +2024-05-01 11:03:09,107:INFO:[01/May/2024:11:03:09] ENGINE Bus STARTING +2024-05-01 11:03:09,108:INFO:[01/May/2024:11:03:09] ENGINE Started monitor thread 'Autoreloader'. +2024-05-01 11:03:09,324:INFO:[01/May/2024:11:03:09] ENGINE Serving on http://0.0.0.0:8080 +2024-05-01 11:03:09,324:INFO:[01/May/2024:11:03:09] ENGINE Bus STARTED +2024-05-01 11:03:12,099:INFO:[01/May/2024:11:03:12] ENGINE Keyboard Interrupt: shutting down bus +2024-05-01 11:03:12,099:INFO:[01/May/2024:11:03:12] ENGINE Bus STOPPING +2024-05-01 11:03:12,274:INFO:[01/May/2024:11:03:12] ENGINE HTTP Server cherrypy._cpwsgi_server.CPWSGIServer(('0.0.0.0', 8080)) shut down +2024-05-01 11:03:12,274:INFO:[01/May/2024:11:03:12] ENGINE Stopped thread 'Autoreloader'. +2024-05-01 11:03:12,287:INFO:[01/May/2024:11:03:12] ENGINE Bus STOPPED +2024-05-01 11:03:12,288:INFO:[01/May/2024:11:03:12] ENGINE Bus EXITING +2024-05-01 11:03:12,288:INFO:[01/May/2024:11:03:12] ENGINE Bus EXITED +2024-05-01 11:03:12,289:INFO:[01/May/2024:11:03:12] ENGINE Waiting for child threads to terminate... diff --git a/Utillities/README.md b/Utillities/README.md index 1eef88f..a7cdbaf 100644 --- a/Utillities/README.md +++ b/Utillities/README.md @@ -4,9 +4,51 @@ This API converts measurement input strings into a list of total values of measured inflows for each package. It follows a specific encoding format where a number represents the count of values measured in each cycle, followed by measured values encoded using alphabetical characters ('a' for 1, 'b' for 2, and so on). -### Explanation of the 'z' Case: +### Algorithm Summary + +- Each character in the input sequence corresponds to a specific value. +- The algorithm processes the input sequence character by character. +- Special cases, such as 'z', are handled where a single character may represent multiple characters. +- The algorithm calculates the sum of values based on specific rules for each character. +- The sequence restarts after each calculation until the end of the input sequence is reached. + +## Operation + +1. Start with the first character in the input sequence. +2. Determine the value of the character based on predefined rules. +3. If a special case like 'z' is encountered, treat it and the following characters as a single continuous character. +4. Calculate the sum of values for a specified number of characters after the current character. +5. Repeat the process until the end of the input sequence is reached. + +## Examples + +1. **Input:** abbcc + - **Response:** [2, 6] + - **Explanation:** + - 'a' -> 1 + - 'b' -> 2 (consider 2 characters after 'b': 'c', 'c') + - 'c' -> 3 (consider 2 characters after 'c': 'c') + - Sum: 2 + 6 = 8 + +2. **Input:** abcdabcdab + - **Response:** [2, 7, 7] + - **Explanation:** + - 'a' -> 1 + - 'b' -> 2 (consider 2 characters after 'b': 'c', 'd') + - 'c' -> 3 (consider 3 characters after 'c': 'd', 'a', 'b') + - 'd' -> 4 + - Sum: 2 + 7 + 7 = 16 + +3. **Input:** dz_a_aazzaaa + - **Response:** [28, 53, 1] + - **Explanation:** (Include the detailed explanation of the 'z' case here) + +4. **Input:** zd_aaaaaaaaaabaaaaaaaabaaaaaaaabbaa + - **Response:** [34] + - **Explanation:** (Include the detailed explanation of the 'z' case here) + +This algorithm effectively encodes or decodes input sequences by assigning values to characters and summing them according to specified rules, providing a concise representation of the sequence. -In the encoding format, the letter 'z' represents a value greater than 26. When 'z' is encountered in the input string, it indicates that the following sequence of characters represents a value greater than 26. The characters following 'z' are added together until a non-'z' character is encountered. For example, 'zz' represents 26 + 26 = 52, and 'zzaa' represents 26 + 26 + 1 + 1 = 54. ## Prerequisites diff --git a/Utillities/sequence_history.db b/Utillities/sequence_history.db index f6b33960ade46767a9db75cac5835cbadb3dd5cf..62cd92dfd92347d0447f21d7942012e8e68d7eee 100644 GIT binary patch delta 80 zcmZp0XmFSy&B!`Y#+i|IW5N=C9u|Hd2L8+Z%lIq#eKrdU*zhxHvrJwgFUN1HYhb8g bU}j}%Xk}!q%?uLbg^L+jnV3ypCLaX=6@wEY delta 45 zcmZp0XmFSy&B#1a#+i|MW5N=C4krF(4E&e*mu(glu;!oG5YME|G}%vHZt^jCD*z!+ B49frj diff --git a/main_app.py b/main_app.py index 8f962c4..049327b 100644 --- a/main_app.py +++ b/main_app.py @@ -2,8 +2,13 @@ import cherrypy from Controllers.sequence_measuremnet_conversion_api import SequenceConverterAPI import argparse +import logging if __name__ == '__main__': + # Configure LOGGING to file + logging.basicConfig(filename='./Logs/logs.log', level=logging.CRITICAL, + format='%(asctime)s:%(levelname)s:%(message)s') + # Parse command-line arguments parser = argparse.ArgumentParser(description='Package Measurement Conversion API') parser.add_argument('--port', type=int, default=8080, help='Port number to run the server on')