06/12 Add export PDF function for Issues filter result
This commit is contained in:
+101
-2
@@ -2,7 +2,7 @@ import os
|
||||
import logging
|
||||
from app.utils.time_utils import now_eastern
|
||||
from flask import (Blueprint, render_template, redirect, url_for,
|
||||
flash, request, current_app, jsonify, abort)
|
||||
flash, request, current_app, jsonify, abort, Response)
|
||||
from flask_login import login_required, current_user
|
||||
from app import db
|
||||
from app.models.issue import Issue, IssueComment, IssueFollower
|
||||
@@ -16,7 +16,8 @@ from app.models.notification import (
|
||||
from app.utils.forms import IssueForm, IssueUpdateForm
|
||||
from app.utils.decorators import supervisor_required
|
||||
from app.utils.notifications import notify, notify_customers_for_facility, notify_by_matrix
|
||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE
|
||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE, ACTION_DELETE, ACTION_EXPORT
|
||||
from app.utils.pdf_export import generate_issues_list_pdf
|
||||
from app.utils.scope import get_customer_scope, get_inspector_scope
|
||||
from app.utils.sla import sla_status
|
||||
from sqlalchemy.orm import joinedload, contains_eager
|
||||
@@ -67,6 +68,104 @@ class _SLAFilteredPage:
|
||||
return iter([1])
|
||||
|
||||
|
||||
@bp.route('/export-list-pdf')
|
||||
@login_required
|
||||
def export_list_pdf():
|
||||
"""Generate and stream a PDF of the currently filtered issue list."""
|
||||
q = (
|
||||
Issue.query
|
||||
.outerjoin(Area, Issue.area_id == Area.id)
|
||||
.options(
|
||||
contains_eager(Issue.area),
|
||||
joinedload(Issue.facility),
|
||||
joinedload(Issue.assigned_user),
|
||||
)
|
||||
.order_by(Issue.reported_at.desc())
|
||||
)
|
||||
|
||||
if current_user.role == 'inspector':
|
||||
fids = get_inspector_scope(current_user)
|
||||
if not fids:
|
||||
q = q.filter(False)
|
||||
else:
|
||||
q = q.filter(db.or_(
|
||||
Issue.facility_id.in_(fids),
|
||||
db.and_(Issue.area_id.isnot(None), Area.facility_id.in_(fids)),
|
||||
))
|
||||
elif current_user.role == 'customer':
|
||||
customer_facility_ids = get_customer_scope(current_user)
|
||||
if not customer_facility_ids:
|
||||
q = q.filter(False)
|
||||
else:
|
||||
q = q.filter(db.or_(
|
||||
Issue.facility_id.in_(customer_facility_ids),
|
||||
db.and_(Issue.area_id.isnot(None), Area.facility_id.in_(customer_facility_ids)),
|
||||
))
|
||||
|
||||
issue_id_filter = request.args.get('issue_id', '').strip()
|
||||
severity_filter = request.args.get('severity', '')
|
||||
status_filter = request.args.get('status', '')
|
||||
sla_filter = request.args.get('sla', '')
|
||||
facility_filter = request.args.get('facility_id', '')
|
||||
contract_filter = request.args.get('contract_id', '')
|
||||
|
||||
if issue_id_filter.isdigit():
|
||||
q = q.filter(Issue.id == int(issue_id_filter))
|
||||
if severity_filter:
|
||||
q = q.filter(Issue.severity == severity_filter)
|
||||
if status_filter:
|
||||
q = q.filter(Issue.status == status_filter)
|
||||
if contract_filter.isdigit():
|
||||
_contract_fids = [
|
||||
f.id for f in Facility.query.filter_by(project_id=int(contract_filter)).all()
|
||||
]
|
||||
q = q.filter(db.or_(
|
||||
Issue.facility_id.in_(_contract_fids),
|
||||
Area.facility_id.in_(_contract_fids),
|
||||
)) if _contract_fids else q.filter(False)
|
||||
if facility_filter:
|
||||
fid = int(facility_filter)
|
||||
q = q.filter(db.or_(Issue.facility_id == fid, Area.facility_id == fid))
|
||||
|
||||
all_issues = q.all()
|
||||
if sla_filter:
|
||||
all_issues = [i for i in all_issues if sla_status(i) == sla_filter]
|
||||
|
||||
# Human-readable filter summary for the PDF header
|
||||
filter_parts = []
|
||||
if issue_id_filter:
|
||||
filter_parts.append(f'Issue #: {issue_id_filter}')
|
||||
if severity_filter:
|
||||
filter_parts.append(f'Severity: {severity_filter.title()}')
|
||||
if status_filter:
|
||||
filter_parts.append(f'Status: {status_filter.replace("_", " ").title()}')
|
||||
if sla_filter:
|
||||
filter_parts.append(f'SLA: {sla_filter.replace("_", " ").title()}')
|
||||
if contract_filter.isdigit():
|
||||
from app.models.project import Project
|
||||
p = db.session.get(Project, int(contract_filter))
|
||||
if p:
|
||||
filter_parts.append(f'Contract: {p.name}')
|
||||
if facility_filter:
|
||||
f = db.session.get(Facility, int(facility_filter))
|
||||
if f:
|
||||
filter_parts.append(f'Facility: {f.name}')
|
||||
|
||||
filter_summary = ' | '.join(filter_parts) if filter_parts else 'All issues'
|
||||
|
||||
pdf_bytes = generate_issues_list_pdf(all_issues, filter_summary)
|
||||
filename = f'issues_list_{now_eastern().strftime("%Y%m%d_%H%M")}.pdf'
|
||||
|
||||
log_action(ACTION_EXPORT, 'Issue', None, 'Issues List',
|
||||
f'format=pdf; filters={filter_summary}; count={len(all_issues)}')
|
||||
|
||||
return Response(
|
||||
pdf_bytes,
|
||||
mimetype='application/pdf',
|
||||
headers={'Content-Disposition': f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
|
||||
@bp.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
|
||||
Reference in New Issue
Block a user