189 lines
6.8 KiB
Python
189 lines
6.8 KiB
Python
#!/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:
|
|
# Try exact match first (name or any alias), then regex fallback
|
|
name_lower = args.name.lower()
|
|
exact = coll.find_one({"$or": [
|
|
{"name": name_lower},
|
|
{"aliases": name_lower},
|
|
]})
|
|
if exact:
|
|
query["_id"] = exact["_id"]
|
|
else:
|
|
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()
|