From fe9b056128cb48d0d65b7aed5dcec6ee747b7095 Mon Sep 17 00:00:00 2001 From: pjpatil12 Date: Fri, 9 Jan 2026 15:22:32 +0530 Subject: [PATCH] changes of column import and add serialize table column fild show as per sheet --- app/models/manhole_domestic_chamber_model.py | 5 +- app/models/manhole_excavation_model.py | 3 + app/models/mh_dc_client_model.py | 4 + app/models/mh_ex_client_model.py | 3 + app/models/tr_ex_client_model.py | 3 + app/models/trench_excavation_model.py | 74 +++++ app/routes/file_report.py | 5 - app/routes/generate_comparison_report.py | 323 ++++++++++--------- app/services/file_service.py | 2 +- app/templates/file_import.html | 12 +- requirements.txt | 3 +- run.py | 2 +- 12 files changed, 269 insertions(+), 170 deletions(-) diff --git a/app/models/manhole_domestic_chamber_model.py b/app/models/manhole_domestic_chamber_model.py index a748744..98d4881 100644 --- a/app/models/manhole_domestic_chamber_model.py +++ b/app/models/manhole_domestic_chamber_model.py @@ -12,7 +12,7 @@ class ManholeDomesticChamber(db.Model): # Basic Fields Location = db.Column(db.String(500)) - Node_No = db.Column(db.String(100)) + MH_NO = db.Column(db.String(100)) Depth_of_MH = db.Column(db.Float) # Excavation categories @@ -46,3 +46,6 @@ class ManholeDomesticChamber(db.Model): def __repr__(self): return f"" + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/models/manhole_excavation_model.py b/app/models/manhole_excavation_model.py index 56e3a81..51e773c 100644 --- a/app/models/manhole_excavation_model.py +++ b/app/models/manhole_excavation_model.py @@ -62,3 +62,6 @@ class ManholeExcavation(db.Model): def __repr__(self): return f"" + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/models/mh_dc_client_model.py b/app/models/mh_dc_client_model.py index dd200b8..6ea30bc 100644 --- a/app/models/mh_dc_client_model.py +++ b/app/models/mh_dc_client_model.py @@ -40,3 +40,7 @@ class ManholeDomesticChamberClient(db.Model): def __repr__(self): return f"" + + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/models/mh_ex_client_model.py b/app/models/mh_ex_client_model.py index 603cacb..c4fd6c8 100644 --- a/app/models/mh_ex_client_model.py +++ b/app/models/mh_ex_client_model.py @@ -78,3 +78,6 @@ class ManholeExcavationClient(db.Model): def __repr__(self): return f"" + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/models/tr_ex_client_model.py b/app/models/tr_ex_client_model.py index 608e5dc..1214de0 100644 --- a/app/models/tr_ex_client_model.py +++ b/app/models/tr_ex_client_model.py @@ -83,3 +83,6 @@ class TrenchExcavationClient(db.Model): def __repr__(self): return f"" + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/models/trench_excavation_model.py b/app/models/trench_excavation_model.py index d31a679..17e34d7 100644 --- a/app/models/trench_excavation_model.py +++ b/app/models/trench_excavation_model.py @@ -74,3 +74,77 @@ class TrenchExcavation(db.Model): def __repr__(self): return f"" + + # def serialize(self): + # return { + # "id": self.id, + # "subcontractor_id": self.subcontractor_id, + # "subcontractor_name": self.subcontractor.subcontractor_name if self.subcontractor else None, + + # "Location": self.Location, + # "MH_NO": self.MH_NO, + # "CC_length": self.CC_length, + # "Invert_Level": self.Invert_Level, + # "MH_Top_Level": self.MH_Top_Level, + # "Ground_Level": self.Ground_Level, + # "ID_of_MH_m": self.ID_of_MH_m, + # "Actual_Trench_Length": self.Actual_Trench_Length, + # "Pipe_Dia_mm": self.Pipe_Dia_mm, + + # # Width + # "Width_0_to_2_5": self.Width_0_to_2_5, + # "Width_2_5_to_3_0": self.Width_2_5_to_3_0, + # "Width_3_0_to_4_5": self.Width_3_0_to_4_5, + # "Width_4_5_to_6_0": self.Width_4_5_to_6_0, + + # # Depth + # "Upto_IL_Depth": self.Upto_IL_Depth, + # "Cutting_Depth": self.Cutting_Depth, + # "Avg_Depth": self.Avg_Depth, + + # # Soft Murum + # "Soft_Murum_0_to_1_5": self.Soft_Murum_0_to_1_5, + # "Soft_Murum_1_5_to_3_0": self.Soft_Murum_1_5_to_3_0, + # "Soft_Murum_3_0_to_4_5": self.Soft_Murum_3_0_to_4_5, + + # # Hard Murum + # "Hard_Murum_0_to_1_5": self.Hard_Murum_0_to_1_5, + # "Hard_Murum_1_5_to_3_0": self.Hard_Murum_1_5_to_3_0, + + # # Soft Rock + # "Soft_Rock_0_to_1_5": self.Soft_Rock_0_to_1_5, + # "Soft_Rock_1_5_to_3_0": self.Soft_Rock_1_5_to_3_0, + + # # Hard Rock + # "Hard_Rock_0_to_1_5": self.Hard_Rock_0_to_1_5, + # "Hard_Rock_1_5_to_3_0": self.Hard_Rock_1_5_to_3_0, + # "Hard_Rock_3_0_to_4_5": self.Hard_Rock_3_0_to_4_5, + # "Hard_Rock_4_5_to_6_0": self.Hard_Rock_4_5_to_6_0, + # "Hard_Rock_6_0_to_7_5": self.Hard_Rock_6_0_to_7_5, + + # # Totals + # "Soft_Murum_0_to_1_5_total": self.Soft_Murum_0_to_1_5_total, + # "Soft_Murum_1_5_to_3_0_total": self.Soft_Murum_1_5_to_3_0_total, + # "Soft_Murum_3_0_to_4_5_total": self.Soft_Murum_3_0_to_4_5_total, + + # "Hard_Murum_0_to_1_5_total": self.Hard_Murum_0_to_1_5_total, + # "Hard_Murum_1_5_and_above_total": self.Hard_Murum_1_5_and_above_total, + + # "Soft_Rock_0_to_1_5_total": self.Soft_Rock_0_to_1_5_total, + # "Soft_Rock_1_5_and_above_total": self.Soft_Rock_1_5_and_above_total, + + # "Hard_Rock_0_to_1_5_total": self.Hard_Rock_0_to_1_5_total, + # "Hard_Rock_1_5_to_3_0_total": self.Hard_Rock_1_5_to_3_0_total, + # "Hard_Rock_3_0_to_4_5_total": self.Hard_Rock_3_0_to_4_5_total, + # "Hard_Rock_4_5_to_6_0_total": self.Hard_Rock_4_5_to_6_0_total, + # "Hard_Rock_6_0_to_7_5_total": self.Hard_Rock_6_0_to_7_5_total, + + # "Total": self.Total, + # "Remarks": self.Remarks, + # "RA_Bill_No": self.RA_Bill_No, + + # "created_at": self.created_at.strftime("%d-%m-%Y") if self.created_at else None + # } + + def serialize(self): + return {c.name: getattr(self, c.name) for c in self.__table__.columns} diff --git a/app/routes/file_report.py b/app/routes/file_report.py index dd5e565..0c9fb71 100644 --- a/app/routes/file_report.py +++ b/app/routes/file_report.py @@ -73,9 +73,7 @@ class SubcontractorBill: "Soft_Murum_0_to_1_5", "Soft_Murum_1_5_to_3_0", "Soft_Murum_3_0_to_4_5", "Hard_Murum_0_to_1_5", "Hard_Murum_1_5_to_3_0", - "Soft_Rock_0_to_1_5", "Soft_Rock_1_5_to_3_0", - "Hard_Rock_0_to_1_5", "Hard_Rock_1_5_to_3_0", "Hard_Rock_3_0_to_4_5", "Hard_Rock_4_5_to_6_0", "Hard_Rock_6_0_to_7_5", @@ -190,9 +188,6 @@ def client_vs_all_subcontractor(): contractorBill = SubcontractorBill() contractorBill.Fetch(RA_Bill_No=RA_Bill_No) - - - # GROUP SUBCONTRACTOR DATA qty_cols = [ "Soft_Murum_0_to_1_5_total", diff --git a/app/routes/generate_comparison_report.py b/app/routes/generate_comparison_report.py index f4f6205..8adfb8e 100644 --- a/app/routes/generate_comparison_report.py +++ b/app/routes/generate_comparison_report.py @@ -1,7 +1,7 @@ - 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 @@ -14,188 +14,211 @@ 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): -# HELPER FUNCTION + # 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.Location, getattr(r, key_field)): r + (r.get("Location"), r.get(key_field)): r for r in rows - if r.Location and getattr(r, key_field, None) + 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 a subcontractor.", "danger") - return render_template("generate_comparison_report.html",subcontractors=subcontractors) + flash("Please select subcontractor", "danger") + return render_template( + "generate_comparison_report.html", + subcontractors=subcontractors + ) subcontractor = Subcontractor.query.get_or_404(subcontractor_id) - # ================= TRENCH EXCAVATION ================= - client_tr_ex = TrenchExcavationClient.query.all() - contractor_tr_ex = TrenchExcavation.query.filter_by(subcontractor_id=subcontractor_id).all() + # ===================== 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") - contractor_lookup = make_lookup(contractor_tr_ex, "MH_NO") - combined_rows_tr_ex = [] + print("-- tr clint --:",tr_client ) + print("-- tr sub --:",tr_sub ) + + - for row1 in client_tr_ex: - row2 = contractor_lookup.get((row1.Location, row1.MH_NO)) - if not row2: - continue - # -------- CLIENT TOTAL ---------- - client_total = sum(v or 0 for k, v in row1.__dict__.items() - if k.endswith("_total")) + 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") - # -------- SUB TOTAL ---------- - contractor_total = sum(v or 0 for k, v in row2.__dict__.items() - if k.endswith("_total")) + # print("-- mh clint --:",mh_client) + # print("-- mh sub --:",mh_sub ) + - diff = client_total - contractor_total + 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") - row_data = { - "Location": row1.Location, - "MH No": row1.MH_NO, - } + # print("-- mh dc clint --:",dc_client ) + # print("-- mh dc sub --:", dc_sub) + - # -------- CLIENT COLUMNS ---------- - for col, val in row1.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Client_{col}"] = val - - row_data["Client_Total"] = round(client_total, 2) - row_data[" - "] = " " - - # -------- SUB COLUMNS ---------- - for col, val in row2.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Sub_{col}"] = val - - row_data["Sub_Total"] = round(contractor_total, 2) - row_data["--"] = " " - row_data["Diff"] = round(diff, 2) - - combined_rows_tr_ex.append(row_data) - - df_tr_ex = pd.DataFrame(combined_rows_tr_ex) - - # ================= MANHOLE EXCAVATION ================= - client_mh_ex = ManholeExcavationClient.query.all() - contractor_mh_ex = ManholeExcavation.query.filter_by(subcontractor_id=subcontractor_id).all() - - contractor_lookup = make_lookup(contractor_mh_ex, "MH_NO") - combined_rows_mh_ex = [] - - for row1 in client_mh_ex: - row2 = contractor_lookup.get((row1.Location, row1.MH_NO)) - if not row2: - continue - - client_total = sum(v or 0 for k, v in row1.__dict__.items() - if k.endswith("_total")) - contractor_total = sum(v or 0 for k, v in row2.__dict__.items() - if k.endswith("_total")) - - diff = client_total - contractor_total - - row_data = { - "Location": row1.Location, - "MH No": row1.MH_NO, - } - - for col, val in row1.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Client_{col}"] = val - - row_data["Client_Total"] = round(client_total, 2) - row_data[" - "] = " " - - for col, val in row2.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Sub_{col}"] = val - - row_data["Sub_Total"] = round(contractor_total, 2) - row_data["--"] = " " - row_data["Diff"] = round(diff, 2) - - combined_rows_mh_ex.append(row_data) - - df_mh_ex = pd.DataFrame(combined_rows_mh_ex) - - - # ============ MANHOLE DOMESTIC CHAMBER ================= - client_mh_dc = ManholeDomesticChamberClient.query.all() - contractor_mh_dc = ManholeDomesticChamber.query.filter_by(subcontractor_id=subcontractor_id).all() - - contractor_lookup = make_lookup(contractor_mh_dc, "Node_no") - combined_rows_mh_dc = [] - - for row1 in client_mh_dc: - row2 = contractor_lookup.get((row1.Location, row1.MH_NO)) - if not row2: - continue - - client_total = sum(v or 0 for k, v in row1.__dict__.items() - if isinstance(v, (int, float))) - contractor_total = sum(v or 0 for k, v in row2.__dict__.items() - if isinstance(v, (int, float))) - - diff = client_total - contractor_total - - row_data = { - "Location": row1.Location, - "Node No": row1.MH_NO, - } - - for col, val in row1.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Client_{col}"] = val - - row_data["Client_Total"] = round(client_total, 2) - row_data[" - "] = " " - - for col, val in row2.__dict__.items(): - if col.startswith("_") or col in ["id", "created_at", "subcontractor_id"]: - continue - row_data[f"Sub_{col}"] = val - - row_data["Sub_Total"] = round(contractor_total, 2) - row_data["--"] = " " - row_data["Diff"] = round(diff, 2) - - combined_rows_mh_dc.append(row_data) - - df_mh_dc = pd.DataFrame(combined_rows_mh_dc) - - - # ================== EXCEL EXPORT ====================== + # ===================== EXCEL ===================== output = io.BytesIO() - file_name = f"{subcontractor.subcontractor_name}_Comparison_Report.xlsx" + filename = f"{subcontractor.subcontractor_name}_Comparison_Report.xlsx" with pd.ExcelWriter(output, engine="xlsxwriter") as writer: - df_tr_ex.to_excel(writer, index=False, sheet_name="Tr.Ex") - df_mh_ex.to_excel(writer, index=False, sheet_name="Mh.Ex") - df_mh_dc.to_excel(writer, index=False, sheet_name="MH & DC") + 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=file_name, + download_name=filename, mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) - return render_template("generate_comparison_report.html",subcontractors=subcontractors) + return render_template( + "generate_comparison_report.html", + subcontractors=subcontractors + ) diff --git a/app/services/file_service.py b/app/services/file_service.py index f79a472..08da634 100644 --- a/app/services/file_service.py +++ b/app/services/file_service.py @@ -186,7 +186,7 @@ class FileService: try: for _, row in df.iterrows(): location = row.get("Location") - node_no = row.get("Node_No") + node_no = row.get("MH_NO") if ( pd.isna(location) diff --git a/app/templates/file_import.html b/app/templates/file_import.html index dd9d12f..9b5bb45 100644 --- a/app/templates/file_import.html +++ b/app/templates/file_import.html @@ -17,17 +17,7 @@ {% endfor %} - - - + diff --git a/requirements.txt b/requirements.txt index 50eb16d..cc2e645 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ openpyxl xlrd Werkzeug python-dotenv -cryptography \ No newline at end of file +cryptography +xlsxwriter \ No newline at end of file diff --git a/run.py b/run.py index ce951e0..0e710a9 100644 --- a/run.py +++ b/run.py @@ -6,4 +6,4 @@ with app.app_context(): db.create_all() if __name__ == "__main__": - app.run(debug=True, port=5001) + app.run(host='0.0.0.0' ,debug=True, port=5001)