LogicGoInfotechSpaces commited on
Commit
6848f57
·
1 Parent(s): 9a427d9

Add ai_edit_daily_count tracking to media_clicks collection

Browse files
Files changed (2) hide show
  1. app/database.py +122 -9
  2. postman_collection.json +0 -436
app/database.py CHANGED
@@ -3,8 +3,8 @@ MongoDB database connection and logging utilities, including admin media click l
3
  """
4
  import os
5
  import logging
6
- from datetime import datetime
7
- from typing import Optional, Dict, Any, Union
8
  from bson import ObjectId
9
  from pymongo import MongoClient
10
  from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
@@ -157,7 +157,115 @@ def _resolve_category_id(
157
  if not chosen:
158
  chosen = default_category_id or os.getenv("DEFAULT_CATEGORY_FALLBACK", "69368fcd2e46bd68ae1889b2")
159
 
160
- return _objectid_from_any(chosen)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  def log_api_call(
163
  endpoint: str,
@@ -361,6 +469,7 @@ def log_media_click(
361
  user_object_id = _normalize_object_id(user_id)
362
  category_object_id = _resolve_category_id(category_id, endpoint_path, default_category_id)
363
  now = datetime.utcnow()
 
364
 
365
  logger.info("Media click - user_id input: %s, normalized: %s, category_id input: %s, normalized: %s",
366
  user_id, str(user_object_id), category_id, str(category_object_id))
@@ -397,8 +506,8 @@ def log_media_click(
397
  },
398
  "$push": {
399
  "categories": {
400
- "categoryId": category_object_id,
401
- "click_count": 1,
402
  "lastClickedAt": now,
403
  }
404
  },
@@ -412,7 +521,8 @@ def log_media_click(
412
  {
413
  "$setOnInsert": {
414
  "createdAt": now,
415
- "ai_edit_complete": 0 # Default 0 for new users
 
416
  },
417
  "$inc": {"ai_edit_complete": 1}, # Increment to 1 on first use
418
  "$set": {
@@ -421,14 +531,17 @@ def log_media_click(
421
  },
422
  "$push": {
423
  "categories": {
424
- "categoryId": category_object_id,
425
- "click_count": 1,
426
  "lastClickedAt": now,
427
  }
428
  },
429
  },
430
  upsert=True,
431
- )
 
 
 
432
 
433
  logger.info("Media click logged for user %s", str(user_object_id))
434
  return True
 
3
  """
4
  import os
5
  import logging
6
+ from datetime import datetime, timedelta
7
+ from typing import Optional, Dict, Any, Union, List
8
  from bson import ObjectId
9
  from pymongo import MongoClient
10
  from pymongo.errors import ConnectionFailure, ServerSelectionTimeoutError
 
157
  if not chosen:
158
  chosen = default_category_id or os.getenv("DEFAULT_CATEGORY_FALLBACK", "69368fcd2e46bd68ae1889b2")
159
 
