From a40eaa91061b4f5f20146bdc959945de9376269f Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Wed, 24 Jun 2026 16:21:41 -0400 Subject: [PATCH] Jun 24 - Update API to support Issue resolve photos --- app/api/issues.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++ app/api/photos.py | 4 +++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/app/api/issues.py b/app/api/issues.py index 90f6a61..6604370 100644 --- a/app/api/issues.py +++ b/app/api/issues.py @@ -433,3 +433,64 @@ def update_issue_photos(issue_id): issue.id, len(new_photos), user.username) return api_ok({'issue_id': issue.id, 'result_photos_count': len(merged)}) + +# ── Attach Resolution Photos (mobile) ───────────────────────────────────────── + +@bp.route('/issues//result_photos', methods=['PATCH']) +@jwt_required +def update_issue_result_photos(issue_id): + """ + Attach resolution photos to an issue from the mobile app. + + Called when an inspector marks an issue resolved and uploads photos + showing the fix. Stored in Issue.result_photos so they appear under + "Resolution Details" on the web — identical to photos uploaded via the + web update form. + + Request JSON + ------------ + { "result_photos": ["uploads/issue_result_photos/a.jpg", ...] } + + Response 200 + ------------ + { "ok": true, "data": { "issue_id": 99, "result_photos_count": 2 } } + """ + user = g.api_user + if user.role not in _ALLOWED_ROLES: + return api_error('Access denied', 403) + + issue = db.session.get(Issue, issue_id) + if issue is None: + return api_error('Issue not found', 404) + + if user.role == 'inspector': + fids = get_inspector_scope(user) + facility = issue.resolved_facility + if not fids or not facility or facility.id not in fids: + return api_error('Access denied', 403) + + data = request.get_json(silent=True) or {} + raw = data.get('result_photos') + + if not isinstance(raw, list): + return api_error('result_photos must be a list of path strings', 400) + + new_photos = [p for p in raw if isinstance(p, str) and p.strip()] + if not new_photos: + return api_error('result_photos must contain at least one valid path', 400) + + # Merge idempotently with any existing result_photos + existing = issue.result_photos or [] + merged = existing + [p for p in new_photos if p not in existing] + issue.result_photos = merged + + db.session.commit() + + log_action(ACTION_UPDATE, 'Issue', issue.id, + f'result_photos updated (+{len(new_photos)} photos)', + f'source=mobile; updated_by={user.username}') + + logger.info('API ISSUES | result_photos_updated | issue_id=%d | added=%d | user=%s', + issue.id, len(new_photos), user.username) + + return api_ok({'issue_id': issue.id, 'result_photos_count': len(merged)}) diff --git a/app/api/photos.py b/app/api/photos.py index fcfbbff..852874d 100644 --- a/app/api/photos.py +++ b/app/api/photos.py @@ -46,7 +46,7 @@ def upload_photo(): Multipart form fields --------------------- file — binary image data (jpg / png / gif) - entity_type — "inspection" | "issue" (controls subfolder) + entity_type — "inspection" | "issue" | "issue_result" (controls subfolder) Response 200 ------------ @@ -80,6 +80,8 @@ def upload_photo(): # Determine destination subfolder if entity_type == 'issue': subfolder = 'issue_photos' + elif entity_type == 'issue_result': + subfolder = 'issue_result_photos' else: subfolder = 'inspection_photos'