feng-x commited on
Commit
783b186
·
verified ·
1 Parent(s): 81c9c6a

Upload folder using huggingface_hub

Browse files
src/ring_size.py CHANGED
@@ -32,6 +32,12 @@ DEFAULT_RING_MODEL = "gen"
32
  # Backwards-compatible alias
33
  RING_SIZE_CHART = RING_MODELS[DEFAULT_RING_MODEL]
34
 
 
 
 
 
 
 
35
 
36
  def _get_sorted_sizes(ring_model: str) -> List[Tuple[int, float]]:
37
  chart = RING_MODELS.get(ring_model, RING_MODELS[DEFAULT_RING_MODEL])
@@ -51,7 +57,7 @@ def recommend_ring_size(diameter_cm: float, ring_model: str = DEFAULT_RING_MODEL
51
  """
52
  diameter_mm = diameter_cm * 10.0
53
 
54
- if diameter_mm < 14.0 or diameter_mm > 26.0:
55
  return None
56
 
57
  sorted_sizes = _get_sorted_sizes(ring_model)
@@ -105,15 +111,32 @@ def aggregate_ring_sizes(per_finger_results: Dict[str, Dict]) -> Dict:
105
  # Build per_finger summary
106
  per_finger: Dict[str, Dict] = {}
107
  for name, result in per_finger_results.items():
108
- failed = result.get("fail_reason") is not None or result.get("ring_size") is None
109
  rs = result.get("ring_size")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  per_finger[name] = {
111
  "diameter_cm": result.get("finger_outer_diameter_cm"),
112
  "confidence": result.get("confidence", 0.0),
113
  "best_match": rs["best_match"] if rs else None,
114
  "range": [rs["range_min"], rs["range_max"]] if rs else None,
115
  "status": "failed" if failed else "ok",
116
- "fail_reason": result.get("fail_reason"),
117
  }
118
 
119
  # Filter to succeeded fingers
 
32
  # Backwards-compatible alias
33
  RING_SIZE_CHART = RING_MODELS[DEFAULT_RING_MODEL]
34
 
35
+ # Diameter bounds for the ring-size lookup. Below MIN or above MAX,
36
+ # recommend_ring_size() returns None and aggregate_ring_sizes() tags the
37
+ # per-finger row as failed with diameter_below_chart_min / diameter_above_chart_max.
38
+ MIN_DIAMETER_MM = 12.0
39
+ MAX_DIAMETER_MM = 26.0
40
+
41
 
42
  def _get_sorted_sizes(ring_model: str) -> List[Tuple[int, float]]:
43
  chart = RING_MODELS.get(ring_model, RING_MODELS[DEFAULT_RING_MODEL])
 
57
  """
58
  diameter_mm = diameter_cm * 10.0
59
 
60
+ if diameter_mm < MIN_DIAMETER_MM or diameter_mm > MAX_DIAMETER_MM:
61
  return None
62
 
63
  sorted_sizes = _get_sorted_sizes(ring_model)
 
111
  # Build per_finger summary
112
  per_finger: Dict[str, Dict] = {}
113
  for name, result in per_finger_results.items():
 
114
  rs = result.get("ring_size")
115
+ upstream_reason = result.get("fail_reason")
116
+ failed = upstream_reason is not None or rs is None
117
+
118
+ # If the measurement succeeded but ring_size is None, the diameter
119
+ # fell outside the chart bounds — surface that as a fail_reason so
120
+ # the dashboard does not show "failed, reason unknown".
121
+ derived_reason = upstream_reason
122
+ if derived_reason is None and rs is None:
123
+ diameter_cm = result.get("finger_outer_diameter_cm")
124
+ if diameter_cm is None:
125
+ derived_reason = "ring_size_lookup_failed"
126
+ elif diameter_cm * 10.0 < MIN_DIAMETER_MM:
127
+ derived_reason = "diameter_below_chart_min"
128
+ elif diameter_cm * 10.0 > MAX_DIAMETER_MM:
129
+ derived_reason = "diameter_above_chart_max"
130
+ else:
131
+ derived_reason = "ring_size_lookup_failed"
132
+
133
  per_finger[name] = {
134
  "diameter_cm": result.get("finger_outer_diameter_cm"),
135
  "confidence": result.get("confidence", 0.0),
136
  "best_match": rs["best_match"] if rs else None,
137
  "range": [rs["range_min"], rs["range_max"]] if rs else None,
138
  "status": "failed" if failed else "ok",
139
+ "fail_reason": derived_reason,
140
  }
141
 
142
  # Filter to succeeded fingers
web_demo/app.py CHANGED
@@ -556,9 +556,19 @@ def _compute_stats(rows: List[Dict[str, Any]], days: int = 30) -> Dict[str, Any]
556
  if m:
557
  mode_counter[m] += 1
558
 
559
- size = row.get("overall_best_size")
560
- if size not in (None, ""):
561
- size_counter[str(size)] += 1
 
 
 
 
 
 
 
 
 
 
562
 
563
  conf = row.get("confidence")
564
  if isinstance(conf, (int, float)):
 
556
  if m:
557
  mode_counter[m] += 1
558
 
559
+ # Ring size distribution: index-finger size only, and exclude demo
560
+ # runs (photo_url is null when the user clicks "try with sample"
561
+ # those measurements aren't the KOL's true size).
562
+ if row.get("photo_url"):
563
+ index_size = None
564
+ if row.get("mode") == "multi":
565
+ pf = (row.get("per_finger") or {}).get("index") or {}
566
+ if pf.get("status") == "ok":
567
+ index_size = pf.get("best_match")
568
+ elif row.get("finger_index") == "index":
569
+ index_size = row.get("overall_best_size")
570
+ if index_size not in (None, ""):
571
+ size_counter[str(index_size)] += 1
572
 
573
  conf = row.get("confidence")
574
  if isinstance(conf, (int, float)):
web_demo/supabase_client.py CHANGED
@@ -122,15 +122,17 @@ def list_measurements(limit: int = 200, offset: int = 0) -> List[Dict[str, Any]]
122
 
123
  STATS_COLUMNS = (
124
  "id,created_at,kol_name,mode,ring_model,confidence,fail_reason,"
125
- "overall_best_size,ring_fit,gt_index_size,gt_middle_size,gt_ring_size"
 
126
  )
127
 
128
 
129
  def list_measurements_for_stats(limit: int = 5000) -> List[Dict[str, Any]]:
130
  """Lightweight projection used by the admin stats endpoint.
131
 
132
- Skips heavy columns (`result_json`, `per_finger`, photo URLs) so we can
133
- pull thousands of rows for aggregation without paying the JSON-blob cost.
 
134
  """
135
  client = _get_client()
136
  if client is None:
 
122
 
123
  STATS_COLUMNS = (
124
  "id,created_at,kol_name,mode,ring_model,confidence,fail_reason,"
125
+ "overall_best_size,ring_fit,gt_index_size,gt_middle_size,gt_ring_size,"
126
+ "finger_index,photo_url,per_finger"
127
  )
128
 
129
 
130
  def list_measurements_for_stats(limit: int = 5000) -> List[Dict[str, Any]]:
131
  """Lightweight projection used by the admin stats endpoint.
132
 
133
+ Skips `result_json` (the heaviest blob). `per_finger` is included so the
134
+ size-distribution chart can use the index-finger size and detect demo
135
+ runs (`photo_url` is null when the user clicks "try with sample").
136
  """
137
  client = _get_client()
138
  if client is None:
web_demo/templates/admin.html CHANGED
@@ -211,7 +211,7 @@
211
  <div class="chart-wrap"><canvas id="chartModels"></canvas></div>
212
  </div>
213
  <div class="chart-card">
214
- <h2>Ring Size Distribution</h2>
215
  <div class="chart-wrap"><canvas id="chartSizes"></canvas></div>
216
  </div>
217
  <div class="chart-card">
 
211
  <div class="chart-wrap"><canvas id="chartModels"></canvas></div>
212
  </div>
213
  <div class="chart-card">
214
+ <h2>Ring Size Distribution (Index, real uploads)</h2>
215
  <div class="chart-wrap"><canvas id="chartSizes"></canvas></div>
216
  </div>
217
  <div class="chart-card">