Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__pycache__/
.idea/
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Package Conversion API Overview
The Package Conversion API functions is a web-based service designed to facilitate the conversion of package measurements as per user specifications. It offers endpoints specifically for converting measurements and accessing conversion records.

## Features
- **Seamless Handling of Sequence Input:** Capable of processing sequences comprising characters and underscores, facilitating smooth conversion.
- **Optimized Conversion Algorithms:** Incorporates streamlined algorithms for the swift transformation of input sequences into lists of measurements.
- **Transparent and Flexible:** Exhibits clarity in design and is easily customizable, allowing for seamless integration of supplementary features.

## Installation
Clone the repository:
```
git clone https://github.com/aljab017/PackageMeasurementConversionAPI.git
```

## Install dependencies:
```
pip install -r requirements.txt
```

## Usage
- Start the server:
```python main.py```
- Convert measurement:
```GET http://127.0.0.1:8080/convert_measurements?input_str=INPUT STRING HERE```
- Get conversion history
```GET http://127.0.0.1:8080/get_history```
## Testing

- To run tests:
``` python -m unittest .\models\tests\measurement_converter_tests.py ```
42 changes: 42 additions & 0 deletions controller/measurement_converter_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import cherrypy
import json
from services.measurement_converter import MeasurementConverter
from utils.measurement_converter_db import MeasurementConverterDB
from models.measurement_history import MeasurementHistory

class MeasurementConverterAPI(object):

@cherrypy.expose
@cherrypy.tools.json_in()
@cherrypy.tools.json_out()
def convert_measurements(self, input_str=None):
"""
Convert the measurements to the sum of letter values for each segment.
"""
db_entry = MeasurementHistory
db_entry.input = input_str
try:
if input_str is None:
raise cherrypy.HTTPError(400, "Missing input parameter")
db_entry.output = MeasurementConverter().package_measurement_converter(input_str)
MeasurementConverterDB().save_to_history(db_entry.input, db_entry.output)
error_msg = "Invalid string input" if db_entry.output == "Invalid string input" else ""
status = "SUCCESS" if error_msg == "" else "FAIL"
result = db_entry.output if error_msg == "" else []
return {"status": status, "error_msg": error_msg, "result": result}
except Exception as e:
return {"status": "FAIL", "error_msg": str(e), "result": []}

@cherrypy.expose
@cherrypy.tools.json_out()
def get_history(self):
"""
Get the history of the measurements from the database.
"""
try:
db_instance = MeasurementConverterDB()
except:
raise cherrypy.HTTPError(500, "Database connection error")

return db_instance.get_history()

Empty file added data/.gitkeep
Empty file.
Empty file added logs/.gitkeep
Empty file.
15 changes: 15 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import cherrypy
import logging
from controller.measurement_converter_api import MeasurementConverterAPI


if __name__ == '__main__':
# Configure logging
logging.basicConfig(filename='./logs/measurement_log.log', level=logging.CRITICAL,
format='%(asctime)s:%(levelname)s:%(message)s')

cherrypy.tree.mount(MeasurementConverterAPI(), '/')
cherrypy.config.update({'server.socket_host': '0.0.0.0'})
cherrypy.config.update({'server.socket_port': 8080})
cherrypy.engine.start()
cherrypy.engine.block()
7 changes: 7 additions & 0 deletions models/measurement_history.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class MeasurementHistory:
"""
Class to represent a measurement history object
"""
def __init__(self, input_str, output):
self.input = input_str
self.output = output
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CherryPy==18.9.0
requests==2.31.0
setuptools==69.2.0
71 changes: 71 additions & 0 deletions services/measurement_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
class MeasurementConverter:
def __init__(self, string=None):
self.string = string

def get_letter_value(self, letter):
"""
Get the numerical value of a letter.
"""
if letter == 'z':
return 26
else:
return ord(letter) - 96

def package_measurement_converter(self, string):
"""
Parse the string and calculate the sum of letter values for each segment.
"""
self.string = string
result = []
index = 0
try:
# Iterate through the string
while index < len(string):
if string[index].isalpha():
count = 0
# If the letter is z, add 26 to the count until you find the first non-z letter/char
if string[index] == 'z':
while string[index] == 'z':
count += self.get_letter_value(string[index])
index += 1
if string[index].isalpha():
count += self.get_letter_value(string[index])
index += 1
else:
count = self.get_letter_value(string[index])
index += 1

sum_value = 0
i = index # i determains the start of the segment while index is a pointer to the current character
range = index + count

# Extract the sum of letter values for the segment
while i < (range):
if string[i].isalpha():
if string[i] == 'z':
range += 1
while string[i] == 'z':
sum_value += self.get_letter_value(string[i])
index += 1
i += 1
if string[i].isalpha():
sum_value += self.get_letter_value(string[i])
else:
sum_value += self.get_letter_value(string[i])
i += 1

result.append(sum_value)
index += count

elif string[index] == '_':
# Append 0 and break the loop if the first character is '_'
result.append(0)
break

else:
# For any other character that is not a letter or '_', return "Invalid string input"
return "Invalid string input"

except:
return "Invalid string input"
return result
39 changes: 39 additions & 0 deletions services/tests/measurement_converter_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import unittest
from services.measurement_converter import MeasurementConverter


class TestMeasurementConverter(unittest.TestCase):

def test_non_z_strings(self):
"""
Test valid non z strings.
"""
non_z_inputs = ["aa", "abbcc", "abcdabcdab", "a_" ]
non_z_outputs = [[1], [2, 6], [2, 7, 7], [0]]

for i in range(len(non_z_inputs)):
self.assertEqual(MeasurementConverter().package_measurement_converter(non_z_inputs[i]), non_z_outputs[i])


def test_z_strings(self):
"""
Test valid z strings.
"""
z_inputs = ["dz_a_aazzaaa", "za_a_a_a_a_a_a_a_a_a_a_a_a_azaaa", "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_", "_zzzb", "_"]
z_outputs = [[28, 53, 1], [40, 1], [26], [0], [0]]

for i in range(len(z_inputs)):
self.assertEqual(MeasurementConverter().package_measurement_converter(z_inputs[i]), z_outputs[i])

def test_invalid_strings(self):
"""
Test invalid strings.
"""
invalid_inputs = ["abc", "aaa", "z", "123", "+!@#"]

for i in range(len(invalid_inputs)):
self.assertEqual(MeasurementConverter().package_measurement_converter(invalid_inputs[i]), "Invalid string input")


if __name__ == '__main__':
unittest.main(verbosity=2)
41 changes: 41 additions & 0 deletions utils/measurement_converter_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sqlite3
import json

class MeasurementConverterDB(object):
def __init__(self, db_name='./data/history.db'):
self.db_name = db_name
self.conn = sqlite3.connect(self.db_name)
self.cursor = self.conn.cursor()
self.create_table()

def create_table(self):
"""
Create the table for the history of the measurements.
"""
self.cursor.execute('''
CREATE TABLE IF NOT EXISTS history (
id INTEGER PRIMARY KEY,
input TEXT,
output TEXT,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
''')
self.conn.commit()

def save_to_history(self, input_str, output_str):
"""
Save the input and output to the history table.
"""
output_str = str(output_str)
self.cursor.execute('INSERT INTO history (input, output) VALUES (?, ?)', (input_str, output_str))
self.conn.commit()

def get_history(self):
"""
Get the history of the measurements from the database.
"""
self.cursor.execute("SELECT input, output FROM history ORDER BY timestamp DESC")
rows = self.cursor.fetchall()
history = [{'input': row[0], 'output': row[1]} for row in rows]
return history