#!/usr/bin/env python3 """CLI for managing food items in MongoDB lists.food_items.""" import argparse import json import sys from datetime import datetime, timezone from pymongo import MongoClient CONN = "mongodb://root:3wwfoUjyk2E2zWELXFlLuHqfw1ALlOp4pb2H5Vq3TImbMIHL2h1u8Jej2mjzCPl@docdb.connorrhodes.com:35563/?tls=true&tlsAllowInvalidCertificates=true" def get_collection(): client = MongoClient(CONN) return client["lists"]["food_items"], client def cmd_add(args): coll, client = get_collection() doc = {"name": args.name} if args.type: doc["type"] = args.type if args.serving: doc["serving"] = args.serving if args.description: doc["description"] = args.description if args.max_dosage: doc["max_dosage"] = args.max_dosage if args.nutrition: try: doc["nutrition"] = json.loads(args.nutrition) except json.JSONDecodeError: print(f"Error: --nutrition must be valid JSON. Got: {args.nutrition}", file=sys.stderr) sys.exit(1) if args.aliases: doc["aliases"] = [a.strip() for a in args.aliases.split(",") if a.strip()] if args.extra: try: extra = json.loads(args.extra) doc.update(extra) except json.JSONDecodeError: print(f"Error: --extra must be valid JSON. Got: {args.extra}", file=sys.stderr) sys.exit(1) result = coll.insert_one(doc) out = coll.find_one({"_id": result.inserted_id}) out["_id"] = str(out["_id"]) print(json.dumps(out, indent=2, default=str)) client.close() def cmd_get(args): coll, client = get_collection() query = {} if args.name: query["$or"] = [ {"name": {"$regex": args.name, "$options": "i"}}, {"aliases": {"$regex": args.name, "$options": "i"}}, ] if args.type: query["type"] = {"$regex": args.type, "$options": "i"} cursor = coll.find(query).sort("name", 1).limit(args.limit if args.limit else 50) docs = [] for d in cursor: d["_id"] = str(d["_id"]) docs.append(d) if not docs: print("No items found.") else: print(json.dumps(docs, indent=2, default=str)) client.close() def cmd_update(args): coll, client = get_collection() # Build filter if args.id: from bson import ObjectId query = {"_id": ObjectId(args.id)} elif args.name: query = {"name": args.name} else: print("Error: provide --id or --name to identify the item", file=sys.stderr) sys.exit(1) # Build update update_fields = {} if args.set_name: update_fields["name"] = args.set_name if args.set_type: update_fields["type"] = args.set_type if args.set_serving: update_fields["serving"] = args.set_serving if args.set_description: update_fields["description"] = args.set_description if args.set_max_dosage: update_fields["max_dosage"] = args.set_max_dosage if args.set_aliases: update_fields["aliases"] = [a.strip() for a in args.set_aliases.split(",") if a.strip()] if args.set_nutrition: try: update_fields["nutrition"] = json.loads(args.set_nutrition) except json.JSONDecodeError: print(f"Error: --set-nutrition must be valid JSON", file=sys.stderr) sys.exit(1) if args.set_extra: try: extra = json.loads(args.set_extra) update_fields.update(extra) except json.JSONDecodeError: print(f"Error: --set-extra must be valid JSON", file=sys.stderr) sys.exit(1) if args.unset: for field in args.unset: update_fields[field] = "" if not update_fields: print("Error: nothing to update", file=sys.stderr) sys.exit(1) set_doc = {"$set": {k: v for k, v in update_fields.items()}} unset_doc = {"$unset": {k: v for k, v in update_fields.items() if v == ""}} update = {} if set_doc["$set"]: update["$set"] = set_doc["$set"] if unset_doc["$unset"]: update["$unset"] = unset_doc["$unset"] result = coll.update_one(query, update) print(f"Matched: {result.matched_count}, Modified: {result.modified_count}") if result.matched_count: updated = coll.find_one(query) updated["_id"] = str(updated["_id"]) print(json.dumps(updated, indent=2, default=str)) else: print("No matching item found.") client.close() def main(): parser = argparse.ArgumentParser(description="Manage food items in lists.food_items") sub = parser.add_subparsers(dest="command", required=True) # add p_add = sub.add_parser("add", help="Add a new food item") p_add.add_argument("--name", required=True) p_add.add_argument("--type") p_add.add_argument("--serving") p_add.add_argument("--description") p_add.add_argument("--max-dosage") p_add.add_argument("--aliases", help="Comma-separated alias names, e.g. \"large bread,large toast\"") p_add.add_argument("--nutrition", help='JSON string, e.g. \'{"calories": 228, "protein_g": 12}\'') p_add.add_argument("--extra", help="JSON string with additional fields to merge into the document") # get p_get = sub.add_parser("get", help="Get food items") p_get.add_argument("--name", help="Filter by name (regex)") p_get.add_argument("--type", help="Filter by type (regex)") p_get.add_argument("--limit", type=int, help="Max items to return") # update p_upd = sub.add_parser("update", help="Update an existing food item") p_upd.add_argument("--id", help="ObjectId to update") p_upd.add_argument("--name", help="Name to match (if no --id)") p_upd.add_argument("--set-name", dest="set_name") p_upd.add_argument("--set-type", dest="set_type") p_upd.add_argument("--set-serving", dest="set_serving") p_upd.add_argument("--set-description", dest="set_description") p_upd.add_argument("--set-max-dosage", dest="set_max_dosage") p_upd.add_argument("--set-aliases", dest="set_aliases", help="Comma-separated alias names (replaces existing)") p_upd.add_argument("--set-nutrition", dest="set_nutrition", help="JSON string to replace nutrition") p_upd.add_argument("--set-extra", dest="set_extra", help="JSON string with additional fields to set") p_upd.add_argument("--unset", nargs="*", help="Field names to remove") args = parser.parse_args() {"add": cmd_add, "get": cmd_get, "update": cmd_update}[args.command](args) if __name__ == "__main__": main()