Pepguy commited on
Commit
02dd8cb
·
verified ·
1 Parent(s): c52a88f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -9
app.py CHANGED
@@ -1,4 +1,3 @@
1
- # server_single_suggest.py
2
  """
3
  Single-endpoint suggestion server.
4
 
@@ -15,6 +14,7 @@ import logging
15
  import uuid
16
  import time
17
  import difflib
 
18
  from typing import List, Dict, Any, Set, Optional
19
 
20
  from flask import Flask, request, jsonify
@@ -127,12 +127,52 @@ def _safe_item_brand(itm: Dict[str, Any]) -> str:
127
  return str(brand).strip()
128
 
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  # ---------- Simple local candidate generator ----------
131
  def naive_generate_candidates(wardrobe_items: List[Dict[str, Any]],
132
  user_inputs: Dict[str, Any],
133
  user_profile: Dict[str, Any],
134
  past_week_items: List[Dict[str, Any]],
135
  max_candidates: int = 6) -> List[Dict[str, Any]]:
 
 
 
136
  grouped = {}
137
  for itm in wardrobe_items:
138
  title = (itm.get("title") or (itm.get("analysis") or {}).get("type") or itm.get("label") or "")
@@ -177,7 +217,15 @@ def naive_generate_candidates(wardrobe_items: List[Dict[str, Any]],
177
  score = max(0, min(1, score + (0.02 * ((hash(ids) % 100) / 100.0))))
178
  candidate = {
179
  "id": str(uuid.uuid4()),
180
- "items": [{"id": x.get("id"), "label": x.get("label"), "title": x.get("title"), "thumbnail_url": x.get("thumbnail_url"), "analysis": x.get("analysis", {})} for x in items],
 
 
 
 
 
 
 
 
181
  "score": round(float(score), 3),
182
  "reason": "Auto combo",
183
  "notes": "",
@@ -435,16 +483,19 @@ def fetch_wardrobe_from_firestore(uid: str) -> List[Dict[str, Any]]:
435
  items = []
436
  for d in docs:
437
  dd = d.to_dict() or {}
 
 
438
  items.append({
439
  "id": dd.get("id") or d.id,
440
  "label": dd.get("label") or dd.get("title") or "item",
441
  "title": dd.get("title") or dd.get("label") or "",
442
- "thumbnail_url": dd.get("thumbnail_url"),
443
  "analysis": dd.get("analysis", {}),
444
  "confidence": dd.get("confidence", 0.8),
445
  })
446
  if items:
447
- return items
 
448
  except Exception as e:
449
  log.warning("users/{uid}/wardrobe subcollection read failed: %s", e)
450
 
@@ -455,15 +506,16 @@ def fetch_wardrobe_from_firestore(uid: str) -> List[Dict[str, Any]]:
455
  items = []
456
  for d in docs:
457
  dd = d.to_dict() or {}
 
458
  items.append({
459
  "id": dd.get("id") or d.id,
460
  "label": dd.get("label") or dd.get("title") or "item",
461
  "title": dd.get("title") or dd.get("label") or "",
462
- "thumbnail_url": dd.get("thumbnail_url"),
463
  "analysis": dd.get("analysis", {}),
464
  "confidence": dd.get("confidence", 0.8),
465
  })
466
- return items
467
  except Exception as e:
468
  log.warning("wardrobe collection query failed: %s", e)
469
  return []
@@ -505,6 +557,8 @@ def suggest_all():
505
  """
506
  is_multipart = request.content_type and request.content_type.startswith("multipart/form-data")
507
  try:
 
 
508
  if is_multipart:
509
  # access form fields and files
510
  form = request.form
@@ -516,21 +570,20 @@ def suggest_all():
516
  if ui_raw:
517
  user_inputs = json.loads(ui_raw)
518
  else:
519
- # collect obvious form fields into user_inputs if given
520
  user_inputs = {}
521
  except Exception:
522
  user_inputs = {}
523
  max_c = int(form.get("max_candidates") or 6)
524
- wardrobe_items = []
525
  w_raw = form.get("wardrobe_items")
526
  if w_raw:
527
  try:
528
  wardrobe_items = json.loads(w_raw)
529
  except Exception:
530
  wardrobe_items = []
 
531
  # audio file
532
  audio_file = files.get("audio")
533
- audio_b64 = None
534
  if audio_file:
535
  try:
536
  audio_bytes = audio_file.read()
@@ -548,6 +601,12 @@ def suggest_all():
548
  log.exception("Invalid request payload: %s", e)
549
  return jsonify({"error": "invalid request payload"}), 400
550
 
 
 
 
 
 
 
551
  # if wardrobe_items empty, attempt to fetch from Firestore for uid
552
  if not wardrobe_items:
553
  try:
 
 
