AdarshDRC commited on
Commit
fb44492
·
verified ·
1 Parent(s): bb4210b

Create services/db_client.py

Browse files
Files changed (1) hide show
  1. src/services/db_client.py +152 -0
src/services/db_client.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from typing import Any, Dict, List
3
+
4
+ import cloudinary
5
+ import cloudinary.uploader
6
+ import cloudinary.api
7
+ from pinecone import Pinecone, ServerlessSpec
8
+
9
+ from src.core.config import IDX_FACES, IDX_OBJECTS
10
+
11
+ class PineconePool:
12
+ def __init__(self):
13
+ self._clients = {}
14
+
15
+ def get(self, api_key: str) -> Pinecone:
16
+ if api_key not in self._clients:
17
+ self._clients[api_key] = Pinecone(api_key=api_key)
18
+ return self._clients[api_key]
19
+
20
+ pinecone_pool = PineconePool()
21
+
22
+ def _set_cld_config(creds: dict):
23
+ cloudinary.config(
24
+ cloud_name=creds.get("cloud_name"),
25
+ api_key=creds.get("api_key"),
26
+ api_secret=creds.get("api_secret"),
27
+ secure=True
28
+ )
29
+
30
+ def cld_ping(creds: dict):
31
+ _set_cld_config(creds)
32
+ cloudinary.api.ping()
33
+
34
+ def cld_upload(file_obj, folder: str, creds: dict) -> dict:
35
+ _set_cld_config(creds)
36
+ return cloudinary.uploader.upload(file_obj, folder=folder)
37
+
38
+ def cld_root_folders(creds: dict) -> dict:
39
+ _set_cld_config(creds)
40
+ return cloudinary.api.root_folders()
41
+
42
+ def cld_list_folder_images(folder: str, creds: dict, cursor: str = None, page_size: int = 100) -> dict:
43
+ _set_cld_config(creds)
44
+ kwargs = {"type": "upload", "prefix": f"{folder}/", "max_results": page_size}
45
+ if cursor:
46
+ kwargs["next_cursor"] = cursor
47
+ return cloudinary.api.resources(**kwargs)
48
+
49
+ def cld_delete_resource(public_id: str, creds: dict):
50
+ _set_cld_config(creds)
51
+ cloudinary.uploader.destroy(public_id)
52
+
53
+ def cld_delete_folder_resources(folder: str, creds: dict):
54
+ _set_cld_config(creds)
55
+ cloudinary.api.delete_resources_by_prefix(f"{folder}/")
56
+
57
+ def cld_remove_folder(folder: str, creds: dict):
58
+ _set_cld_config(creds)
59
+ try:
60
+ cloudinary.api.delete_folder(folder)
61
+ except Exception:
62
+ pass
63
+
64
+ def cld_delete_all_paginated(creds: dict) -> int:
65
+ _set_cld_config(creds)
66
+ deleted = 0
67
+ cursor = None
68
+ while True:
69
+ kwargs = {"type": "upload", "max_results": 500}
70
+ if cursor:
71
+ kwargs["next_cursor"] = cursor
72
+ res = cloudinary.api.resources(**kwargs)
73
+ resources = res.get("resources", [])
74
+ if not resources:
75
+ break
76
+ pids = [r["public_id"] for r in resources]
77
+ cloudinary.api.delete_resources(pids)
78
+ deleted += len(pids)
79
+ cursor = res.get("next_cursor")
80
+ if not cursor:
81
+ break
82
+ return deleted
83
+
84
+ def ensure_indexes(pc: Pinecone) -> List[str]:
85
+ created = []
86
+ existing = [idx.name for idx in pc.list_indexes()]
87
+ for name in [IDX_FACES, IDX_OBJECTS]:
88
+ if name not in existing:
89
+ pc.create_index(
90
+ name=name,
91
+ dimension=1024 if name == IDX_FACES else 1536,
92
+ metric="cosine",
93
+ spec=ServerlessSpec(cloud="aws", region="us-east-1")
94
+ )
95
+ created.append(name)
96
+ return created
97
+
98
+ def delete_and_recreate_indexes(pc: Pinecone):
99
+ existing = [idx.name for idx in pc.list_indexes()]
100
+ for name in [IDX_FACES, IDX_OBJECTS]:
101
+ if name in existing:
102
+ pc.delete_index(name)
103
+ time.sleep(5)
104
+ ensure_indexes(pc)
105
+
106
+ def search_faces(idx, vec: List[float], det_score: float) -> Dict[str, Any]:
107
+ res = idx.query(vector=vec, top_k=50, include_metadata=True)
108
+ image_map = {}
109
+ for match in res.get("matches", []):
110
+ meta = match.get("metadata", {})
111
+ url = meta.get("url")
112
+ if not url:
113
+ continue
114
+ score = match.get("score", 0)
115
+ if url not in image_map or image_map[url]["raw_score"] < score:
116
+ image_map[url] = {
117
+ "raw_score": score,
118
+ "face_crop": meta.get("face_crop", ""),
119
+ "folder": meta.get("folder", "uncategorized")
120
+ }
121
+ return image_map
122
+
123
+ def search_objects(idx, vec: List[float]) -> List[Dict[str, Any]]:
124
+ res = idx.query(vector=vec, top_k=50, include_metadata=True)
125
+ results = []
126
+ for match in res.get("matches", []):
127
+ meta = match.get("metadata", {})
128
+ results.append({
129
+ "url": meta.get("url", ""),
130
+ "score": round(match.get("score", 0) * 100, 2),
131
+ "raw_score": match.get("score", 0),
132
+ "folder": meta.get("folder", "uncategorized")
133
+ })
134
+ return results
135
+
136
+ def merge_face_results(groups: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
137
+ merged = {}
138
+ for group in groups:
139
+ for match in group.get("matches", []):
140
+ url = match["url"]
141
+ if url not in merged or merged[url]["score"] < match["score"]:
142
+ merged[url] = match
143
+ return sorted(merged.values(), key=lambda x: x["score"], reverse=True)
144
+
145
+ def merge_object_results(nested_results: List[List[Dict[str, Any]]]) -> List[Dict[str, Any]]:
146
+ merged = {}
147
+ for res_list in nested_results:
148
+ for match in res_list:
149
+ url = match["url"]
150
+ if url not in merged or merged[url]["score"] < match["score"]:
151
+ merged[url] = match
152
+ return sorted(merged.values(), key=lambda x: x["score"], reverse=True)