diff --git a/app/routes/inspections.py b/app/routes/inspections.py index 99c384c..78c3e60 100644 --- a/app/routes/inspections.py +++ b/app/routes/inspections.py @@ -496,67 +496,80 @@ def view(inspection_id): issues = inspection.issues.order_by(Issue.reported_at.desc()).all() # ── Score comparison against parent inspection ──────────────────────── - # Produces a list of per-item dicts so the template can render a - # side-by-side delta table without any additional queries at render time. + # Scores live in form_data (field_id -> value) inside inspection.notes, + # NOT in InspectionResult checklist rows. We walk the shared form_fields + # schema and compare current vs. parent responses per scoreable field. comparison = None if inspection.parent and inspection.parent.status == 'completed': parent = inspection.parent - # Index parent results by checklist_item_id - parent_results = { - r.checklist_item_id: r - for r in parent.results.all() - } + # Decode parent form_data from its notes field + parent_form_data = {} + if parent.notes: + try: + parsed_p = json.loads(parent.notes) + if isinstance(parsed_p, dict): + parent_form_data = parsed_p.get('_form_data', {}) + except (json.JSONDecodeError, TypeError): + pass + scoreable_types = ('rating', 'checkbox', 'radio') rows = [] - for result in inspection.results.order_by( - InspectionResult.checklist_item_id - ).all(): - item = result.checklist_item - p_res = parent_results.get(result.checklist_item_id) - # Normalise scores to a 0–100 percentage for fair comparison - def _pct(res, ci): - if res is None: - return None - if ci.scoring_type == 'pass_fail': - if res.passed is None: + for field in form_fields: + ftype = field.get('type') + if ftype not in scoreable_types: + continue + + fid = field.get('id', '') + label = field.get('label') or field.get('placeholder') or fid + + cur_val = form_data.get(fid, '') + par_val = parent_form_data.get(fid, '') + + def _field_pct(val, ft): + if ft == 'rating': + try: + v = int(val) + return None if v == 0 else round(v / 5 * 100, 1) + except (ValueError, TypeError): return None - return 100.0 if res.passed else 0.0 - if ci.scoring_type == 'rating_5': - return (float(res.score) / 5 * 100) if res.score is not None else None - if ci.scoring_type == 'rating_10': - return (float(res.score) / 10 * 100) if res.score is not None else None + if ft == 'checkbox': + if val == '' or val is None: + return None + return 100.0 if val == 'true' else 0.0 + if ft == 'radio': + return None if not val else 100.0 return None - cur_pct = _pct(result, item) - par_pct = _pct(p_res, item) if p_res else None + cur_pct = _field_pct(cur_val, ftype) + par_pct = _field_pct(par_val, ftype) if cur_pct is None and par_pct is None: - continue # skip unanswered items on both sides + continue delta = None if cur_pct is not None and par_pct is not None: delta = round(cur_pct - par_pct, 1) rows.append({ - 'category': item.category or 'Uncategorised', - 'description': item.item_description, - 'parent_pct': round(par_pct, 1) if par_pct is not None else None, - 'current_pct': round(cur_pct, 1) if cur_pct is not None else None, + 'category': field.get('section') or field.get('category') or 'General', + 'description': label, + 'parent_pct': par_pct, + 'current_pct': cur_pct, 'delta': delta, }) comparison = { - 'parent_id': parent.id, - 'parent_date': parent.inspection_date, - 'parent_score': float(parent.overall_score) if parent.overall_score else None, + 'parent_id': parent.id, + 'parent_date': parent.inspection_date, + 'parent_score': float(parent.overall_score) if parent.overall_score else None, 'current_score': float(inspection.overall_score) if inspection.overall_score else None, - 'score_delta': ( + 'score_delta': ( round(float(inspection.overall_score) - float(parent.overall_score), 2) if inspection.overall_score and parent.overall_score else None ), - 'rows': rows, + 'rows': rows, 'improved': sum(1 for r in rows if r['delta'] is not None and r['delta'] > 0), 'regressed': sum(1 for r in rows if r['delta'] is not None and r['delta'] < 0), 'unchanged': sum(1 for r in rows if r['delta'] == 0),