160
+ return _objectid_from_any(chosen)
161
+
162
+
163
+ def _get_today_midnight_utc() -> datetime:
164
+ """Get today's date at midnight UTC (timezone-naive for MongoDB compatibility)."""
165
+ now = datetime.utcnow()
166
+ return datetime(now.year, now.month, now.day)
167
+
168
+
169
+ def _update_daily_count(collection, user_object_id: ObjectId, today: datetime) -> None:
170
+ """Update ai_edit_daily_count array: add today with count 1 if missing, fill gaps with count 0."""
171
+ # Ensure today is at midnight
172
+ today_start = today.replace(hour=0, minute=0, second=0, microsecond=0)
173
+
174
+ # Check if today's date already exists
175
+ user_doc = collection.find_one({"userId": user_object_id})
176
+
177
+ if not user_doc:
178
+ # User document doesn't exist yet - this shouldn't happen as we create it first
179
+ # But handle it gracefully by initializing the field
180
+ logger.warning("User document not found when updating daily count, initializing")
181
+ collection.update_one(
182
+ {"userId": user_object_id},
183
+ {
184
+ "$setOnInsert": {
185
+ "ai_edit_daily_count": [{
186
+ "date": today_start,
187
+ "count": 1
188
+ }]
189
+ }
190
+ },
191
+ upsert=False
192
+ )
193
+ return
194
+
195
+ # Get existing daily counts
196
+ existing_counts = user_doc.get("ai_edit_daily_count", [])
197
+
198
+ # Check if today's date already exists
199
+ today_exists = False
200
+ for entry in existing_counts:
201
+ entry_date = entry.get("date")
202
+ if entry_date:
203
+ # Normalize to midnight for comparison
204
+ if isinstance(entry_date, datetime):
205
+ normalized_date = entry_date.replace(hour=0, minute=0, second=0, microsecond=0)
206
+ if normalized_date == today_start:
207
+ today_exists = True
208
+ break
209
+
210
+ # If today exists, do nothing (leave it as is)
211
+ if today_exists:
212
+ logger.debug("Today's date already exists in daily count, leaving unchanged: %s", today_start.isoformat())
213
+ return
214
+
215
+ # Today doesn't exist - need to add it and fill missing dates
216
+ # Find the latest date in existing counts
217
+ last_date = None
218
+ if existing_counts:
219
+ dates = []
220
+ for entry in existing_counts:
221
+ entry_date = entry.get("date")
222
+ if entry_date:
223
+ # Normalize to midnight for comparison
224
+ if isinstance(entry_date, datetime):
225
+ dates.append(entry_date.replace(hour=0, minute=0, second=0, microsecond=0))
226
+ if dates:
227
+ last_date = max(dates)
228
+
229
+ # Generate missing dates between last_date and today
230
+ dates_to_add = []
231
+ if last_date:
232
+ # Normalize last_date to midnight
233
+ last_date = last_date.replace(hour=0, minute=0, second=0, microsecond=0)
234
+ current_date = last_date + timedelta(days=1)
235
+ # Fill missing dates with count 0
236
+ while current_date < today_start:
237
+ dates_to_add.append({
238
+ "date": current_date,
239
+ "count": 0
240
+ })
241
+ current_date += timedelta(days=1)
242
+
243
+ # Add today's entry with count 1
244
+ dates_to_add.append({
245
+ "date": today_start,
246
+ "count": 1
247
+ })
248
+
249
+ # Push all missing dates (including today)
250
+ if dates_to_add:
251
+ # Ensure ai_edit_daily_count field exists
252
+ if "ai_edit_daily_count" not in user_doc:
253
+ collection.update_one(
254
+ {"userId": user_object_id},
255
+ {"$set": {"ai_edit_daily_count": []}}
256
+ )
257
+
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
+ }
267
+ )
268
+ logger.debug("Added %d daily count entries (including today with count 1)", len(dates_to_add))
269
 
270
  def log_api_call(
271
  endpoint: str,
 
469
  user_object_id = _normalize_object_id(user_id)
470
  category_object_id = _resolve_category_id(category_id, endpoint_path, default_category_id)
471
  now = datetime.utcnow()
472
+ today = _get_today_midnight_utc()
473
 
474
  logger.info("Media click - user_id input: %s, normalized: %s, category_id input: %s, normalized: %s",
475
  user_id, str(user_object_id), category_id, str(category_object_id))
 
506
  },
507
  "$push": {
508
  "categories": {
509
+ "categoryId": category_object_id,
510
+ "click_count": 1,
511
  "lastClickedAt": now,
512
  }
513
  },
 
521
  {
522
  "$setOnInsert": {
523
  "createdAt": now,
524
+ "ai_edit_complete": 0, # Default 0 for new users
525
+ "ai_edit_daily_count": [] # Initialize empty array
526
  },
527
  "$inc": {"ai_edit_complete": 1}, # Increment to 1 on first use
528
  "$set": {
 
531
  },
532
  "$push": {
533
  "categories": {
534
+ "categoryId": category_object_id,
535
+ "click_count": 1,
536
  "lastClickedAt": now,
537
  }
538
  },
539
  },
