06/09 Add Inspecter filters, and allow customer to flag issue

This commit is contained in:
2026-06-09 13:26:09 -04:00
parent 711ecb8d27
commit 792e84f439
5 changed files with 145 additions and 42 deletions
+42
View File
@@ -232,6 +232,11 @@ def index():
facility_filter = request.args.get('facility_id', '')
follow_up_filter = request.args.get('follow_up', '')
contract_filter = request.args.get('contract_id', '')
date_from_filter = request.args.get('date_from', '')
date_to_filter = request.args.get('date_to', '')
score_min_filter = request.args.get('score_min', '')
score_max_filter = request.args.get('score_max', '')
inspector_filter = request.args.get('inspector_id', '')
if status_filter:
q = q.filter(Inspection.status == status_filter)
@@ -247,6 +252,29 @@ def index():
Inspection.follow_up_required == True,
Inspection.status == 'completed',
).filter(~Inspection.follow_ups.any())
if date_from_filter:
try:
q = q.filter(Inspection.inspection_date >= datetime.strptime(date_from_filter, '%Y-%m-%d'))
except ValueError:
date_from_filter = ''
if date_to_filter:
try:
_dt = datetime.strptime(date_to_filter, '%Y-%m-%d').replace(hour=23, minute=59, second=59)
q = q.filter(Inspection.inspection_date <= _dt)
except ValueError:
date_to_filter = ''
if score_min_filter:
try:
q = q.filter(Inspection.overall_score >= float(score_min_filter))
except ValueError:
score_min_filter = ''
if score_max_filter:
try:
q = q.filter(Inspection.overall_score <= float(score_max_filter))
except ValueError:
score_max_filter = ''
if inspector_filter.isdigit() and current_user.role != 'inspector':
q = q.filter(Inspection.inspector_id == int(inspector_filter))
inspections = q.paginate(page=page, per_page=20, error_out=False)
@@ -276,14 +304,28 @@ def index():
else:
projects = Project.query.filter_by(active=True).order_by(Project.name).all()
# Inspector dropdown — shown to all roles except inspector (they only see their own)
if current_user.role != 'inspector':
inspectors = (User.query
.filter(User.role == 'inspector', User.active == True)
.order_by(User.full_name, User.username).all())
else:
inspectors = []
return render_template('inspections/list.html',
inspections=inspections,
facilities=facilities,
projects=projects,
inspectors=inspectors,
status_filter=status_filter,
facility_filter=facility_filter,
follow_up_filter=follow_up_filter,
contract_filter=contract_filter,
date_from_filter=date_from_filter,
date_to_filter=date_to_filter,
score_min_filter=score_min_filter,
score_max_filter=score_max_filter,
inspector_filter=inspector_filter,
now=now_eastern())
+19 -2
View File
@@ -478,10 +478,27 @@ def unfollow(issue_id):
@bp.route('/new', methods=['GET', 'POST'])
@login_required
@supervisor_required
def create():
from app.models.project import Project
if current_user.role not in ('admin', 'director', 'customer'):
abort(403)
from app.models.project import Project, CustomerAssignment
form = IssueForm()
if current_user.role == 'customer':
cids = get_customer_scope(current_user) or []
facilities = (Facility.query
.filter(Facility.id.in_(cids), Facility.active == True)
.order_by(Facility.name).all()) if cids else []
assigned_pids = {
a.project_id for a in
CustomerAssignment.query.filter_by(user_id=current_user.id).all()
}
projects = Project.query.filter(
Project.active == True, Project.id.in_(assigned_pids)
).order_by(Project.name).all()
staff = []
else:
facilities = Facility.query.filter_by(active=True).order_by(Facility.name).all()
projects = Project.query.filter_by(active=True).order_by(Project.name).all()
staff = User.query.filter(User.role.in_(['admin', 'director', 'inspector'])).order_by(User.username).all()
+39 -2
View File
@@ -13,7 +13,8 @@
{# Filters #}
<div class="card shadow-sm mb-4">
<div class="card-body py-2">
<form method="get" class="row g-2 align-items-end">
<form method="get">
<div class="row g-2 align-items-end">
<div class="col-md-2">
<label class="form-label small mb-1">Status</label>
<select name="status" class="form-select form-select-sm">
@@ -41,10 +42,46 @@
{% endfor %}
</select>
</div>
{% if inspectors %}
<div class="col-md-2">
<label class="form-label small mb-1">Inspector</label>
<select name="inspector_id" class="form-select form-select-sm">
<option value="">All Inspectors</option>
{% for u in inspectors %}
<option value="{{ u.id }}" {% if inspector_filter == u.id|string %}selected{% endif %}>{{ u.display_name }}</option>
{% endfor %}
</select>
</div>
{% endif %}
</div>
<div class="row g-2 align-items-end mt-1">
<div class="col-md-2">
<label class="form-label small mb-1">Date From</label>
<input type="date" name="date_from" class="form-control form-control-sm"
value="{{ date_from_filter }}">
</div>
<div class="col-md-2">
<label class="form-label small mb-1">Date To</label>
<input type="date" name="date_to" class="form-control form-control-sm"
value="{{ date_to_filter }}">
</div>
<div class="col-md-1">
<label class="form-label small mb-1">Min Score</label>
<input type="number" name="score_min" class="form-control form-control-sm"
min="0" max="100" placeholder="0"
value="{{ score_min_filter }}">
</div>
<div class="col-md-1">
<label class="form-label small mb-1">Max Score</label>
<input type="number" name="score_max" class="form-control form-control-sm"
min="0" max="100" placeholder="100"
value="{{ score_max_filter }}">
</div>
<div class="col-auto">
<button type="submit" class="btn btn-sm btn-outline-primary">Filter</button>
<a href="{{ url_for('inspections.index') }}" class="btn btn-sm btn-outline-secondary">Clear</a>
</div>
</div>
</form>
</div>
</div>
@@ -125,7 +162,7 @@
{% for p in inspections.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if p %}
<li class="page-item {{ 'active' if p == inspections.page }}">
<a class="page-link" href="{{ url_for('inspections.index', page=p, status=status_filter, contract_id=contract_filter, facility_id=facility_filter) }}">{{ p }}</a>
<a class="page-link" href="{{ url_for('inspections.index', page=p, status=status_filter, contract_id=contract_filter, facility_id=facility_filter, date_from=date_from_filter, date_to=date_to_filter, score_min=score_min_filter, score_max=score_max_filter, inspector_id=inspector_filter) }}">{{ p }}</a>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link"></span></li>
+9 -2
View File
@@ -29,14 +29,21 @@
{% for e in form.facility_id.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
</div>
{# Remaining fields #}
{% for field in [form.severity, form.description, form.photo, form.assigned_to] %}
{# Remaining fields — assigned_to hidden from customer role #}
{% for field in [form.severity, form.description, form.photo] %}
<div class="mb-3">
{{ field.label(class="form-label fw-semibold") }}
{{ field(class="form-select" if field.type == 'SelectField' else "form-control", rows=4 if field.type == 'TextAreaField' else none) }}
{% for e in field.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
</div>
{% endfor %}
{% if current_user.role != 'customer' %}
<div class="mb-3">
{{ form.assigned_to.label(class="form-label fw-semibold") }}
{{ form.assigned_to(class="form-select") }}
{% for e in form.assigned_to.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
</div>
{% endif %}
<div class="d-flex gap-2">
<button type="submit" class="btn btn-danger">Log Issue</button>
+1 -1
View File
@@ -3,7 +3,7 @@
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h2><i class="bi bi-exclamation-triangle"></i> Issues</h2>
{% if current_user.role in ['admin','director'] %}
{% if current_user.role in ['admin','director','customer'] %}
<a href="{{ url_for('issues.create') }}" class="btn btn-danger">
<i class="bi bi-plus-circle"></i> Log Issue
</a>