|
import os |
|
from datetime import datetime |
|
import pymongo |
|
from urllib.parse import quote_plus |
|
from bson.objectid import ObjectId |
|
|
|
raw_username = os.getenv("DB_USERNAME") |
|
raw_password = os.getenv("DB_PASSWORD") |
|
|
|
if not raw_username or not raw_password: |
|
raise Exception("Database credentials are missing. Check your environment variables.") |
|
|
|
username = quote_plus(raw_username) |
|
password = quote_plus(raw_password) |
|
|
|
cluster = "cluster0.yxjok.mongodb.net" |
|
|
|
DB_URI = f"mongodb+srv://{username}:{password}@{cluster}/?retryWrites=true&w=majority&appName=Cluster0" |
|
|
|
client = pymongo.MongoClient(DB_URI) |
|
db = client["billing_app"] |
|
|
|
users_coll = db["users"] |
|
sections_coll = db["sections"] |
|
bills_coll = db["bills"] |
|
|
|
sections_coll.create_index( |
|
[("owner_email", 1), ("section_name", 1)], |
|
unique=True |
|
) |
|
|
|
def create_user(email, password): |
|
if users_coll.find_one({"email": email}): |
|
raise ValueError("Email already exists") |
|
users_coll.insert_one({"email": email, "password": password}) |
|
|
|
def get_user_by_email_and_password(email, password): |
|
return users_coll.find_one({"email": email, "password": password}) |
|
|
|
def create_section(owner_email, section_name, participants_list): |
|
""" |
|
Creates a new section for the given user. |
|
Fails if that user already has a section with the same name. |
|
""" |
|
existing = sections_coll.find_one({"owner_email": owner_email, "section_name": section_name}) |
|
if existing: |
|
raise ValueError("Section with this name already exists for your account.") |
|
sections_coll.insert_one({ |
|
"owner_email": owner_email, |
|
"section_name": section_name, |
|
"participants": participants_list |
|
}) |
|
|
|
def update_section(owner_email, section_name, participants_list): |
|
sections_coll.update_one( |
|
{"owner_email": owner_email, "section_name": section_name}, |
|
{"$set": {"participants": participants_list}}, |
|
upsert=True |
|
) |
|
|
|
def get_section(owner_email, section_name): |
|
return sections_coll.find_one({"owner_email": owner_email, "section_name": section_name}) |
|
|
|
def delete_section(owner_email, section_name): |
|
sections_coll.delete_one({"owner_email": owner_email, "section_name": section_name}) |
|
bills_coll.delete_many({"owner_email": owner_email, "section_name": section_name}) |
|
|
|
def get_all_sections(owner_email): |
|
""" |
|
Returns all sections for that specific user/email. |
|
""" |
|
sections = sections_coll.find({"owner_email": owner_email}) |
|
return [sec["section_name"] for sec in sections] |
|
|
|
def create_bill(owner_email, section_name, participant, item, price): |
|
bills_coll.insert_one({ |
|
"owner_email": owner_email, |
|
"section_name": section_name, |
|
"participant": participant, |
|
"item": item, |
|
"price": float(price), |
|
"timestamp": datetime.now().date().isoformat() |
|
}) |
|
|
|
def get_submitted_items(owner_email, section_name): |
|
pipeline = [ |
|
{"$match": {"owner_email": owner_email, "section_name": section_name}}, |
|
{"$group": {"_id": "$item"}} |
|
] |
|
results = list(bills_coll.aggregate(pipeline)) |
|
return [r["_id"] for r in results] |
|
|
|
def get_billing_history(owner_email, section_name): |
|
pipeline = [ |
|
{"$match": {"owner_email": owner_email, "section_name": section_name}}, |
|
{ |
|
"$group": { |
|
"_id": "$participant", |
|
"total_price": {"$sum": "$price"}, |
|
"last_updated": {"$max": "$timestamp"} |
|
} |
|
} |
|
] |
|
return list(bills_coll.aggregate(pipeline)) |
|
|
|
def remove_items(owner_email, section_name, participant, items_to_remove): |
|
if items_to_remove is None: |
|
bills_coll.delete_many({ |
|
"owner_email": owner_email, |
|
"section_name": section_name, |
|
"participant": participant |
|
}) |
|
else: |
|
for item in items_to_remove: |
|
bills_coll.delete_many({ |
|
"owner_email": owner_email, |
|
"section_name": section_name, |
|
"participant": participant, |
|
"item": item |
|
}) |
|
|
|
def get_most_bought_item(owner_email, section_name): |
|
pipeline = [ |
|
{"$match": {"owner_email": owner_email, "section_name": section_name}}, |
|
{ |
|
"$group": { |
|
"_id": "$item", |
|
"count": {"$sum": 1}, |
|
"max_price": {"$max": "$price"} |
|
} |
|
}, |
|
{"$sort": {"count": -1, "max_price": -1}}, |
|
{"$limit": 1} |
|
] |
|
result = list(bills_coll.aggregate(pipeline)) |
|
if result: |
|
return (result[0]["_id"], result[0]["count"], result[0]["max_price"]) |
|
return None |
|
|