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