LogicGoInfotechSpaces commited on
Commit
0183b1e
·
1 Parent(s): 49b5e1e

Fix empty string handling and support both subCategories and categories schemas

Browse files

- Handle empty strings for user_id and category_id properly
- Support both old (subCategories) and new (categories) schemas simultaneously
- Add debug logging for media click tracking
- Preserve existing subCategories data when adding new categories
- Update all endpoints to properly handle empty form fields

Files changed (5) hide show
  1. app/database.py +47 -15
  2. app/main.py +14 -4
  3. app/main_fastai.py +9 -3
  4. app/main_sdxl.py +14 -4
  5. postman_collection.json +100 -0
app/database.py CHANGED
@@ -97,6 +97,10 @@ def _normalize_object_id(raw_value: Optional[Union[str, int, ObjectId]]) -> Obje
97
  if raw_value is None:
98
  return ObjectId()
99
 
 
 
 
 
100
  try:
101
  if isinstance(raw_value, int) or (isinstance(raw_value, str) and raw_value.strip().lstrip("-").isdigit()):
102
  int_value = int(str(raw_value).strip())
@@ -133,6 +137,10 @@ def _resolve_category_id(
133
  default_category_id: Optional[str],
134
  ) -> ObjectId:
135
  """Pick category id from explicit value, endpoint default, or fallback."""
 
 
 
 
