Feb 27 2026: add inspection delete button

This commit is contained in:
2026-02-27 12:31:53 -05:00
parent 64445eb793
commit e31027f770
3 changed files with 112 additions and 5 deletions
+1 -1
View File
@@ -65,7 +65,7 @@ class Inspection(db.Model):
completed_at = db.Column(db.DateTime) completed_at = db.Column(db.DateTime)
results = db.relationship('InspectionResult', backref='inspection', lazy='dynamic', cascade='all, delete-orphan') results = db.relationship('InspectionResult', backref='inspection', lazy='dynamic', cascade='all, delete-orphan')
issues = db.relationship('Issue', backref='inspection', lazy='dynamic') issues = db.relationship('Issue', backref='inspection', lazy='dynamic', cascade='all, delete-orphan')
def __repr__(self): def __repr__(self):
return f'<Inspection {self.id} - {self.inspection_date}>' return f'<Inspection {self.id} - {self.inspection_date}>'
+49 -1
View File
@@ -404,7 +404,55 @@ def flag_issue(inspection_id):
@supervisor_required @supervisor_required
def delete(inspection_id): def delete(inspection_id):
inspection = Inspection.query.get_or_404(inspection_id) inspection = Inspection.query.get_or_404(inspection_id)
# Capture identifiers for the log before deletion
insp_id = inspection.id
insp_date = inspection.inspection_date.strftime('%Y-%m-%d %H:%M')
facility_name = inspection.facility.name
template_name = inspection.template.name
inspector_name = inspection.inspector.username
# ── Clean up uploaded photos from disk ────────────────────────────────
# Collect photo paths from form data (inspection_photos) and issues
photo_paths = []
if inspection.notes:
try:
notes_data = json.loads(inspection.notes)
form_data = notes_data.get('_form_data', {}) if isinstance(notes_data, dict) else {}
for val in form_data.values():
if isinstance(val, str) and val.startswith('uploads/'):
photo_paths.append(val)
except (json.JSONDecodeError, TypeError):
pass
for issue in inspection.issues.all():
if issue.photo_path:
photo_paths.append(issue.photo_path)
# Delete the inspection record (cascade removes results and issues)
db.session.delete(inspection) db.session.delete(inspection)
db.session.commit() db.session.commit()
flash('Inspection deleted.', 'success')
# Remove photo files after successful DB commit
for rel_path in photo_paths:
abs_path = os.path.join(current_app.config['UPLOAD_FOLDER'], '..', 'static', rel_path)
abs_path = os.path.normpath(abs_path)
try:
if os.path.isfile(abs_path):
os.remove(abs_path)
except OSError:
pass # Non-fatal: log but don't block the response
current_app.logger.info(
'INSPECTION DELETED | id=%s | facility="%s" | template="%s" | '
'date=%s | inspector=%s | deleted_by=%s',
insp_id, facility_name, template_name,
insp_date, inspector_name, current_user.username
)
flash(
f'Inspection #{insp_id} ({template_name}{facility_name}, {insp_date}) '
f'has been permanently deleted.',
'success'
)
return redirect(url_for('inspections.index')) return redirect(url_for('inspections.index'))
+60 -1
View File
@@ -70,12 +70,23 @@
{{ ins.status|replace('_',' ')|title }} {{ ins.status|replace('_',' ')|title }}
</span> </span>
</td> </td>
<td> <td class="text-nowrap">
{% if ins.status == 'in_progress' or ins.status == 'flagged' %} {% if ins.status == 'in_progress' or ins.status == 'flagged' %}
<a href="{{ url_for('inspections.execute', inspection_id=ins.id) }}" class="btn btn-sm btn-outline-primary">Continue</a> <a href="{{ url_for('inspections.execute', inspection_id=ins.id) }}" class="btn btn-sm btn-outline-primary">Continue</a>
{% else %} {% else %}
<a href="{{ url_for('inspections.view', inspection_id=ins.id) }}" class="btn btn-sm btn-outline-secondary">View</a> <a href="{{ url_for('inspections.view', inspection_id=ins.id) }}" class="btn btn-sm btn-outline-secondary">View</a>
{% endif %} {% endif %}
{% if current_user.role in ['admin', 'supervisor'] %}
<button type="button"
class="btn btn-sm btn-outline-danger ms-1"
data-bs-toggle="modal"
data-bs-target="#deleteInspectionModal"
data-inspection-id="{{ ins.id }}"
data-inspection-label="{{ ins.template.name }} — {{ ins.facility.name }} ({{ ins.inspection_date.strftime('%Y-%m-%d') }})"
title="Delete inspection">
<i class="bi bi-trash3"></i>
</button>
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@@ -103,4 +114,52 @@
{% endif %} {% endif %}
</div> </div>
</div> </div>
{% if current_user.role in ['admin', 'supervisor'] %}
<!-- Delete Inspection Confirmation Modal -->
<div class="modal fade" id="deleteInspectionModal" tabindex="-1" aria-labelledby="deleteInspectionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteInspectionModalLabel">
<i class="bi bi-exclamation-triangle-fill"></i> Confirm Deletion
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>You are about to permanently delete the following inspection:</p>
<p class="fw-bold" id="deleteInspectionLabel"></p>
<p class="text-muted mb-0">This will also remove all associated results, flagged issues, and uploaded photos. This action is <strong>irreversible</strong>.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="bi bi-x-circle"></i> Cancel
</button>
<form id="deleteInspectionForm" method="POST" action="" class="d-inline">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-danger">
<i class="bi bi-trash3-fill"></i> Delete Permanently
</button>
</form>
</div>
</div>
</div>
</div>
{% endif %}
{% endblock %}
{% block extra_js %}
{% if current_user.role in ['admin', 'supervisor'] %}
<script>
document.addEventListener('DOMContentLoaded', function () {
const modal = document.getElementById('deleteInspectionModal');
modal.addEventListener('show.bs.modal', function (event) {
const btn = event.relatedTarget;
const id = btn.getAttribute('data-inspection-id');
const label = btn.getAttribute('data-inspection-label');
document.getElementById('deleteInspectionLabel').textContent = label;
document.getElementById('deleteInspectionForm').action = '/inspections/' + id + '/delete';
});
});
</script>
{% endif %}
{% endblock %} {% endblock %}