From 70f7978ce27eaff6e8a4853417fa1d4725b30a33 Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Thu, 21 May 2026 12:20:47 -0400 Subject: [PATCH] 05/21 Fix issue with multiple photos --- app/api/issues.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/app/api/issues.py b/app/api/issues.py index 6e61097..eded629 100644 --- a/app/api/issues.py +++ b/app/api/issues.py @@ -204,12 +204,22 @@ def create_issue(): inspection = None # ── Create ──────────────────────────────────────────────────────────── + # result_photos may be supplied by the mobile app as a list of paths that + # were already uploaded via /api/v1/photos/upload (one call per photo). + # Validate that it is a list of non-empty strings; silently drop bad entries. + raw_result_photos = data.get('result_photos') + if isinstance(raw_result_photos, list): + result_photos = [p for p in raw_result_photos if isinstance(p, str) and p.strip()] + else: + result_photos = [] + issue = Issue( inspection_id = inspection_id, facility_id = facility_id, severity = severity, description = description, photo_path = data.get('photo_path') or None, + result_photos = result_photos or None, status = 'open', reported_at = now_eastern(), reported_by = user.id, @@ -334,4 +344,65 @@ def update_issue_status(issue_id): logger.info('API ISSUES | status_updated | issue_id=%d | %s→%s | user=%s', issue.id, old_status, new_status, user.username) - return api_ok({'issue_id': issue.id, 'status': issue.status}) \ No newline at end of file + return api_ok({'issue_id': issue.id, 'status': issue.status}) + +# ── Update Issue Photos (mobile) ────────────────────────────────────────────── + +@bp.route('/issues//photos', methods=['PATCH']) +@jwt_required +def update_issue_photos(issue_id): + """ + Attach additional photos to an existing issue created from the mobile app. + + Called by the iOS app immediately after create_issue when the inspector + attached more than one photo. The photos are already uploaded to the server + via /api/v1/photos/upload; this endpoint stores their paths in result_photos. + + Request JSON + ------------ + { + "result_photos": ["uploads/issue_photos/a.jpg", "uploads/issue_photos/b.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) + + # Inspectors may only update issues they reported or are assigned to + if user.role == 'inspector' and issue.assigned_to != user.id and issue.reported_by != user.id: + 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 with any existing result_photos rather than overwriting, + # so multiple PATCH calls (e.g. retry) are idempotent. + 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 | 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)}) \ No newline at end of file