from flask import Blueprint, render_template, request, send_file, flash import pandas as pd import io import re from app.models.subcontractor_model import Subcontractor from app.models.trench_excavation_model import TrenchExcavation from app.models.tr_ex_client_model import TrenchExcavationClient from app.models.manhole_excavation_model import ManholeExcavation from app.models.mh_ex_client_model import ManholeExcavationClient from app.models.manhole_domestic_chamber_model import ManholeDomesticChamber from app.models.mh_dc_client_model import ManholeDomesticChamberClient generate_report_bp = Blueprint("generate_report", __name__, url_prefix="/report") # HEADER FORMATTER def format_header(header): # Handle Client- / Subcontractor- if "-" in header: prefix, rest = header.split("-", 1) prefix = prefix.title() else: prefix, rest = None, header parts = rest.split("_") result = [] i = 0 while i < len(parts): # Detect num_num → num.num if i + 1 < len(parts) and parts[i].isdigit() and parts[i + 1].isdigit(): result.append(f"{parts[i]}.{parts[i + 1]}") i += 2 else: result.append(parts[i].title()) i += 1 final_text = " ".join(result) return f"{prefix}-{final_text}" if prefix else final_text # LOOKUP CREATOR def make_lookup(rows, key_field): return { (r.get("Location"), r.get(key_field)): r for r in rows if r.get("Location") and r.get(key_field) } # GENERIC COMPARISON BUILDER def build_comparison(client_rows, contractor_rows, key_field): contractor_lookup = make_lookup(contractor_rows, key_field) output = [] for c in client_rows: s = contractor_lookup.get((c.get("Location"), c.get(key_field))) if not s: continue client_total = sum(v or 0 for k, v in c.items() if k.endswith("_total")) sub_total = sum(v or 0 for k, v in s.items() if k.endswith("_total")) diff = client_total - sub_total row = { "Location": c.get("Location"), key_field.replace("_", " "): c.get(key_field) } # CLIENT DATA for k, v in c.items(): if k in ["id", "created_at"]: continue row[f"Client-{k}"] = v row["Client-Total"] = round(client_total, 2) row[" "] = "" # SUBCONTRACTOR DATA for k, v in s.items(): if k in ["id", "created_at", "subcontractor_id"]: continue row[f"Subcontractor-{k}"] = v row["Subcontractor-Total"] = round(sub_total, 2) row["Diff"] = round(diff, 2) output.append(row) df = pd.DataFrame(output) df.columns = [format_header(col) for col in df.columns] return df # EXCEL SHEET WRITER (SEPARATED) def write_sheet(writer, df, sheet_name, subcontractor_name): workbook = writer.book df.to_excel(writer, sheet_name=sheet_name, index=False, startrow=3) ws = writer.sheets[sheet_name] title_fmt = workbook.add_format({ "bold": True, "font_size": 14 }) client_fmt = workbook.add_format({ "bold": True, "border": 1, "bg_color": "#D9EDF7" }) sub_fmt = workbook.add_format({ "bold": True, "border": 1, "bg_color": "#F7E1D9" }) total_fmt = workbook.add_format({ "bold": True, "border": 1, "bg_color": "#FFF2CC" }) diff_fmt = workbook.add_format({ "bold": True, "border": 1, "bg_color": "#E2EFDA" }) # MAIN HEADING ws.merge_range( 0, 0, 0, len(df.columns) - 1, f"CLIENT vs SUBCONTRACTOR – {subcontractor_name}", title_fmt ) # COLUMN HEADERS for col_num, col_name in enumerate(df.columns): if col_name.startswith("Client-"): ws.write(3, col_num, col_name, client_fmt) elif col_name.startswith("Subcontractor-"): ws.write(3, col_num, col_name, sub_fmt) elif col_name.endswith("Total"): ws.write(3, col_num, col_name, total_fmt) elif col_name == "Diff": ws.write(3, col_num, col_name, diff_fmt) else: ws.write(3, col_num, col_name) ws.set_column(col_num, col_num, 20) # old report generate ROUTE @generate_report_bp.route("/comparison_report", methods=["GET", "POST"]) def comparison_report(): subcontractors = Subcontractor.query.all() if request.method == "POST": subcontractor_id = request.form.get("subcontractor_id") if not subcontractor_id: flash("Please select subcontractor", "danger") return render_template( "generate_comparison_report.html", subcontractors=subcontractors ) subcontractor = Subcontractor.query.get_or_404(subcontractor_id) # ===================== DATA ===================== tr_client = [r.serialize() for r in TrenchExcavationClient.query.all()] tr_sub = [r.serialize() for r in TrenchExcavation.query.filter_by( subcontractor_id=subcontractor_id ).all()] df_tr = build_comparison(tr_client, tr_sub, "MH_NO") print("-- tr clint --:",tr_client ) print("-- tr sub --:",tr_sub ) mh_client = [r.serialize() for r in ManholeExcavationClient.query.all()] mh_sub = [r.serialize() for r in ManholeExcavation.query.filter_by( subcontractor_id=subcontractor_id ).all()] df_mh = build_comparison(mh_client, mh_sub, "MH_NO") # print("-- mh clint --:",mh_client) # print("-- mh sub --:",mh_sub ) dc_client = [r.serialize() for r in ManholeDomesticChamberClient.query.all()] dc_sub = [r.serialize() for r in ManholeDomesticChamber.query.filter_by( subcontractor_id=subcontractor_id ).all()] df_dc = build_comparison(dc_client, dc_sub, "MH_NO") # print("-- mh dc clint --:",dc_client ) # print("-- mh dc sub --:", dc_sub) # ===================== EXCEL ===================== output = io.BytesIO() filename = f"{subcontractor.subcontractor_name}_Comparison_Report.xlsx" with pd.ExcelWriter(output, engine="xlsxwriter") as writer: write_sheet(writer, df_tr, "Tr.Ex", subcontractor.subcontractor_name) write_sheet(writer, df_mh, "Mh.Ex", subcontractor.subcontractor_name) write_sheet(writer, df_dc, "MH & DC", subcontractor.subcontractor_name) output.seek(0) return send_file( output, as_attachment=True, download_name=filename, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) return render_template( "generate_comparison_report.html", subcontractors=subcontractors )