diff --git a/app/models/inspection.py b/app/models/inspection.py
index 8987d47..0789cc2 100644
--- a/app/models/inspection.py
+++ b/app/models/inspection.py
@@ -63,8 +63,10 @@ class Inspection(db.Model):
status = db.Column(db.Enum('in_progress', 'completed', 'flagged'), default='in_progress')
notes = db.Column(db.Text) # inspector free-text notes
form_data = db.Column(db.JSON) # filled form field responses {field_id: value}
- completed_at = db.Column(db.DateTime)
- mobile_local_id = db.Column(db.String(64), nullable=True, index=True) # idempotency key for mobile submissions
+ completed_at = db.Column(db.DateTime)
+ mobile_local_id = db.Column(db.String(64), nullable=True, index=True)
+ submit_latitude = db.Column(db.Numeric(10, 7), nullable=True)
+ submit_longitude = db.Column(db.Numeric(10, 7), nullable=True)
# ── Re-inspection / follow-up workflow ────────────────────────────────
parent_inspection_id = db.Column(
diff --git a/app/routes/inspections.py b/app/routes/inspections.py
index 9bdb68f..cbc6b22 100644
--- a/app/routes/inspections.py
+++ b/app/routes/inspections.py
@@ -533,6 +533,16 @@ def execute(inspection_id):
inspection.status = 'completed'
inspection.completed_at = now_eastern()
+ # Capture GPS coordinates submitted by the browser Geolocation API
+ try:
+ _lat = request.form.get('submit_lat', '').strip()
+ _lng = request.form.get('submit_lng', '').strip()
+ if _lat and _lng:
+ inspection.submit_latitude = float(_lat)
+ inspection.submit_longitude = float(_lng)
+ except (ValueError, TypeError):
+ pass
+
# Snapshot the current template schema so view() renders correctly
# even if the template is later edited or deleted.
_save_responses(inspection, responses, snapshot_schema=form_fields)
diff --git a/app/templates/inspections/execute.html b/app/templates/inspections/execute.html
index 8d85a85..4f4dcbd 100644
--- a/app/templates/inspections/execute.html
+++ b/app/templates/inspections/execute.html
@@ -238,6 +238,8 @@
+ {# ── Submission GPS (admin / director only) ──────────────────────────── #}
+ {% if current_user.role in ['admin', 'director'] and inspection.submit_latitude and inspection.submit_longitude %}
+ {% set _lat = inspection.submit_latitude | float %}
+ {% set _lng = inspection.submit_longitude | float %}
+ {% set _maps_key = config.get('GOOGLE_MAPS_API_KEY', '') %}
+
+
+ Submission Location
+
+ {% if _maps_key %}
+
+

+
+ {% endif %}
+
+
+ {% endif %}
+
{# ── Build the filtered field set ──────────────────────────────────────
Strategy:
1. Find all grid rows that contain at least one rated rating field.
diff --git a/config.py b/config.py
index 8cb36a4..841847d 100644
--- a/config.py
+++ b/config.py
@@ -51,6 +51,9 @@ class Config:
# ── Digest email secret token (used to authenticate cron trigger) ────────
DIGEST_SECRET = os.environ.get('DIGEST_SECRET')
+ # ── Google Maps (used for GPS map on inspection view) ────────────────────
+ GOOGLE_MAPS_API_KEY = os.environ.get('GOOGLE_MAPS_API_KEY', '')
+
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
diff --git a/migrations/versions/phase25_inspection_gps.py b/migrations/versions/phase25_inspection_gps.py
new file mode 100644
index 0000000..0e78f36
--- /dev/null
+++ b/migrations/versions/phase25_inspection_gps.py
@@ -0,0 +1,36 @@
+"""phase25 — add GPS coordinates to inspections
+
+Adds submit_latitude and submit_longitude columns to the inspections table.
+Populated at web submission time via browser Geolocation API.
+Null for all existing inspections and mobile submissions (handled separately).
+"""
+
+import sqlalchemy as sa
+from alembic import op
+
+revision = 'phase25_inspection_gps'
+down_revision = 'phase24_notify_defaults'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ conn = op.get_bind()
+
+ has_lat = conn.execute(sa.text(
+ "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS "
+ "WHERE TABLE_SCHEMA = DATABASE() "
+ " AND TABLE_NAME = 'inspections' "
+ " AND COLUMN_NAME = 'submit_latitude'"
+ )).scalar()
+
+ if not has_lat:
+ op.add_column('inspections',
+ sa.Column('submit_latitude', sa.Numeric(10, 7), nullable=True))
+ op.add_column('inspections',
+ sa.Column('submit_longitude', sa.Numeric(10, 7), nullable=True))
+
+
+def downgrade():
+ op.drop_column('inspections', 'submit_longitude')
+ op.drop_column('inspections', 'submit_latitude')