540
  upsert=True,
541
+ )
542
+
543
+ # Update daily count (after document is created/updated)
544
+ _update_daily_count(collection, user_object_id, today)
545
 
546
  logger.info("Media click logged for user %s", str(user_object_id))
547
  return True
postman_collection.json DELETED
@@ -1,436 +0,0 @@
1
- {
2
- "info": {
3
- "_postman_id": "image-colorization-api",
4
- "name": "Image Colorization API",
5
- "description": "API collection for Text-Guided Image Colorization using Hugging Face Inference API",
6
- "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
7
- },
8
- "item": [
9
- {
10
- "name": "Health Check",
11
- "request": {
12
- "method": "GET",
13
- "header": [],
14
- "url": {
15
- "raw": "{{base_url}}/health",
16
- "host": [
17
- "{{base_url}}"
18
- ],
19
- "path": [
20
- "health"
21
- ]
22
- },
23
- "description": "Check if the API is running and models are loaded"
24
- },
25
- "response": []
26
- },
27
- {
28
- "name": "API Info",
29
- "request": {
30
- "method": "GET",
31
- "header": [],
32
- "url": {
33
- "raw": "{{base_url}}/api",
34
- "host": [
35
- "{{base_url}}"
36
- ],
37
- "path": [
38
- "api"
39
- ]
40
- },
41
- "description": "Get API information and available endpoints"
42
- },
43
- "response": []
44
- },
45
- {
46
- "name": "Colorize Image - Basic",
47
- "request": {
48
- "method": "POST",
49
- "header": [
50
- {
51
- "key": "Authorization",
52
- "value": "Bearer {{firebase_token}}",
53
- "type": "text"
54
- },
55
- {
56
- "key": "X-Firebase-AppCheck",
57
- "value": "{{firebase_app_check}}",
58
- "type": "text"
59
- }
60
- ],
61
- "body": {
62
- "mode": "formdata",
63
- "formdata": [
64
- {
65
- "key": "file",
66
- "type": "file",
67
- "src": [],
68
- "description": "Image file to colorize"
69
- },
70
- {
71
- "key": "user_id",
72
- "value": "{{user_id}}",
73
- "type": "text",
74
- "description": "Optional user id (ObjectId or numeric) for media click logging"
75
- },
76
- {
77
- "key": "category_id",
78
- "value": "{{category_id}}",
79
- "type": "text",
80
- "description": "Optional category id; endpoint default used when omitted"
81
- },
82
- {
83
- "key": "categoryId",
84
- "value": "{{categoryId}}",
85
- "type": "text",
86
- "description": "Optional category id (alternative to category_id)"
87
- }
88
- ]
89
- },
90
- "url": {
91
- "raw": "{{base_url}}/colorize",
92
- "host": [
93
- "{{base_url}}"
94
- ],
95
- "path": [
96
- "colorize"
97
- ]
98
- },
99
- "description": "Colorize an image with default settings"
100
- },
101
- "response": []
102
- },
103
- {
104
- "name": "Colorize Image - With Prompt",
105
- "request": {
106
- "method": "POST",
107
- "header": [
108
- {
109
- "key": "Authorization",
110
- "value": "Bearer {{firebase_token}}",
111
- "type": "text"
112
- },
113
- {
114
- "key": "X-Firebase-AppCheck",
115
- "value": "{{firebase_app_check}}",
116
- "type": "text"
117
- }
118
- ],
119
- "body": {
120
- "mode": "formdata",
121
- "formdata": [
122
- {
123
- "key": "file",
124
- "type": "file",
125
- "src": [],
126
- "description": "Image file to colorize"
127
- },
128
- {
129
- "key": "positive_prompt",
130
- "value": "vibrant natural colors, high quality photo",
131
- "type": "text",
132
- "description": "Additional descriptive text to enhance the caption"
133
- },
134
- {
135
- "key": "user_id",
136
- "value": "{{user_id}}",
137
- "type": "text",
138
- "description": "Optional user id (ObjectId or numeric) for media click logging"
139
- },
140
- {
141
- "key": "category_id",
142
- "value": "{{category_id}}",
143
- "type": "text",
144
- "description": "Optional category id; endpoint default used when omitted"
145
- },
146
- {
147
- "key": "categoryId",
148
- "value": "{{categoryId}}",
149
- "type": "text",
150
- "description": "Optional category id (alternative to category_id)"
151
- }
152
- ]
153
- },
154
- "url": {
155
- "raw": "{{base_url}}/colorize",
156
- "host": [
157
- "{{base_url}}"
158
- ],
159
- "path": [
160
- "colorize"
161
- ]
162
- },
163
- "description": "Colorize an image with custom positive prompt"
164
- },
165
- "response": []
166
- },
167
- {
168
- "name": "Colorize Image - Full Options",
169
- "request": {
170
- "method": "POST",
171
- "header": [
172
- {
173
- "key": "Authorization",
174
- "value": "Bearer {{firebase_token}}",
175
- "type": "text"
176
- },
177
- {
178
- "key": "X-Firebase-AppCheck",
179
- "value": "{{firebase_app_check}}",
180
- "type": "text"
181
- }
182
- ],
183
- "body": {
184
- "mode": "formdata",
185
- "formdata": [
186
- {
187
- "key": "file",
188
- "type": "file",
189
- "src": [],
190
- "description": "Image file to colorize"
191
- },
192
- {
193
- "key": "positive_prompt",
194
- "value": "vibrant natural colors, high quality photo, detailed lighting",
195
- "type": "text",
196
- "description": "Additional descriptive text to enhance the caption"
197
- },
198
- {
199
- "key": "negative_prompt",
200
- "value": "low quality, monochrome, blurry, grainy",
201
- "type": "text",
202
- "description": "Words or phrases to avoid during generation"
203
- },
204
- {
205
- "key": "seed",
206
- "value": "123",
207
- "type": "text",
208
- "description": "Random seed for reproducible generation"
209
- },
210
- {
211
- "key": "num_inference_steps",
212
- "value": "8",
213
- "type": "text",
214
- "description": "Number of inference steps"
215
- },
216
- {
217
- "key": "user_id",
218
- "value": "{{user_id}}",
219
- "type": "text",
220
- "description": "Optional user id (ObjectId or numeric) for media click logging"
221
- },
222
- {
223
- "key": "category_id",
224
- "value": "{{category_id}}",
225
- "type": "text",
226
- "description": "Optional category id; endpoint default used when omitted"
227
- },
228
- {
229
- "key": "categoryId",
230
- "value": "{{categoryId}}",
231
- "type": "text",
232
- "description": "Optional category id (alternative to category_id)"
233
- }
234
- ]
235
- },
236
- "url": {
237
- "raw": "{{base_url}}/colorize",
238
- "host": [
239
- "{{base_url}}"
240
- ],
241
- "path": [
242
- "colorize"
243
- ]
244
- },
245
- "description": "Colorize an image with all available options"
246
- },
247
- "response": []
248
- },
249
- {
250
- "name": "Download Result",
251
- "request": {
252
- "method": "GET",
253
- "header": [
254
- {
255
- "key": "Authorization",
256
- "value": "Bearer {{firebase_token}}",
257
- "type": "text"
258
- }
259
- ],
260
- "url": {
261
- "raw": "{{base_url}}/download/{{result_id}}",
262
- "host": [
263
- "{{base_url}}"
264
- ],
265
- "path": [
266
- "download",
267
- "{{result_id}}"
268
- ]
269
- },
270
- "description": "Download colorized image by result ID (requires authentication)"
271
- },
272
- "response": []
273
- },
274
- {
275
- "name": "Get Result (Public)",
276
- "request": {
277
- "method": "GET",
278
- "header": [],
279
- "url": {
280
- "raw": "{{base_url}}/results/{{filename}}",
281
- "host": [
282
- "{{base_url}}"
283
- ],
284
- "path": [
285
- "results",
286
- "{{filename}}"
287
- ]
288
- },
289
- "description": "Public endpoint to access colorized images by filename"
290
- },
291
- "response": []
292
- },
293
- {
294
- "name": "Upload Image",
295
- "request": {
296
- "method": "POST",
297
- "header": [
298
- {
299
- "key": "Authorization",
300
- "value": "Bearer {{firebase_token}}",
301
- "type": "text"
302
- },
303
- {
304
- "key": "X-Firebase-AppCheck",
305
- "value": "{{firebase_app_check}}",
306
- "type": "text"
307
- }
308
- ],
309
- "body": {
310
- "mode": "formdata",
311
- "formdata": [
312
- {
313
- "key": "file",
314
- "type": "file",
315
- "src": [],
316
- "description": "Image file to upload"
317
- },
318
- {
319
- "key": "user_id",
320
- "value": "{{user_id}}",
321
- "type": "text",
322
- "description": "Optional user id (ObjectId or numeric) for media click logging"
323
- },
324
- {
325
- "key": "category_id",
326
- "value": "{{category_id}}",
327
- "type": "text",
328
- "description": "Optional category id; endpoint default used when omitted"
329
- },
330
- {
331
- "key": "categoryId",
332
- "value": "{{categoryId}}",
333
- "type": "text",
334
- "description": "Optional category id (alternative to category_id)"
335
- }
336
- ]
337
- },
338
- "url": {
339
- "raw": "{{base_url}}/upload",
340
- "host": [
341
- "{{base_url}}"
342
- ],
343
- "path": [
344
- "upload"
345
- ]
346
- },
347
- "description": "Upload an image and get the uploaded image URL"
348
- },
349
- "response": []
350
- },
351
- {
352
- "name": "Colorize Image - Without Auth",
353
- "request": {
354
- "method": "POST",
355
- "header": [],
356
- "body": {
357
- "mode": "formdata",
358
- "formdata": [
359
- {
360
- "key": "file",
361
- "type": "file",
362
- "src": [],
363
- "description": "Image file to colorize"
364
- },
365
- {
366
- "key": "positive_prompt",
367
- "value": "colorize this image with vibrant colors",
368
- "type": "text"
369
- }
370
- ]
371
- },
372
- "url": {
373
- "raw": "{{base_url}}/colorize",
374
- "host": [
375
- "{{base_url}}"
376
- ],
377
- "path": [
378
- "colorize"
379
- ]
380
- },
381
- "description": "Colorize image without authentication (only works if DISABLE_AUTH=true)"
382
- },
383
- "response": []
384
- }
385
- ],
386
- "variable": [
387
- {
388
- "key": "base_url",
389
- "value": "https://YOUR_SPACE_URL",
390
- "type": "string",
391
- "description": "Base URL of your Hugging Face Space or local server"
392
- },
393
- {
394
- "key": "firebase_token",
395
- "value": "YOUR_FIREBASE_ID_TOKEN",
396
- "type": "string",
397
- "description": "Firebase authentication token"
398
- },
399
- {
400
- "key": "result_id",
401
- "value": "",
402
- "type": "string",
403
- "description": "Result ID from colorize response"
404
- },
405
- {
406
- "key": "filename",
407
- "value": "",
408
- "type": "string",
409
- "description": "Filename from colorize response (e.g., uuid.png)"
410
- },
411
- {
412
- "key": "user_id",
413
- "value": "",
414
- "type": "string",
415
- "description": "User ID for media click logging (ObjectId string, integer, or leave empty to auto-generate)"
416
- },
417
- {
418
- "key": "category_id",
419
- "value": "",
420
- "type": "string",
421
- "description": "Category ID for media click logging (ObjectId string, or leave empty for endpoint default)"
422
- },
423
- {
424
- "key": "categoryId",
425
- "value": "",
426
- "type": "string",
427
- "description": "Category ID (alternative to category_id)"
428
- },
429
- {
430
- "key": "firebase_app_check",
431
- "value": "YOUR_FIREBASE_APP_CHECK_TOKEN",
432
- "type": "string",
433
- "description": "Firebase App Check token"
434
- }
435
- ]
436
- }