1
  """
2
  Single-endpoint suggestion server.
3
 
 
14
  import uuid
15
  import time
16
  import difflib
17
+ import base64
18
  from typing import List, Dict, Any, Set, Optional
19
 
20
  from flask import Flask, request, jsonify
 
127
  return str(brand).strip()
128
 
129
 
130
+ # ---------- Normalization helpers ----------
131
+ def normalize_wardrobe_item(itm: Dict[str, Any]) -> Dict[str, Any]:
132
+ """
133
+ Accepts various incoming key naming schemes (camelCase or snake_case) and returns a normalized dict
134
+ with keys: id, label, title, thumbnail_url, analysis, confidence, brand, etc.
135
+ """
136
+ if not isinstance(itm, dict):
137
+ return {}
138
+
139
+ # Prefer explicit thumbnail_url, then camelCase thumbnailUrl, then thumbnail, then thumbnailPath
140
+ thumb = itm.get("thumbnail_url") or itm.get("thumbnailUrl") or itm.get("thumbnail") or itm.get("thumbnailPath") or None
141
+
142
+ title = itm.get("title") or itm.get("label") or (itm.get("analysis") or {}).get("type") or ""
143
+ label = itm.get("label") or itm.get("title") or title or ""
144
+ confidence = itm.get("confidence")
145
+ if confidence is None:
146
+ confidence = itm.get("score") or itm.get("prob") or 0.8
147
+
148
+ normalized = {
149
+ "id": itm.get("id") or itm.get("doc_id") or "",
150
+ "label": label,
151
+ "title": title,
152
+ "thumbnail_url": thumb,
153
+ "analysis": itm.get("analysis") or {},
154
+ "confidence": float(confidence) if confidence is not None else 0.8,
155
+ # keep original payload for debugging if needed
156
+ "_raw": itm,
157
+ }
158
+ # try to include brand/top-level fields if present
159
+ if itm.get("brand"):
160
+ try:
161
+ normalized["analysis"]["brand"] = itm.get("brand")
162
+ except Exception:
163
+ pass
164
+ return normalized
165
+
166
+
167
  # ---------- Simple local candidate generator ----------
168
  def naive_generate_candidates(wardrobe_items: List[Dict[str, Any]],
169
  user_inputs: Dict[str, Any],
170
  user_profile: Dict[str, Any],
171
  past_week_items: List[Dict[str, Any]],
172
  max_candidates: int = 6) -> List[Dict[str, Any]]:
173
+ # ensure items are normalized (safe guard)
174
+ wardrobe_items = [normalize_wardrobe_item(it) for it in (wardrobe_items or [])]
175
+
176
  grouped = {}
177
  for itm in wardrobe_items:
178
  title = (itm.get("title") or (itm.get("analysis") or {}).get("type") or itm.get("label") or "")
 
217
  score = max(0, min(1, score + (0.02 * ((hash(ids) % 100) / 100.0))))
218
  candidate = {
219
  "id": str(uuid.uuid4()),
220
+ "items": [
221
+ {
222
+ "id": x.get("id"),
223
+ "label": x.get("label"),
224
+ "title": x.get("title"),
225
+ "thumbnail_url": x.get("thumbnail_url"),
226
+ "analysis": x.get("analysis", {})
227
+ } for x in items
228
+ ],
229
  "score": round(float(score), 3),
230
  "reason": "Auto combo",
231
  "notes": "",
 
483
  items = []
484
  for d in docs:
485
  dd = d.to_dict() or {}
486
+ # accept both snake_case and camelCase thumbnail fields
487
+ thumb = dd.get("thumbnail_url") or dd.get("thumbnailUrl") or dd.get("thumbnail") or dd.get("thumbnailPath") or None
488
  items.append({
489
  "id": dd.get("id") or d.id,
490
  "label": dd.get("label") or dd.get("title") or "item",
491
  "title": dd.get("title") or dd.get("label") or "",
492
+ "thumbnail_url": thumb,
493
  "analysis": dd.get("analysis", {}),
494
  "confidence": dd.get("confidence", 0.8),
495
  })
496
  if items:
497
+ # normalize and return
498
+ return [normalize_wardrobe_item(it) for it in items]
499
  except Exception as e:
500
  log.warning("users/{uid}/wardrobe subcollection read failed: %s", e)
501
 
 
506
  items = []
507
  for d in docs:
508
  dd = d.to_dict() or {}
509
+ thumb = dd.get("thumbnail_url") or dd.get("thumbnailUrl") or dd.get("thumbnail") or dd.get("thumbnailPath") or None
510
  items.append({
511
  "id": dd.get("id") or d.id,
512
  "label": dd.get("label") or dd.get("title") or "item",
513
  "title": dd.get("title") or dd.get("label") or "",
514
+ "thumbnail_url": thumb,
515
  "analysis": dd.get("analysis", {}),
516
  "confidence": dd.get("confidence", 0.8),
517
  })
518
+ return [normalize_wardrobe_item(it) for it in items]
519
  except Exception as e:
520
  log.warning("wardrobe collection query failed: %s", e)
521
  return []
 
557
  """
558
  is_multipart = request.content_type and request.content_type.startswith("multipart/form-data")
559
  try:
560
+ wardrobe_items = []
561
+ audio_b64 = None
562
  if is_multipart:
563
  # access form fields and files
564
  form = request.form
 
570
  if ui_raw:
571
  user_inputs = json.loads(ui_raw)
572
  else:
 
573
  user_inputs = {}
574
  except Exception:
575
  user_inputs = {}
576
  max_c = int(form.get("max_candidates") or 6)
577
+
578
  w_raw = form.get("wardrobe_items")
579
  if w_raw:
580
  try:
581
  wardrobe_items = json.loads(w_raw)
582
  except Exception:
583
  wardrobe_items = []
584
+
585
  # audio file
586
  audio_file = files.get("audio")
 
587
  if audio_file:
588
  try:
589
  audio_bytes = audio_file.read()
 
601
  log.exception("Invalid request payload: %s", e)
602
  return jsonify({"error": "invalid request payload"}), 400
603
 
604
+ # normalize any incoming wardrobe_items (accept camelCase/snake_case)
605
+ try:
606
+ wardrobe_items = [normalize_wardrobe_item(it) for it in (wardrobe_items or [])]
607
+ except Exception:
608
+ wardrobe_items = []
609
+
610
  # if wardrobe_items empty, attempt to fetch from Firestore for uid
611
  if not wardrobe_items:
612
  try: