Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using .json file as the receipt input, calculate_bill works fine. #2

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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 data/WiredTiger
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WiredTiger
WiredTiger 11.2.0: (November 10, 2022)
1 change: 1 addition & 0 deletions data/WiredTiger.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
WiredTiger lock file
6 changes: 6 additions & 0 deletions data/WiredTiger.turtle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
WiredTiger version string
WiredTiger 11.2.0: (November 10, 2022)
WiredTiger version
major=11,minor=2,patch=0
file:WiredTiger.wt
access_pattern_hint=none,allocation_size=4KB,app_metadata=,assert=(commit_timestamp=none,durable_timestamp=none,read_timestamp=none,write_timestamp=off),block_allocation=best,block_compressor=,cache_resident=false,checksum=on,collator=,columns=,dictionary=0,encryption=(keyid=,name=),format=btree,huffman_key=,huffman_value=,id=0,ignore_in_memory_cache_size=false,internal_item_max=0,internal_key_max=0,internal_key_truncate=true,internal_page_max=4KB,key_format=S,key_gap=10,leaf_item_max=0,leaf_key_max=0,leaf_page_max=32KB,leaf_value_max=0,log=(enabled=true),memory_page_image_max=0,memory_page_max=5MB,os_cache_dirty_max=0,os_cache_max=0,prefix_compression=false,prefix_compression_min=4,readonly=false,split_deepen_min_child=0,split_deepen_per_child=0,split_pct=90,tiered_object=false,tiered_storage=(auth_token=,bucket=,bucket_prefix=,cache_directory=,local_retention=300,name=,object_target_size=0),value_format=S,verbose=[],version=(major=1,minor=1),write_timestamp_usage=none,checkpoint=(WiredTigerCheckpoint.308=(addr="018881e461e58ddb8981e4ee482c7d8a81e4687addcb808080e29fc0e22fc0",order=308,time=1714543150,size=24576,newest_start_durable_ts=0,oldest_start_ts=0,newest_txn=8,newest_stop_durable_ts=0,newest_stop_ts=-1,newest_stop_txn=-11,prepare=0,write_gen=886,run_write_gen=870)),checkpoint_backup_info=,checkpoint_lsn=(38,8064)
Binary file added data/WiredTiger.wt
Binary file not shown.
Binary file added data/WiredTigerHS.wt
Binary file not shown.
Binary file added data/_mdb_catalog.wt
Binary file not shown.
Binary file added data/collection-0-2112355434297019392.wt
Binary file not shown.
Binary file added data/collection-2-2112355434297019392.wt
Binary file not shown.
Binary file added data/collection-4-2112355434297019392.wt
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added data/index-1-2112355434297019392.wt
Binary file not shown.
Binary file added data/index-3-2112355434297019392.wt
Binary file not shown.
Binary file added data/index-5-2112355434297019392.wt
Binary file not shown.
Binary file added data/index-6-2112355434297019392.wt
Binary file not shown.
Binary file added data/journal/WiredTigerLog.0000000038
Binary file not shown.
Binary file added data/journal/WiredTigerPreplog.0000000001
Binary file not shown.
Binary file added data/journal/WiredTigerPreplog.0000000002
Binary file not shown.
Empty file added data/mongod.lock
Empty file.
Binary file added data/sizeStorer.wt
Binary file not shown.
Binary file added data/storage.bson
Binary file not shown.
82 changes: 55 additions & 27 deletions machine-learning-client/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@
cxn = pymongo.MongoClient(os.getenv("MONGO_URI"))
db = cxn[os.getenv("MONGO_DBNAME")] # store a reference to the database

# Load response1.json
with open("response1.json", "r") as f:
data = json.load(f)

print('Receipt Keys:', data['receipts'][0].keys())
items = data['receipts'][0]['items']
print()
print(f"Your purchase at {data['receipts'][0]['merchant_name']}")

for item in items:
print(f"{item['description']} - {data['receipts'][0]['currency']} {item['amount']}")
print("-" * 20)
print(f"Subtotal: {data['receipts'][0]['currency']} {data['receipts'][0]['subtotal']}")
print(f"Tax: {data['receipts'][0]['currency']} {data['receipts'][0]['tax']}")
print("-" * 20)
print(f"Total: {data['receipts'][0]['currency']} {data['receipts'][0]['total']}")
# print(data['receipts'])

@app.route('/predict', methods=['POST'])
def pretdict_endpoint():
# Get the image data from the request
Expand Down Expand Up @@ -91,33 +109,43 @@ def pretdict_endpoint():
# Return the inserted_id as a JSON response
return jsonify({'_id': str(inserted_id)})

