Jul 17 - Update Facility/Area QR code report page with anti-duplication reports
This commit is contained in:
+50
-7
@@ -62,6 +62,31 @@ def _save_report_photos(file_list):
|
||||
return photo_path, extra_paths
|
||||
|
||||
|
||||
#: Window within which an identical public report is treated as a duplicate.
|
||||
DUPLICATE_REPORT_WINDOW_SECONDS = 60
|
||||
|
||||
|
||||
def _recent_duplicate_report(facility_id, area_id, description):
|
||||
"""Return True if an identical public report was just filed.
|
||||
|
||||
Belt-and-suspenders against duplicate submissions (double-taps, JS-disabled
|
||||
clients, retries): if a public issue (reported_by IS NULL) with the same
|
||||
facility/area and identical description was created within the last
|
||||
DUPLICATE_REPORT_WINDOW_SECONDS, treat this one as a duplicate and skip it.
|
||||
"""
|
||||
cutoff = now_eastern() - timedelta(seconds=DUPLICATE_REPORT_WINDOW_SECONDS)
|
||||
q = Issue.query.filter(
|
||||
Issue.reported_by.is_(None),
|
||||
Issue.reported_at >= cutoff,
|
||||
Issue.description == description,
|
||||
)
|
||||
if area_id is not None:
|
||||
q = q.filter(Issue.area_id == area_id)
|
||||
else:
|
||||
q = q.filter(Issue.facility_id == facility_id, Issue.area_id.is_(None))
|
||||
return db.session.query(q.exists()).scalar()
|
||||
|
||||
|
||||
def _facility_by_token_or_404(token: str) -> Facility:
|
||||
"""Resolve an ACTIVE facility from its public token, else 404."""
|
||||
if not token:
|
||||
@@ -344,10 +369,6 @@ def report_problem(token):
|
||||
return render_template('public/facility.html',
|
||||
form=form, token=token, **summary), 400
|
||||
|
||||
# Save up to 5 optional photos through the shared, magic-byte-validated
|
||||
# saver. First → photo_path, the rest → mobile_photo_paths.
|
||||
photo_path, extra_photos = _save_report_photos(form.photos.data)
|
||||
|
||||
# Fold optional reporter identity + location into the description; the
|
||||
# public reporter is not a User, so reported_by stays NULL.
|
||||
parts = ['[Reported via facility QR code]']
|
||||
@@ -360,6 +381,19 @@ def report_problem(token):
|
||||
parts.append(form.description.data.strip())
|
||||
description = '\n'.join(parts)
|
||||
|
||||
# Server-side idempotency: silently accept an identical repeat as success
|
||||
# without creating a second issue (or saving its photos).
|
||||
if _recent_duplicate_report(facility.id, None, description):
|
||||
logger.info('PUBLIC REPORT | duplicate suppressed | facility_id=%s | ip=%s',
|
||||
facility.id, request.remote_addr)
|
||||
flash('Thank you — your report has been received and the team has been notified.',
|
||||
'success')
|
||||
return redirect(url_for('public.facility_summary', token=token))
|
||||
|
||||
# Save up to 5 optional photos through the shared, magic-byte-validated
|
||||
# saver. First → photo_path, the rest → mobile_photo_paths.
|
||||
photo_path, extra_photos = _save_report_photos(form.photos.data)
|
||||
|
||||
issue = Issue(
|
||||
facility_id = facility.id,
|
||||
area_id = None,
|
||||
@@ -416,9 +450,6 @@ def area_report_problem(token):
|
||||
return render_template('public/area.html',
|
||||
form=form, token=token, **summary), 400
|
||||
|
||||
# Save up to 5 optional photos (first → photo_path, rest → mobile_photo_paths).
|
||||
photo_path, extra_photos = _save_report_photos(form.photos.data)
|
||||
|
||||
# The area is known from the QR token, so we set area_id directly and note
|
||||
# the source. A public reporter is not a User, so reported_by stays NULL.
|
||||
parts = [f'[Reported via area QR code — {area.name}]']
|
||||
@@ -431,6 +462,18 @@ def area_report_problem(token):
|
||||
parts.append(form.description.data.strip())
|
||||
description = '\n'.join(parts)
|
||||
|
||||
# Server-side idempotency: silently accept an identical repeat as success
|
||||
# without creating a second issue (or saving its photos).
|
||||
if _recent_duplicate_report(facility.id, area.id, description):
|
||||
logger.info('PUBLIC REPORT | duplicate suppressed | area_id=%s | facility_id=%s | ip=%s',
|
||||
area.id, facility.id, request.remote_addr)
|
||||
flash('Thank you — your report has been received and the team has been notified.',
|
||||
'success')
|
||||
return redirect(url_for('public.area_summary', token=token))
|
||||
|
||||
# Save up to 5 optional photos (first → photo_path, rest → mobile_photo_paths).
|
||||
photo_path, extra_photos = _save_report_photos(form.photos.data)
|
||||
|
||||
issue = Issue(
|
||||
facility_id = facility.id,
|
||||
area_id = area.id,
|
||||
|
||||
Reference in New Issue
Block a user