From 28196b4a03414bb9807d44da303add3dc3de7735 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Tue, 17 Mar 2026 16:39:15 -0400 Subject: [PATCH] March 17 2026: Fix bugs --- app/routes/audit.py | 3 +- app/routes/customers.py | 50 +++++++++++++++++++++++++++------ app/routes/scheduled_reports.py | 3 +- app/routes/templates.py | 4 +++ app/utils/sla.py | 4 +-- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/app/routes/audit.py b/app/routes/audit.py index 1156798..5cd7068 100644 --- a/app/routes/audit.py +++ b/app/routes/audit.py @@ -6,6 +6,7 @@ from app.models.audit import AuditLog from app.models.user import User from app.utils.decorators import admin_required from app.utils.audit import log_action, ACTION_DELETE +from app.utils.time_utils import now_eastern bp = Blueprint('audit', __name__, url_prefix='/audit') @@ -120,7 +121,7 @@ def purge(): flash('Invalid purge threshold selected.', 'danger') return redirect(url_for('audit.index')) - cutoff = datetime.utcnow() - timedelta(days=older_than) + cutoff = now_eastern() - timedelta(days=older_than) deleted = AuditLog.query.filter(AuditLog.created_at < cutoff).delete() db.session.flush() diff --git a/app/routes/customers.py b/app/routes/customers.py index 53d0664..1b3dc67 100644 --- a/app/routes/customers.py +++ b/app/routes/customers.py @@ -43,14 +43,47 @@ def index(): .all() ) - # Pre-compute assignment summary per customer to avoid N+1 in template - assignment_map = {} # user_id → list[CustomerAssignment] - scope_map = {} # user_id → list[int] facility IDs + customer_ids = [c.id for c in customers] + # ── Single bulk query for all assignments ───────────────────────────── + # Replaces per-customer CustomerAssignment.query.filter_by(user_id=...) loop + all_assignments = ( + CustomerAssignment.query + .filter(CustomerAssignment.user_id.in_(customer_ids)) + .all() + ) if customer_ids else [] + + assignment_map = {c.id: [] for c in customers} + for a in all_assignments: + assignment_map[a.user_id].append(a) + + # ── Single bulk query for all active facilities in assigned projects ── + # Resolves facility scope for every customer without repeated DB round-trips. + from collections import defaultdict + assigned_project_ids = {a.project_id for a in all_assignments} + + project_facilities_map = defaultdict(list) # project_id → [facility_id, ...] + if assigned_project_ids: + proj_facs = ( + Facility.query + .filter( + Facility.project_id.in_(assigned_project_ids), + Facility.active == True, + ) + .all() + ) + for f in proj_facs: + project_facilities_map[f.project_id].append(f.id) + + scope_map = {} # user_id → sorted list[int] facility IDs for customer in customers: - assignments = CustomerAssignment.query.filter_by(user_id=customer.id).all() - assignment_map[customer.id] = assignments - scope_map[customer.id] = get_customer_scope(customer) or [] + ids = set() + for a in assignment_map[customer.id]: + if a.facility_id: + ids.add(a.facility_id) + else: + ids.update(project_facilities_map.get(a.project_id, [])) + scope_map[customer.id] = sorted(ids) # All active projects for the assignment modal projects = Project.query.filter_by(active=True).order_by(Project.name).all() @@ -386,8 +419,9 @@ def bulk_import(): facility_id = fac_id or None, ) db.session.add(assign) + db.session.flush() # populate assign.id before audit log created_assign += 1 - log_action(ACTION_CREATE, 'CustomerAssignment', 0, + log_action(ACTION_CREATE, 'CustomerAssignment', assign.id, f'{uname} → project_id={proj_id}', f'facility_id={fac_id}; source=bulk_import') else: @@ -548,4 +582,4 @@ def facilities_for_project(project_id): from flask import jsonify project = Project.query.get_or_404(project_id) facilities = project.facilities.filter_by(active=True).order_by(Facility.name).all() - return jsonify([{'id': f.id, 'name': f.name} for f in facilities]) + return jsonify([{'id': f.id, 'name': f.name} for f in facilities]) \ No newline at end of file diff --git a/app/routes/scheduled_reports.py b/app/routes/scheduled_reports.py index 2203d9c..9ef538a 100644 --- a/app/routes/scheduled_reports.py +++ b/app/routes/scheduled_reports.py @@ -55,8 +55,7 @@ def _compute_next_send(frequency: str, from_dt: datetime = None) -> datetime: if frequency == 'daily': return (now + timedelta(days=1)).replace(hour=7, minute=0, second=0, microsecond=0) if frequency == 'weekly': - days_ahead = 7 - now.weekday() # next Monday - return (now + timedelta(days=days_ahead)).replace(hour=7, minute=0, second=0, microsecond=0) + return (now + timedelta(weeks=1)).replace(hour=7, minute=0, second=0, microsecond=0) # monthly: first of next month if now.month == 12: return now.replace(year=now.year + 1, month=1, day=1, hour=7, minute=0, second=0, microsecond=0) diff --git a/app/routes/templates.py b/app/routes/templates.py index 1c079da..aabcd23 100644 --- a/app/routes/templates.py +++ b/app/routes/templates.py @@ -108,6 +108,8 @@ def rename_template(template_id): template.frequency = new_frequency db.session.commit() + log_action(ACTION_UPDATE, 'Template', template.id, template.name, + f'frequency={template.frequency}; via=rename') flash(f'Template "{template.name}" updated successfully.', 'success') return redirect(url_for('templates.index')) @@ -258,6 +260,8 @@ def save_form_schema(template_id): template.form_schema = sanitised db.session.commit() + log_action(ACTION_UPDATE, 'Template', template.id, template.name, + f'form_schema saved; field_count={len(sanitised)}') return jsonify({'success': True, 'field_count': len(sanitised)}) diff --git a/app/utils/sla.py b/app/utils/sla.py index 89a668a..9cc79b2 100644 --- a/app/utils/sla.py +++ b/app/utils/sla.py @@ -112,7 +112,7 @@ def send_sla_alerts(): logger = logging.getLogger(__name__) open_issues = Issue.query.filter( - Issue.status.in_(['open', 'in_progress']) + Issue.status.in_(['open', 'in_progress', 'pending_verification']) ).all() admins = User.query.filter_by(role='admin').all() @@ -200,4 +200,4 @@ def send_sla_alerts(): if total_sent: db.session.commit() - return total_sent + return total_sent \ No newline at end of file