def perform_ocr(Object_ID):
logger.debug("starting perform_ocr function...") # debug
url = "https://ocr.asprise.com/api/v1/receipt"
image_data = db.receipts.find_one({"_id": Object_ID})['image']
file_path = f"receipt_{Object_ID}.jpg" # Set the file path to save the image
logger.debug("file path: %s", file_path) # debug
with open(file_path, "wb") as f:
f.write(image_data)
file_path = file_path.replace('\x00', '') # Remove any null bytes from the file path


# Get response (can only do this a couple times with the test API key)
res = requests.post(url,
data = {
'api_key': 'TEST',
'recognizer': 'auto',
'ref_no': 'ocr_python_123'
},

files = {
'file': open(file_path, "rb")
})

with open("response.json", "w") as f:
f.write(res.text)

return res.json()
def perform_ocr():
try:
json_file_path = os.path.join(os.path.dirname(__file__), 'response1.json')
with open(json_file_path, "r") as file:
data = json.load(file)
if 'receipts' in data and len(data['receipts']) > 0:
# Access the first receipt since the JSON has an array of receipts
receipt_data = data['receipts'][0]

# Prepare the receipt format as needed, you might need to adjust based on your MongoDB schema
formatted_receipt = {
'merchant_name': receipt_data.get('merchant_name'),
'merchant_address': receipt_data.get('merchant_address'),
'merchant_phone': receipt_data.get('merchant_phone'),
'total': receipt_data.get('total'),
'tax': receipt_data.get('tax'),
'subtotal': receipt_data.get('subtotal'),
'currency': receipt_data.get('currency'),
'items': receipt_data.get('items', []),
'date': receipt_data.get('date')
}
return formatted_receipt
else:
logger.error("No 'receipts' key found in JSON file or 'receipts' array is empty.")
return None
except FileNotFoundError:
logger.error(f"The file {json_file_path} was not found.")
return None
except json.JSONDecodeError as e:
logger.error(f"JSON decode error in file {json_file_path}: {str(e)}")
return None
except Exception as e:
logger.error(f"An unexpected error occurred: {str(e)}")
return None




if __name__ == '__main__':
app.run(host='0.0.0.0', port=5002) # Run the app
204 changes: 141 additions & 63 deletions web-app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from dotenv import load_dotenv
from bson import ObjectId
import json
import uuid

import logging

Expand All @@ -30,16 +31,43 @@
cxn = pymongo.MongoClient(os.getenv("MONGO_URI"))
db = cxn[os.getenv("MONGO_DBNAME")] # store a reference to the database

def perform_ocr():
try:
json_file_path = os.path.join(os.path.dirname(__file__), 'response1.json')
with open(json_file_path, "r") as file:
data = json.load(file)
if 'receipts' in data and len(data['receipts']) > 0:
# Access the first receipt since the JSON has an array of receipts
receipt_data = data['receipts'][0]

# Prepare the receipt format as needed, you might need to adjust based on your MongoDB schema
formatted_receipt = {
'merchant_name': receipt_data.get('merchant_name'),
'merchant_address': receipt_data.get('merchant_address'),
'merchant_phone': receipt_data.get('merchant_phone'),
'total': receipt_data.get('total'),
'tax': receipt_data.get('tax'),
'subtotal': receipt_data.get('subtotal'),
'currency': receipt_data.get('currency'),
'items': receipt_data.get('items', []),
'date': receipt_data.get('date')
}
for item in receipt_data.get('items', []):
item['_id'] = str(uuid.uuid4()) # Assign a unique ID
return formatted_receipt
else:
logger.error("No 'receipts' key found in JSON file or 'receipts' array is empty.")
return None
except FileNotFoundError:
logger.error(f"The file {json_file_path} was not found.")
return None
except json.JSONDecodeError as e:
logger.error(f"JSON decode error in file {json_file_path}: {str(e)}")
return None
except Exception as e:
logger.error(f"An unexpected error occurred: {str(e)}")
return None

# Call the ML service to perform OCR on the receipt
def call_ml_service(Object_ID):
url = "http://machine-learning-client:5002/predict"
headers = {'Content-Type': 'application/json'}
data = json.dumps({"Object_ID": str(Object_ID)}) # Serialize the Object_ID into a JSON string
response = requests.post(url, data=data, headers=headers)
logger.debug(f"Response Status Code: {response.status_code}")
logger.debug(f"Response Text: {response.text}")
return response.json()


