LogicGoInfotechSpaces commited on
Commit
571cdaa
·
verified ·
1 Parent(s): 6848f57

Update app/database.py

Browse files
Files changed (1) hide show
  1. app/database.py +570 -1
app/database.py CHANGED
@@ -258,9 +258,11 @@ def _update_daily_count(collection, user_object_id: ObjectId, today: datetime) -
258
  collection.update_one(
259
  {"userId": user_object_id},
260
  {
 
261
  "$push": {
262
  "ai_edit_daily_count": {
263
- "$each": dates_to_add
 
264
  }
265
  }
266
  }
@@ -563,3 +565,570 @@ def close_connection():
563
  _admin_db = None
564
  logger.info("Admin MongoDB connection closed")
565
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  collection.update_one(
259
  {"userId": user_object_id},
260
  {
261
+ # Append new dates and keep only the most recent 32 entries
262
  "$push": {
263
  "ai_edit_daily_count": {
264
+ "$each": dates_to_add,
265
+ "$slice": -32,
266
  }
267
  }
268
  }
 
565
  _admin_db = None
566
  logger.info("Admin MongoDB connection closed")
567
 
568
+
569
+
570
+ # """
571
+ # MongoDB database connection and logging utilities, including admin media click logging.
572
+ # """
573
+ # import os
574
+ # import logging
575
+ # from datetime import datetime, timedelta
576
+ # from typing import Optional, Dict, Any, Union, List
577
+ # from bson import ObjectId
578
+ # from pymongo import MongoClient
579
+ # from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
580
+
581
+ # logger = logging.getLogger(__name__)
582
+
583
+ # # MongoDB connection
584
+ # _client: Optional[MongoClient] = None
585
+ # _db = None
586
+
587
+ # # Admin MongoDB connection (media_clicks)
588
+ # _admin_client: Optional[MongoClient] = None
589
+ # _admin_db = None
590
+
591
+ # def get_mongodb_client() -> Optional[MongoClient]:
592
+ # """Get or create MongoDB client"""
593
+ # global _client
594
+ # if _client is None:
595
+ # mongodb_uri = os.getenv("MONGODB_URI")
596
+ # if not mongodb_uri:
597
+ # logger.warning("MONGODB_URI environment variable not set. MongoDB features will be disabled.")
598
+ # return None
599
+ # try:
600
+ # _client = MongoClient(
601
+ # mongodb_uri,
602
+ # serverSelectionTimeoutMS=5000,
603
+ # connectTimeoutMS=5000
604
+ # )
605
+ # # Test connection
606
+ # _client.admin.command('ping')
607
+ # logger.info("MongoDB connection established successfully")
608
+ # except (ConnectionFailure, ServerSelectionTimeoutError) as e:
609
+ # logger.error("Failed to connect to MongoDB: %s", str(e))
610
+ # _client = None
611
+ # return _client
612
+
613
+ # def get_database():
614
+ # """Get database instance"""
615
+ # global _db
616
+ # if _db is None:
617
+ # client = get_mongodb_client()
618
+ # if client:
619
+ # db_name = os.getenv("MONGODB_DB_NAME", "colorization_db")
620
+ # _db = client[db_name]
621
+ # else:
622
+ # logger.warning("MongoDB client not available")
623
+ # return _db
624
+
625
+
626
+ # def get_admin_client() -> Optional[MongoClient]:
627
+ # """Get or create admin MongoDB client (for media_clicks collection)."""
628
+ # global _admin_client
629
+ # if _admin_client is None:
630
+ # mongodb_uri = os.getenv("MONGODB_ADMIN")
631
+ # if not mongodb_uri:
632
+ # logger.warning("MONGODB_ADMIN environment variable not set. Admin MongoDB features will be disabled.")
633
+ # return None
634
+ # try:
635
+ # _admin_client = MongoClient(
636
+ # mongodb_uri,
637
+ # serverSelectionTimeoutMS=5000,
638
+ # connectTimeoutMS=5000,
639
+ # )
640
+ # _admin_client.admin.command("ping")
641
+ # logger.info("Admin MongoDB connection established successfully")
642
+ # except (ConnectionFailure, ServerSelectionTimeoutError) as exc:
643
+ # logger.error("Failed to connect to Admin MongoDB: %s", str(exc))
644
+ # _admin_client = None
645
+ # return _admin_client
646
+
647
+
648
+ # def get_admin_database():
649
+ # """Get admin database instance."""
650
+ # global _admin_db
651
+ # if _admin_db is None:
652
+ # client = get_admin_client()
653
+ # if client:
654
+ # db_name = os.getenv("MONGODB_ADMIN_DB_NAME", "adminPanel")
655
+ # _admin_db = client[db_name]
656
+ # logger.info("Using admin database: %s", db_name)
657
+ # else:
658
+ # logger.warning("Admin MongoDB client not available")
659
+ # return _admin_db
660
+
661
+
662
+ # def _normalize_object_id(raw_value: Optional[Union[str, int, ObjectId]]) -> ObjectId:
663
+ # """Normalize user id inputs into a deterministic ObjectId."""
664
+ # if isinstance(raw_value, ObjectId):
665
+ # return raw_value
666
+
667
+ # if raw_value is None:
668
+ # return ObjectId()
669
+
670
+ # # Handle empty strings as None
671
+ # if isinstance(raw_value, str) and not raw_value.strip():
672
+ # return ObjectId()
673
+
674
+ # try:
675
+ # if isinstance(raw_value, int) or (isinstance(raw_value, str) and raw_value.strip().lstrip("-").isdigit()):
676
+ # int_value = int(str(raw_value).strip())
677
+ # hex_str = format(abs(int_value), "x").zfill(24)[-24:]
678
+ # if ObjectId.is_valid(hex_str):
679
+ # return ObjectId(hex_str)
680
+ # except Exception as exc: # pragma: no cover - defensive
681
+ # logger.debug("Numeric user id normalization failed: %s", str(exc))
682
+
683
+ # if isinstance(raw_value, str):
684
+ # candidate = raw_value.strip()
685
+ # if ObjectId.is_valid(candidate):
686
+ # return ObjectId(candidate)
687
+
688
+ # return ObjectId()
689
+
690
+
691
+ # def _objectid_from_any(value: str) -> ObjectId:
692
+ # """Convert arbitrary string into an ObjectId deterministically when possible."""
693
+ # if ObjectId.is_valid(value):
694
+ # return ObjectId(value)
695
+ # try:
696
+ # hex_str = value.encode("utf-8").hex().zfill(24)[-24:]
697
+ # if ObjectId.is_valid(hex_str):
698
+ # return ObjectId(hex_str)
699
+ # except Exception as exc: # pragma: no cover - defensive
700
+ # logger.debug("Category id normalization failed: %s", str(exc))
701
+ # return ObjectId()
702
+
703
+
704
+ # def _resolve_category_id(
705
+ # category_id: Optional[str],
706
+ # endpoint_path: Optional[str],
707
+ # default_category_id: Optional[str],
708
+ # ) -> ObjectId:
709
+ # """Pick category id from explicit value, endpoint default, or fallback."""
710
+ # # Handle empty strings as None
711
+ # if isinstance(category_id, str) and not category_id.strip():
712
+ # category_id = None
713
+
714
+ # endpoint_map = {
715
+ # "colorization": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
716
+ # "upload": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
717
+ # "colorize": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
718
+ # }
719
+ # normalized_endpoint = None
720
+ # if endpoint_path:
721
+ # normalized_endpoint = endpoint_path.strip("/").split("/")[0].lower() or None
722
+
723
+ # chosen = category_id
724
+ # if not chosen and normalized_endpoint and endpoint_map.get(normalized_endpoint):
725
+ # chosen = endpoint_map[normalized_endpoint]
726
+ # if not chosen:
727
+ # chosen = default_category_id or os.getenv("DEFAULT_CATEGORY_FALLBACK", "69368fcd2e46bd68ae1889b2")
728
+
729
+ # return _objectid_from_any(chosen)
730
+
731
+
732
+ # def _get_today_midnight_utc() -> datetime:
733
+ # """Get today's date at midnight UTC (timezone-naive for MongoDB compatibility)."""
734
+ # now = datetime.utcnow()
735
+ # return datetime(now.year, now.month, now.day)
736
+
737
+
738
+ # def _update_daily_count(collection, user_object_id: ObjectId, today: datetime) -> None:
739
+ # """Update ai_edit_daily_count array: add today with count 1 if missing, fill gaps with count 0."""
740
+ # # Ensure today is at midnight
741
+ # today_start = today.replace(hour=0, minute=0, second=0, microsecond=0)
742
+
743
+ # # Check if today's date already exists
744
+ # user_doc = collection.find_one({"userId": user_object_id})
745
+
746
+ # if not user_doc:
747
+ # # User document doesn't exist yet - this shouldn't happen as we create it first
748
+ # # But handle it gracefully by initializing the field
749
+ # logger.warning("User document not found when updating daily count, initializing")
750
+ # collection.update_one(
751
+ # {"userId": user_object_id},
752
+ # {
753
+ # "$setOnInsert": {
754
+ # "ai_edit_daily_count": [{
755
+ # "date": today_start,
756
+ # "count": 1
757
+ # }]
758
+ # }
759
+ # },
760
+ # upsert=False
761
+ # )
762
+ # return
763
+
764
+ # # Get existing daily counts
765
+ # existing_counts = user_doc.get("ai_edit_daily_count", [])
766
+
767
+ # # Check if today's date already exists
768
+ # today_exists = False
769
+ # for entry in existing_counts:
770
+ # entry_date = entry.get("date")
771
+ # if entry_date:
772
+ # # Normalize to midnight for comparison
773
+ # if isinstance(entry_date, datetime):
774
+ # normalized_date = entry_date.replace(hour=0, minute=0, second=0, microsecond=0)
775
+ # if normalized_date == today_start:
776
+ # today_exists = True
777
+ # break
778
+
779
+ # # If today exists, do nothing (leave it as is)
780
+ # if today_exists:
781
+ # logger.debug("Today's date already exists in daily count, leaving unchanged: %s", today_start.isoformat())
782
+ # return
783
+
784
+ # # Today doesn't exist - need to add it and fill missing dates
785
+ # # Find the latest date in existing counts
786
+ # last_date = None
787
+ # if existing_counts:
788
+ # dates = []
789
+ # for entry in existing_counts:
790
+ # entry_date = entry.get("date")
791
+ # if entry_date:
792
+ # # Normalize to midnight for comparison
793
+ # if isinstance(entry_date, datetime):
794
+ # dates.append(entry_date.replace(hour=0, minute=0, second=0, microsecond=0))
795
+ # if dates:
796
+ # last_date = max(dates)
797
+
798
+ # # Generate missing dates between last_date and today
799
+ # dates_to_add = []
800
+ # if last_date:
801
+ # # Normalize last_date to midnight
802
+ # last_date = last_date.replace(hour=0, minute=0, second=0, microsecond=0)
803
+ # current_date = last_date + timedelta(days=1)
804
+ # # Fill missing dates with count 0
805
+ # while current_date < today_start:
806
+ # dates_to_add.append({
807
+ # "date": current_date,
808
+ # "count": 0
809
+ # })
810
+ # current_date += timedelta(days=1)
811
+
812
+ # # Add today's entry with count 1
813
+ # dates_to_add.append({
814
+ # "date": today_start,
815
+ # "count": 1
816
+ # })
817
+
818
+ # # Push all missing dates (including today)
819
+ # if dates_to_add:
820
+ # # Ensure ai_edit_daily_count field exists
821
+ # if "ai_edit_daily_count" not in user_doc:
822
+ # collection.update_one(
823
+ # {"userId": user_object_id},
824
+ # {"$set": {"ai_edit_daily_count": []}}
825
+ # )
826
+
827
+ # collection.update_one(
828
+ # {"userId": user_object_id},
829
+ # {
830
+ # "$push": {
831
+ # "ai_edit_daily_count": {
832
+ # "$each": dates_to_add
833
+ # }
834
+ # }
835
+ # }
836
+ # )
837
+ # logger.debug("Added %d daily count entries (including today with count 1)", len(dates_to_add))
838
+
839
+ # def log_api_call(
840
+ # endpoint: str,
841
+ # method: str,
842
+ # status_code: int = 200,
843
+ # request_data: Optional[Dict[str, Any]] = None,
844
+ # response_data: Optional[Dict[str, Any]] = None,
845
+ # error: Optional[str] = None,
846
+ # user_id: Optional[str] = None,
847
+ # ip_address: Optional[str] = None
848
+ # ) -> bool:
849
+ # """
850
+ # Log API call to MongoDB
851
+
852
+ # Args:
853
+ # endpoint: API endpoint path
854
+ # method: HTTP method (GET, POST, etc.)
855
+ # status_code: HTTP status code
856
+ # request_data: Request data/parameters
857
+ # response_data: Response data
858
+ # error: Error message if any
859
+ # user_id: User ID if authenticated
860
+ # ip_address: Client IP address
861
+
862
+ # Returns:
863
+ # True if logged successfully, False otherwise
864
+ # """
865
+ # try:
866
+ # db = get_database()
867
+ # if db is None:
868
+ # logger.warning("MongoDB not available, skipping API log")
869
+ # return False
870
+
871
+ # collection = db["api_calls"]
872
+
873
+ # log_entry = {
874
+ # "endpoint": endpoint,
875
+ # "method": method,
876
+ # "status_code": status_code,
877
+ # "timestamp": datetime.utcnow(),
878
+ # "request_data": request_data or {},
879
+ # "response_data": response_data or {},
880
+ # "error": error,
881
+ # "user_id": user_id,
882
+ # "ip_address": ip_address
883
+ # }
884
+
885
+ # result = collection.insert_one(log_entry)
886
+ # logger.info("API call logged to MongoDB: %s", result.inserted_id)
887
+ # return True
888
+ # except Exception as e:
889
+ # logger.error("Failed to log API call to MongoDB: %s", str(e))
890
+ # return False
891
+
892
+ # def log_image_upload(
893
+ # image_id: str,
894
+ # filename: str,
895
+ # file_size: int,
896
+ # content_type: str,
897
+ # user_id: Optional[str] = None,
898
+ # ip_address: Optional[str] = None
899
+ # ) -> bool:
900
+ # """
901
+ # Log image upload to MongoDB
902
+
903
+ # Args:
904
+ # image_id: Unique image identifier
905
+ # filename: Original filename
906
+ # file_size: File size in bytes
907
+ # content_type: MIME type
908
+ # user_id: User ID if authenticated
909
+ # ip_address: Client IP address
910
+
911
+ # Returns:
912
+ # True if logged successfully, False otherwise
913
+ # """
914
+ # try:
915
+ # db = get_database()
916
+ # if db is None:
917
+ # logger.warning("MongoDB not available, skipping upload log")
918
+ # return False
919
+
920
+ # collection = db["image_uploads"]
921
+
922
+ # log_entry = {
923
+ # "image_id": image_id,
924
+ # "filename": filename,
925
+ # "file_size": file_size,
926
+ # "content_type": content_type,
927
+ # "uploaded_at": datetime.utcnow(),
928
+ # "user_id": user_id,
929
+ # "ip_address": ip_address
930
+ # }
931
+
932
+ # result = collection.insert_one(log_entry)
933
+ # logger.info("Image upload logged to MongoDB: %s", result.inserted_id)
934
+ # return True
935
+ # except Exception as e:
936
+ # logger.error("Failed to log image upload to MongoDB: %s", str(e))
937
+ # return False
938
+
939
+ # def log_colorization(
940
+ # result_id: Optional[str] = None,
941
+ # image_id: Optional[str] = None,
942
+ # prompt: Optional[str] = None,
943
+ # model_type: Optional[str] = None,
944
+ # processing_time: Optional[float] = None,
945
+ # user_id: Optional[str] = None,
946
+ # ip_address: Optional[str] = None,
947
+ # status: str = "success",
948
+ # error: Optional[str] = None
949
+ # ) -> bool:
950
+ # """
951
+ # Log colorization request to MongoDB (colorization_db -> colorizations collection)
952
+ # Logs both successful and failed API calls.
953
+
954
+ # Args:
955
+ # result_id: Unique result identifier (None for failed requests)
956
+ # image_id: Original image identifier
957
+ # prompt: Text prompt used (if any)
958
+ # model_type: Model type used (fastai, pytorch, sdxl, etc.)
959
+ # processing_time: Time taken to process in seconds
960
+ # user_id: User ID if authenticated
961
+ # ip_address: Client IP address
962
+ # status: Status of the request ("success" or "failed")
963
+ # error: Error message if status is "failed"
964
+
965
+ # Returns:
966
+ # True if logged successfully, False otherwise
967
+ # """
968
+ # try:
969
+ # db = get_database()
970
+ # if db is None:
971
+ # logger.warning("MongoDB not available, skipping colorization log")
972
+ # return False
973
+
974
+ # collection = db["colorizations"]
975
+
976
+ # # Generate result_id if not provided (for failed requests)
977
+ # if not result_id:
978
+ # import uuid
979
+ # result_id = str(uuid.uuid4())
980
+
981
+ # log_entry = {
982
+ # "result_id": result_id,
983
+ # "image_id": image_id,
984
+ # "prompt": prompt,
985
+ # "model_type": model_type,
986
+ # "processing_time": processing_time,
987
+ # "created_at": datetime.utcnow(),
988
+ # "user_id": user_id,
989
+ # "ip_address": ip_address
990
+ # }
991
+
992
+ # # Add status and error fields only if provided (for new documents)
993
+ # # Existing documents won't have these fields, which is fine
994
+ # if status:
995
+ # log_entry["status"] = status
996
+ # if error:
997
+ # log_entry["error"] = error
998
+
999
+ # result = collection.insert_one(log_entry)
1000
+ # logger.info("Colorization logged to MongoDB (status: %s): %s", status, result.inserted_id)
1001
+ # return True
1002
+ # except Exception as e:
1003
+ # logger.error("Failed to log colorization to MongoDB: %s", str(e))
1004
+ # return False
1005
+
1006
+
1007
+ # def log_media_click(
1008
+ # user_id: Optional[Union[str, int, ObjectId]],
1009
+ # category_id: Optional[str],
1010
+ # *,
1011
+ # endpoint_path: Optional[str] = None,
1012
+ # default_category_id: Optional[str] = None,
1013
+ # ) -> bool:
1014
+ # """Log media clicks into the admin MongoDB (media_clicks collection).
1015
+
1016
+ # Only logs if user_id is provided. If user_id is None or empty, returns False without logging.
1017
+ # Regular MongoDB logging (api_calls, image_uploads, colorizations) always happens regardless.
1018
+ # """
1019
+ # # Only log to media_clicks if user_id is provided
1020
+ # if not user_id or (isinstance(user_id, str) and not user_id.strip()):
1021
+ # logger.debug("Skipping media click log - user_id not provided")
1022
+ # return False
1023
+
1024
+ # try:
1025
+ # db = get_admin_database()
1026
+ # if db is None:
1027
+ # logger.warning("Admin MongoDB not available, skipping media click log")
1028
+ # return False
1029
+
1030
+ # collection = db["media_clicks"]
1031
+
1032
+ # # Drop legacy index to avoid duplicate key errors (best effort)
1033
+ # try:
1034
+ # collection.drop_index("user_id_1_header_1_media_id_1")
1035
+ # except Exception as exc:
1036
+ # logger.debug("Legacy index drop skipped: %s", str(exc))
1037
+
1038
+ # user_object_id = _normalize_object_id(user_id)
1039
+ # category_object_id = _resolve_category_id(category_id, endpoint_path, default_category_id)
1040
+ # now = datetime.utcnow()
1041
+ # today = _get_today_midnight_utc()
1042
+
1043
+ # logger.info("Media click - user_id input: %s, normalized: %s, category_id input: %s, normalized: %s",
1044
+ # user_id, str(user_object_id), category_id, str(category_object_id))
1045
+
1046
+ # # Try to update existing category in new schema (categories.categoryId)
1047
+ # update_existing = collection.update_one(
1048
+ # {"userId": user_object_id, "categories.categoryId": category_object_id},
1049
+ # {
1050
+ # "$inc": {
1051
+ # "categories.$.click_count": 1,
1052
+ # "ai_edit_complete": 1 # Increment usage count
1053
+ # },
1054
+ # "$set": {
1055
+ # "categories.$.lastClickedAt": now,
1056
+ # "updatedAt": now,
1057
+ # "ai_edit_last_date": now # Update last used date (MongoDB Date object)
1058
+ # },
1059
+ # },
1060
+ # )
1061
+
1062
+ # if update_existing.matched_count == 0:
1063
+ # # Category not found in new schema, check if user exists
1064
+ # user_exists = collection.find_one({"userId": user_object_id})
1065
+
1066
+ # if user_exists:
1067
+ # # User exists but category doesn't in new schema - add to categories array
1068
+ # collection.update_one(
1069
+ # {"userId": user_object_id},
1070
+ # {
1071
+ # "$inc": {"ai_edit_complete": 1}, # Increment usage count
1072
+ # "$set": {
1073
+ # "updatedAt": now,
1074
+ # "ai_edit_last_date": now # Update last used date (MongoDB Date object)
1075
+ # },
1076
+ # "$push": {
1077
+ # "categories": {
1078
+ # "categoryId": category_object_id,
1079
+ # "click_count": 1,
1080
+ # "lastClickedAt": now,
1081
+ # }
1082
+ # },
1083
+ # },
1084
+ # )
1085
+ # else:
1086
+ # # User doesn't exist - create new document with categories array
1087
+ # # Default ai_edit_complete = 0, then increment to 1 on first use
1088
+ # collection.update_one(
1089
+ # {"userId": user_object_id},
1090
+ # {
1091
+ # "$setOnInsert": {
1092
+ # "createdAt": now,
1093
+ # "ai_edit_complete": 0, # Default 0 for new users
1094
+ # "ai_edit_daily_count": [] # Initialize empty array
1095
+ # },
1096
+ # "$inc": {"ai_edit_complete": 1}, # Increment to 1 on first use
1097
+ # "$set": {
1098
+ # "updatedAt": now,
1099
+ # "ai_edit_last_date": now # Set last used date (MongoDB Date object)
1100
+ # },
1101
+ # "$push": {
1102
+ # "categories": {
1103
+ # "categoryId": category_object_id,
1104
+ # "click_count": 1,
1105
+ # "lastClickedAt": now,
1106
+ # }
1107
+ # },
1108
+ # },
1109
+ # upsert=True,
1110
+ # )
1111
+
1112
+ # # Update daily count (after document is created/updated)
1113
+ # _update_daily_count(collection, user_object_id, today)
1114
+
1115
+ # logger.info("Media click logged for user %s", str(user_object_id))
1116
+ # return True
1117
+ # except Exception as exc:
1118
+ # logger.error("Failed to log media click to admin MongoDB: %s", str(exc))
1119
+ # return False
1120
+
1121
+ # def close_connection():
1122
+ # """Close MongoDB connection"""
1123
+ # global _client, _db, _admin_client, _admin_db
1124
+ # if _client:
1125
+ # _client.close()
1126
+ # _client = None
1127
+ # _db = None
1128
+ # logger.info("MongoDB connection closed")
1129
+ # if _admin_client:
1130
+ # _admin_client.close()
1131
+ # _admin_client = None
1132
+ # _admin_db = None
1133
+ # logger.info("Admin MongoDB connection closed")
1134
+