136
  endpoint_map = {
137
  "colorization": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
138
  "upload": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
@@ -325,8 +333,12 @@ def log_media_click(
325
 
326
  user_object_id = _normalize_object_id(user_id)
327
  category_object_id = _resolve_category_id(category_id, endpoint_path, default_category_id)
328
- now = datetime.utcnow()
 
 
 
329
 
 
330
  update_existing = collection.update_one(
331
  {"userId": user_object_id, "categories.categoryId": category_object_id},
332
  {
@@ -336,21 +348,41 @@ def log_media_click(
336
  )
337
 
338
  if update_existing.matched_count == 0:
339
- collection.update_one(
340
- {"userId": user_object_id},
341
- {
342
- "$setOnInsert": {"createdAt": now},
343
- "$set": {"updatedAt": now},
344
- "$push": {
345
- "categories": {
346
- "categoryId": category_object_id,
347
- "click_count": 1,
348
- "lastClickedAt": now,
349
- }
 
 
 
 
 
350
  },
351
- },
352
- upsert=True,
353
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
  logger.info("Media click logged for user %s", str(user_object_id))
356
  return True
 
97
  if raw_value is None:
98
  return ObjectId()
99
 
100
+ # Handle empty strings as None
101
+ if isinstance(raw_value, str) and not raw_value.strip():
102
+ return ObjectId()
103
+
104
  try:
105
  if isinstance(raw_value, int) or (isinstance(raw_value, str) and raw_value.strip().lstrip("-").isdigit()):
106
  int_value = int(str(raw_value).strip())
 
137
  default_category_id: Optional[str],
138
  ) -> ObjectId:
139
  """Pick category id from explicit value, endpoint default, or fallback."""
140
+ # Handle empty strings as None
141
+ if isinstance(category_id, str) and not category_id.strip():
142
+ category_id = None
143
+
144
  endpoint_map = {
145
  "colorization": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
146
  "upload": os.getenv("DEFAULT_CATEGORY_COLORIZATION"),
 
333
 
334
  user_object_id = _normalize_object_id(user_id)
335
  category_object_id = _resolve_category_id(category_id, endpoint_path, default_category_id)
336
+ now = datetime.utcnow()
337
+
338
+ logger.info("Media click - user_id input: %s, normalized: %s, category_id input: %s, normalized: %s",
339
+ user_id, str(user_object_id), category_id, str(category_object_id))
340
 
341
+ # Try to update existing category in new schema (categories.categoryId)
342
  update_existing = collection.update_one(
343
  {"userId": user_object_id, "categories.categoryId": category_object_id},
344
  {
 
348
  )
349
 
350
  if update_existing.matched_count == 0:
351
+ # Category not found in new schema, check if user exists
352
+ user_exists = collection.find_one({"userId": user_object_id})
353
+
354
+ if user_exists:
355
+ # User exists but category doesn't in new schema - add to categories array
356
+ collection.update_one(
357
+ {"userId": user_object_id},
358
+ {
359
+ "$set": {"updatedAt": now},
360
+ "$push": {
361
+ "categories": {
362
+ "categoryId": category_object_id,
363
+ "click_count": 1,
364
+ "lastClickedAt": now,
365
+ }
366
+ },
367
  },
368
+ )
369
+ else:
370
+ # User doesn't exist - create new document with categories array
371
+ collection.update_one(
372
+ {"userId": user_object_id},
373
+ {
374
+ "$setOnInsert": {"createdAt": now},
375
+ "$set": {"updatedAt": now},
376
+ "$push": {
377
+ "categories": {
378
+ "categoryId": category_object_id,
379
+ "click_count": 1,
380
+ "lastClickedAt": now,
381
+ }
382
+ },
383
+ },
384
+ upsert=True,
385
+ )
386
 
387
  logger.info("Media click logged for user %s", str(user_object_id))
388
  return True
app/main.py CHANGED
@@ -129,8 +129,10 @@ def verify_app_check_token(token: str):
129
  return True
130
 
131
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
132
- """Return supplied user_id if provided, otherwise None (will auto-generate in log_media_click)."""
133
- return supplied_user_id
 
 
134
 
135
  # -------------------------------------------------
136
  # 📤 Upload Image
@@ -148,7 +150,11 @@ async def upload_image(
148
 
149
  ip_address = request.client.host if request.client else None
150
  effective_user_id = _resolve_user_id(request, user_id)
151
- effective_category_id = category_id or categoryId
 
 
 
 
152
 
153
  if not file.content_type.startswith("image/"):
154
  log_api_call(
@@ -225,7 +231,11 @@ async def colorize(
225
 
226
  ip_address = request.client.host if request.client else None
227
  effective_user_id = _resolve_user_id(request, user_id)
228
- effective_category_id = category_id or categoryId
 
 
 
 
229
 
230
  if not file.content_type.startswith("image/"):
231
  log_api_call(
 
129
  return True
130
 
131
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
132
+ """Return supplied user_id if provided and not empty, otherwise None (will auto-generate in log_media_click)."""
133
+ if supplied_user_id and supplied_user_id.strip():
134
+ return supplied_user_id.strip()
135
+ return None
136
 
137
  # -------------------------------------------------
138
  # 📤 Upload Image
 
150
 
151
  ip_address = request.client.host if request.client else None
152
  effective_user_id = _resolve_user_id(request, user_id)
153
+ effective_category_id = (category_id or categoryId) if (category_id or categoryId) else None
154
+ if effective_category_id:
155
+ effective_category_id = effective_category_id.strip() if isinstance(effective_category_id, str) else effective_category_id
156
+ if not effective_category_id:
157
+ effective_category_id = None
158
 
159
  if not file.content_type.startswith("image/"):
160
  log_api_call(
 
231
 
232
  ip_address = request.client.host if request.client else None
233
  effective_user_id = _resolve_user_id(request, user_id)
234
+ effective_category_id = (category_id or categoryId) if (category_id or categoryId) else None
235
+ if effective_category_id:
236
+ effective_category_id = effective_category_id.strip() if isinstance(effective_category_id, str) else effective_category_id
237
+ if not effective_category_id:
238
+ effective_category_id = None
239
 
240
  if not file.content_type.startswith("image/"):
241
  log_api_call(
app/main_fastai.py CHANGED
@@ -209,8 +209,10 @@ async def verify_request(request: Request):
209
 
210
 
211
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
212
- """Return supplied user_id if provided, otherwise None (will auto-generate in log_media_click)."""
213
- return supplied_user_id
 
 
214
 
215
  @app.get("/api")
216
  async def api_info(request: Request):
@@ -382,7 +384,11 @@ async def colorize_api(
382
  start_time = time.time()
383
 
384
  effective_user_id = _resolve_user_id(request, user_id)
385
- effective_category_id = category_id or categoryId
 
 
 
 
386
 
387
  ip_address = request.client.host if request.client else None
388
 
 
209
 
210
 
211
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
212
+ """Return supplied user_id if provided and not empty, otherwise None (will auto-generate in log_media_click)."""
213
+ if supplied_user_id and supplied_user_id.strip():
214
+ return supplied_user_id.strip()
215
+ return None
216
 
217
  @app.get("/api")
218
  async def api_info(request: Request):
 
384
  start_time = time.time()
385
 
386
  effective_user_id = _resolve_user_id(request, user_id)
387
+ effective_category_id = (category_id or categoryId) if (category_id or categoryId) else None
388
+ if effective_category_id:
389
+ effective_category_id = effective_category_id.strip() if isinstance(effective_category_id, str) else effective_category_id
390
+ if not effective_category_id:
391
+ effective_category_id = None
392
 
393
  ip_address = request.client.host if request.client else None
394
 
app/main_sdxl.py CHANGED
@@ -308,8 +308,10 @@ async def verify_request(request: Request):
308
 
309
 
310
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
311
- """Return supplied user_id if provided, otherwise None (will auto-generate in log_media_click)."""
312
- return supplied_user_id
 
 
313
 
314
 
315
  # ========== Auth Endpoints ==========
@@ -648,7 +650,11 @@ async def upload_image(
648
  Requires Firebase App Check authentication.
649
  """
650
  effective_user_id = _resolve_user_id(request, user_id)
651
- effective_category_id = category_id or categoryId
 
 
 
 
652
 
653
  ip_address = request.client.host if request.client else None
654
 
@@ -754,7 +760,11 @@ async def colorize_api(
754
  start_time = time.time()
755
 
756
  effective_user_id = _resolve_user_id(request, user_id)
757
- effective_category_id = category_id or categoryId
 
 
 
 
758
 
759
  ip_address = request.client.host if request.client else None
760
 
 
308
 
309
 
310
  def _resolve_user_id(request: Request, supplied_user_id: Optional[str]) -> Optional[str]:
311
+ """Return supplied user_id if provided and not empty, otherwise None (will auto-generate in log_media_click)."""
312
+ if supplied_user_id and supplied_user_id.strip():
313
+ return supplied_user_id.strip()
314
+ return None
315
 
316
 
317
  # ========== Auth Endpoints ==========
 
650
  Requires Firebase App Check authentication.
651
  """
652
  effective_user_id = _resolve_user_id(request, user_id)
653
+ effective_category_id = (category_id or categoryId) if (category_id or categoryId) else None
654
+ if effective_category_id:
655
+ effective_category_id = effective_category_id.strip() if isinstance(effective_category_id, str) else effective_category_id
656
+ if not effective_category_id:
657
+ effective_category_id = None
658
 
659
  ip_address = request.client.host if request.client else None
660
 
 
760
  start_time = time.time()
761
 
762
  effective_user_id = _resolve_user_id(request, user_id)
763
+ effective_category_id = (category_id or categoryId) if (category_id or categoryId) else None
764
+ if effective_category_id:
765
+ effective_category_id = effective_category_id.strip() if isinstance(effective_category_id, str) else effective_category_id
766
+ if not effective_category_id:
767
+ effective_category_id = None
768
 
769
  ip_address = request.client.host if request.client else None
770
 
postman_collection.json CHANGED
@@ -78,6 +78,12 @@
78
  "value": "{{category_id}}",
79
  "type": "text",
80
  "description": "Optional category id; endpoint default used when omitted"
 
 
 
 
 
 
81
  }
82
  ]
83
  },
@@ -136,6 +142,12 @@
136
  "value": "{{category_id}}",
137
  "type": "text",
138
  "description": "Optional category id; endpoint default used when omitted"
 
 
 
 
 
 
139
  }
140
  ]
141
  },
@@ -212,6 +224,12 @@
212
  "value": "{{category_id}}",
213
  "type": "text",
214
  "description": "Optional category id; endpoint default used when omitted"
 
 
 
 
 
 
215
  }
216
  ]
217
  },
@@ -272,6 +290,64 @@
272
  },
273
  "response": []
274
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  {
276
  "name": "Colorize Image - Without Auth",
277
  "request": {
@@ -331,6 +407,30 @@
331
  "value": "",
332
  "type": "string",
333
  "description": "Filename from colorize response (e.g., uuid.png)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  }
335
  ]
336
  }
 
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
  },
 
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
  },
 
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
  },
 
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": {
 
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
  }