#homepage -add receipt - history
Expand All @@ -55,19 +83,16 @@ def upload_image():
file = request.files['image']
if file.filename == '':
return jsonify({"error": "No selected file"}), 400
if file:
image_data = file.read()
try:
result = db.receipts.insert_one({"image": image_data})
inserted_id = str(result.inserted_id)
#logger.debug("YAY", inserted_id)
call_ml_service(inserted_id)
return redirect(url_for('numofpeople', receipt_id=inserted_id))
except pymongo.errors.ServerSelectionTimeoutError as e:
logger.error("Could not connect to MongoDB: %s", str(e))
return jsonify({"error": "Database connection failed"}), 503

return jsonify({"error": "Unexpected error occurred"}), 500
# Simulate OCR by loading data from a JSON file
receipt_data = perform_ocr()
if not receipt_data:
return jsonify({"error": "OCR simulation failed or JSON file is incorrect"}), 500

# Insert parsed data into MongoDB
result = db.receipts.insert_one(receipt_data)
inserted_id = str(result.inserted_id)
return redirect(url_for('numofpeople', receipt_id=inserted_id))


#( pull receipt from database )
Expand Down Expand Up @@ -98,6 +123,13 @@ def submit_people(receipt_id):
return jsonify({"error": "Database connection failed"}), 503
#label appetizers

def is_valid_uuid(uuid_to_test, version=4):
try:
uuid_obj = uuid.UUID(uuid_to_test, version=version)
return str(uuid_obj) == uuid_to_test
except ValueError:
return False

@app.route('/select_appetizers/<receipt_id>', methods=['GET', 'POST'])
def select_appetizers(receipt_id):
if request.method == 'POST':
Expand All @@ -112,21 +144,29 @@ def select_appetizers(receipt_id):
appetizer_ids = request.form.getlist('appetizers')
logging.debug(f"Received appetizer IDs: {appetizer_ids}")

valid_ids = [id for id in appetizer_ids if ObjectId.is_valid(id) and id.strip() != '']
valid_ids = [id for id in appetizer_ids if is_valid_uuid(id)]
logging.debug(f"Valid appetizer IDs: {valid_ids}")

# First reset all items to not be appetizers
db.receipts.update_one(
{'_id': ObjectId(receipt_id)},
{'$set': {'items.$[].is_appetizer': False}}
)

if valid_ids:
object_ids = [ObjectId(id) for id in valid_ids]
# Update items where the ID matches any of the valid appetizer IDs
db.receipts.update_many(
{'_id': ObjectId(receipt_id)},
{'$set': {'items.$[elem].is_appetizer': True}},
array_filters=[{'elem._id': {'$in': object_ids}}]
{'_id': ObjectId(receipt_id), 'items._id': {'$in': valid_ids}},
{'$set': {'items.$.is_appetizer': True}}
)
db.receipts.update_one(
{'_id': ObjectId(receipt_id)},
{'$set': {'items.$[elem].is_appetizer': False}},
array_filters=[{'elem._id': {'$nin': object_ids}}]
# Reset is_appetizer for other items
db.receipts.update_many(
{'_id': ObjectId(receipt_id), 'items._id': {'$nin': valid_ids}},
{'$set': {'items.$.is_appetizer': False}}
)
selected_appetizers = db.receipts.find_one({'_id': ObjectId(receipt_id)}, {'items': 1})['items']
selected_appetizer_details = [(item['description'], item['amount']) for item in selected_appetizers if str(item['_id']) in valid_ids]
logger.debug(f"Selected Appetizers: {selected_appetizer_details}")
else:
db.receipts.update_one(
{'_id': ObjectId(receipt_id)},
Expand All @@ -150,25 +190,27 @@ def allocateitems(receipt_id):
# Clear previous allocations to avoid duplicates
db.receipts.update_one({'_id': ObjectId(receipt_id)}, {'$unset': {'allocations': ''}})

# Process form data and re-allocate items
allocations = {key: request.form.getlist(key) for key in request.form.keys()}
for name, items in allocations.items():
if items: # Ensure we don't push empty lists
db.receipts.update_one(
{'_id': ObjectId(receipt_id)},
{'$push': {'allocations': {'name': name, 'items': items}}}
)
allocations = {} # This will store which items are chosen by which people
item_counts = {} # This will count how many people have chosen each item

for key, values in request.form.lists():
if key.startswith("item_"):
item_id = key[5:] # Remove 'item_' prefix
allocations[item_id] = values
item_counts[item_id] = len(values)

logger.debug(f"Updated allocations: {allocations}")
logger.debug(f"Updated item counts: {item_counts}")

# Store the updated allocations and counts in the database
db.receipts.update_one(
{'_id': ObjectId(receipt_id)},
{'$set': {'allocations': allocations, 'item_counts': item_counts}}
)
return redirect(url_for('enter_tip', receipt_id=receipt_id))

# Fetch data to display form
receipt = db.receipts.find_one({'_id': ObjectId(receipt_id)})
if not receipt:
return jsonify({"error": "Receipt not found"}), 404

people = receipt.get('names', [])
food_items = receipt.get('items', [])

return render_template('allocateitems.html', people=people, food_items=food_items, receipt_id=receipt_id)
else:
receipt = db.receipts.find_one({'_id': ObjectId(receipt_id)})
return render_template('allocateitems.html', people=receipt.get('names', []), food_items=receipt.get('items', []), receipt_id=receipt_id)

@app.route('/enter_tip/<receipt_id>', methods=['GET', 'POST'])
def enter_tip(receipt_id):
Expand Down Expand Up @@ -204,41 +246,77 @@ def calculate_bill(receipt_id):
return jsonify({"error": "Receipt not found"}), 404

items = receipt.get('items', [])
allocations = receipt.get('allocations', [])
allocations = receipt.get('allocations', {})
item_counts = receipt.get('item_counts', {})
logger.debug(f"Allocations retrieved: {allocations}")
logger.debug(f"Item counts retrieved: {item_counts}")

# Aggregate total appetizer cost
appetizer_items = [item for item in items if item.get('is_appetizer', False)]
logger.debug(f"Items marked as appetizers: {[(item['description'], item['amount']) for item in appetizer_items]}")

appetizer_total = sum(item['amount'] for item in appetizer_items if item.get('is_appetizer', False))
logger.debug(f"Total appetizer cost: {appetizer_total}")

tax = float(receipt.get('tax', 0.00))
subtotal = float(receipt.get('subtotal', 0.00))


logger.debug(f"Subtotal and tax values: Subtotal={subtotal}, Tax={tax}")

if not items or subtotal <= 0:
logger.error("No items found or subtotal is zero or negative.")
return jsonify({"error": "Invalid receipt data"}), 400

payments = {name: 0 for name in receipt.get('names', [])}
appetizer_total = sum(item.get('amount', 0.00) for item in items if item.get('is_appetizer', False))

num_people = len(receipt.get('names', []))

if num_people == 0:
logger.error("Number of people is zero.")
return jsonify({"error": "Number of people cannot be zero"}), 400

appetizer_split = appetizer_total / num_people
if num_people > 0:
appetizer_cost_per_person = appetizer_total / num_people
else:
appetizer_cost_per_person = 0
logger.debug(f"Appetizer cost per person: {appetizer_cost_per_person}")

appetizer_cost_per_person = appetizer_total / num_people if num_people else 0
for name in payments:
payments[name] += appetizer_split
payments[name] += appetizer_cost_per_person
logger.debug(f"Initial payment for {name}: {payments[name]}")

# Calculate individual item costs and distribute them
for item_id, users in allocations.items():
item = next((item for item in items if str(item['_id']) == item_id), None)
if item:
num_users = item_counts.get(item_id, 1)
cost_per_user = item['amount'] / num_users
for user in users:
payments[user] += cost_per_user
logger.debug(f"Allocating ${cost_per_user:.2f} to {user} for item {item['description']}")
else:
logger.debug(f"Item not found for ID: {item_id}")



# Calculating individual payments
for allocation in allocations:
for item_index in allocation.get('items', []):
if item_index.isdigit() and int(item_index) < len(items):
payments[allocation['name']] += items[int(item_index)].get('amount', 0.00)

total_with_tax = subtotal + tax
total_with_tip = total_with_tax * (1 + tip_percentage)
logger.debug(f"Total with tax: {total_with_tax}, Total with tip: {total_with_tip}")

# Distributing tax and tip across individuals
# Apply tax and tip proportionally
total_payment = 0
for name, payment in payments.items():
if subtotal > 0:
person_share_before_tax = payment / subtotal
payments[name] = payment + (total_with_tax + total_with_tip - subtotal) * person_share_before_tax
person_share_before_tax = payment / subtotal if subtotal > 0 else 0
final_payment = payment + (total_with_tax - subtotal) * person_share_before_tax + (total_with_tip - total_with_tax) * person_share_before_tax
payments[name] = round(final_payment, 2)
total_payment += payments[name]
logger.debug(f"Final payment for {name}: {payments[name]}")

db.receipts.update_one({"_id": ObjectId(receipt_id)}, {'$set': {'payments': payments}})

return render_template('results.html', payments=payments, receipt_id=receipt_id)
return render_template('results.html', payments=payments, total_payment=total_payment, receipt_id=receipt_id)
except Exception as e:
logger.error(f"Error in calculate_bill: {str(e)}")
return jsonify({"error": "An unexpected error occurred"}), 500
Expand Down
Loading