Compare commits

..

4 Commits

6 changed files with 121 additions and 42 deletions

View File

@@ -66,3 +66,17 @@ def dashboard():
pie_chart=pie_chart(), pie_chart=pie_chart(),
histogram=histogram_chart() histogram=histogram_chart()
) )
# subcontractor dashboard
@dashboard_bp.route("/subcontractor_dashboard")
def subcontractor_dashboard():
if not session.get("user_id"):
return redirect(url_for("auth.login"))
return render_template(
"subcontractor_dashboard.html",
title="Dashboard",
bar_chart=bar_chart(),
pie_chart=pie_chart(),
histogram=histogram_chart()
)

View File

@@ -144,70 +144,87 @@ def report_file():
@file_report_bp.route("/client_report", methods=["GET", "POST"]) @file_report_bp.route("/client_report", methods=["GET", "POST"])
@login_required @login_required
def client_vs_all_subcontractor(): def client_vs_all_subcontractor():
tables = {"tr": None, "mh": None, "dc": None} # Initialize dictionary keys to match HTML variables
tables = {"tr": None, "mh": None, "dc": None, "laying": None}
ra_val = "" ra_val = ""
if request.method == "POST": if request.method == "POST":
RA_Bill_No = request.form.get("RA_Bill_No") RA_Bill_No = request.form.get("RA_Bill_No")
action = request.form.get("action") # Identify if 'download' or 'preview'
ra_val = RA_Bill_No ra_val = RA_Bill_No
if not RA_Bill_No: if not RA_Bill_No:
flash("Please enter RA Bill No.", "danger") flash("Please enter RA Bill No.", "danger")
return render_template("generate_comparison_client_vs_subcont.html", tables=tables, ra_val=ra_val) return render_template("client_report.html", tables=tables, ra_val=ra_val)
clientBill = ClientBill() clientBill = ClientBill()
clientBill.Fetch(RA_Bill_No=RA_Bill_No) clientBill.Fetch(RA_Bill_No=RA_Bill_No)
contractorBill = SubcontractorBill() contractorBill = SubcontractorBill()
contractorBill.Fetch(RA_Bill_No=RA_Bill_No) contractorBill.Fetch(RA_Bill_No=RA_Bill_No)
# --- SAFETY CHECK: Verify data exists before merging --- # Safety Check: Verify if data exists
if clientBill.df_tr.empty and clientBill.df_mh.empty: if clientBill.df_tr.empty and clientBill.df_mh.empty and clientBill.df_laying.empty:
flash(f"No Client records found for RA Bill {RA_Bill_No}", "warning") flash(f"No Client records found for RA Bill {RA_Bill_No}", "warning")
return render_template("generate_comparison_client_vs_subcont.html", tables=tables, ra_val=ra_val) return render_template("client_report.html", tables=tables, ra_val=ra_val)
qty_cols = [...] # (Keep your existing list) # Define columns based on your model field names
mh_dc_qty_cols = [...] # (Keep your existing list) qty_cols = [
mh_lay_qty_cols =[...] "pipe_150_mm", "pipe_200_mm", "pipe_250_mm", "pipe_300_mm",
"pipe_350_mm", "pipe_400_mm", "pipe_450_mm", "pipe_500_mm",
"pipe_600_mm", "pipe_700_mm", "pipe_900_mm", "pipe_1200_mm"
]
mh_dc_qty_cols = ["qty", "total"] # Update these based on your DC model fields
mh_lay_qty_cols = qty_cols + ["Laying_Length", "CC_length"]
def aggregate_df(df, group_cols, sum_cols): def aggregate_df(df, group_cols, sum_cols):
if df.empty: if df.empty:
# Create an empty DF with the correct columns to avoid Merge/Key Errors
return pd.DataFrame(columns=group_cols + sum_cols) return pd.DataFrame(columns=group_cols + sum_cols)
existing_cols = [c for c in sum_cols if c in df.columns] existing_cols = [c for c in sum_cols if c in df.columns]
# Ensure group_cols exist in the DF
for col in group_cols: for col in group_cols:
if col not in df.columns: if col not in df.columns:
df[col] = "N/A" # Fill missing join keys df[col] = "N/A"
return df.groupby(group_cols, as_index=False)[existing_cols].sum() return df.groupby(group_cols, as_index=False)[existing_cols].sum()
# Aggregate data # Aggregate data from Subcontractor tables
df_sub_tr_grp = aggregate_df(contractorBill.df_tr, ["Location", "MH_NO"], qty_cols) df_sub_tr_grp = aggregate_df(contractorBill.df_tr, ["Location", "MH_NO"], qty_cols)
df_sub_mh_grp = aggregate_df(contractorBill.df_mh, ["Location", "MH_NO"], qty_cols) df_sub_mh_grp = aggregate_df(contractorBill.df_mh, ["Location", "MH_NO"], qty_cols)
df_sub_dc_grp = aggregate_df(contractorBill.df_dc, ["Location", "MH_NO"], mh_dc_qty_cols) df_sub_dc_grp = aggregate_df(contractorBill.df_dc, ["Location", "MH_NO"], mh_dc_qty_cols)
df_sub_lay_grp = aggregate_df(contractorBill.df_dc, ["Location", "MH_NO"], mh_lay_qty_cols) # FIXED: Aggregating from .df_laying
df_sub_lay_grp = aggregate_df(contractorBill.df_laying, ["Location", "MH_NO"], mh_lay_qty_cols)
# --- FINAL MERGE LOGIC --- # Standardize "Location" column to prevent merge keys being missing
# We check if "Location" exists in the client data. If not, we add it to prevent the KeyError. for df_client in [clientBill.df_tr, clientBill.df_mh, clientBill.df_dc, clientBill.df_laying]:
for df_client in [clientBill.df_tr, clientBill.df_mh, clientBill.df_dc, clientBill.df_laying ]:
if not df_client.empty and "Location" not in df_client.columns: if not df_client.empty and "Location" not in df_client.columns:
df_client["Location"] = "Unknown" df_client["Location"] = "Unknown"
try: try:
# Final Merge for Comparison
df_tr_cmp = clientBill.df_tr.merge(df_sub_tr_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub")) df_tr_cmp = clientBill.df_tr.merge(df_sub_tr_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub"))
df_mh_cmp = clientBill.df_mh.merge(df_sub_mh_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub")) df_mh_cmp = clientBill.df_mh.merge(df_sub_mh_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub"))
df_dc_cmp = clientBill.df_dc.merge(df_sub_dc_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub")) df_dc_cmp = clientBill.df_dc.merge(df_sub_dc_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub"))
df_lay_cmp = clientBill.df_laying.merge(df_sub_lay_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub")) df_lay_cmp = clientBill.df_laying.merge(df_sub_lay_grp, on=["Location", "MH_NO"], how="left", suffixes=("_Client", "_Sub"))
# --- EXCEL DOWNLOAD LOGIC ---
if action == "download":
output = io.BytesIO()
with pd.ExcelWriter(output, engine="xlsxwriter") as writer:
df_tr_cmp.to_excel(writer, index=False, sheet_name="Tr.Ex ")
df_mh_cmp.to_excel(writer, index=False, sheet_name="Mh.Ex ")
df_dc_cmp.to_excel(writer, index=False, sheet_name="DC ")
df_lay_cmp.to_excel(writer, index=False, sheet_name="Laying and Bedding")
output.seek(0)
file_name = f"Client_Comparison_RA_{RA_Bill_No}.xlsx"
return send_file(output, download_name=file_name, as_attachment=True)
# --- PREVIEW HTML LOGIC ---
table_classes = 'table table-striped table-hover table-sm border shadow-sm'
tables["tr"] = df_tr_cmp.to_html(classes=table_classes, index=False)
tables["mh"] = df_mh_cmp.to_html(classes=table_classes, index=False)
tables["dc"] = df_dc_cmp.to_html(classes=table_classes, index=False)
tables["laying"] = df_lay_cmp.to_html(classes=table_classes, index=False)
except KeyError as e: except KeyError as e:
flash(f"Merge Error: Missing column {str(e)}. Check if 'Location' is defined in your database models.", "danger") flash(f"Merge Error: Missing column {str(e)}", "danger")
return render_template("client_report.html", tables=tables, ra_val=ra_val) return render_template("client_report.html", tables=tables, ra_val=ra_val)
# Convert to HTML for preview
tables["tr"] = df_tr_cmp.to_html(classes='table table-striped table-hover table-sm', index=False)
tables["mh"] = df_mh_cmp.to_html(classes='table table-striped table-hover table-sm', index=False)
tables["dc"] = df_dc_cmp.to_html(classes='table table-striped table-hover table-sm', index=False)
tables["laying"] = df_lay_cmp.to_html(classes='table table-striped table-hover table-sm', index=False)
return render_template("client_report.html", tables=tables, ra_val=ra_val) return render_template("client_report.html", tables=tables, ra_val=ra_val)

View File

@@ -4,11 +4,15 @@ import io
from app.models.subcontractor_model import Subcontractor from app.models.subcontractor_model import Subcontractor
from app.models.trench_excavation_model import TrenchExcavation 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.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.manhole_domestic_chamber_model import ManholeDomesticChamber
from app.models.laying_model import Laying
from app.models.tr_ex_client_model import TrenchExcavationClient
from app.models.mh_ex_client_model import ManholeExcavationClient
from app.models.mh_dc_client_model import ManholeDomesticChamberClient from app.models.mh_dc_client_model import ManholeDomesticChamberClient
from app.models.laying_client_model import LayingClient
from app.utils.helpers import login_required from app.utils.helpers import login_required
generate_report_bp = Blueprint("generate_report", __name__, url_prefix="/report") generate_report_bp = Blueprint("generate_report", __name__, url_prefix="/report")
@@ -191,6 +195,13 @@ def comparison_report():
).all()] ).all()]
df_dc = build_comparison(dc_client, dc_sub, "MH_NO") df_dc = build_comparison(dc_client, dc_sub, "MH_NO")
lay_client = [r.serialize() for r in LayingClient.query.all()]
lay_sub = [r.serialize() for r in Laying.query.filter_by(
subcontractor_id=subcontractor_id
).all()]
df_lay = build_comparison(lay_client, lay_sub, "MH_NO")
# -------- EXCEL -------- # -------- EXCEL --------
output = io.BytesIO() output = io.BytesIO()
filename = f"{subcontractor.subcontractor_name}_Comparison_Report.xlsx" filename = f"{subcontractor.subcontractor_name}_Comparison_Report.xlsx"
@@ -199,6 +210,7 @@ def comparison_report():
write_sheet(writer, df_tr, "Tr.Ex", subcontractor.subcontractor_name) write_sheet(writer, df_tr, "Tr.Ex", subcontractor.subcontractor_name)
write_sheet(writer, df_mh, "Mh.Ex", subcontractor.subcontractor_name) write_sheet(writer, df_mh, "Mh.Ex", subcontractor.subcontractor_name)
write_sheet(writer, df_dc, "MH & DC", subcontractor.subcontractor_name) write_sheet(writer, df_dc, "MH & DC", subcontractor.subcontractor_name)
write_sheet(writer, df_lay, "Laying", subcontractor.subcontractor_name)
output.seek(0) output.seek(0)
return send_file( return send_file(

View File

@@ -77,6 +77,13 @@
<i class="bi bi-download me-2"></i> Show Reports <i class="bi bi-download me-2"></i> Show Reports
</a> </a>
</li> </li>
<li>
<a class="dropdown-item" href="/dashboard/subcontractor_dashboard">
<i class="bi bi-speedometer2 me-2"></i> Subcontractor Dashboard
</a>
</li>
</ul> </ul>
</li> </li>

View File

@@ -40,19 +40,19 @@
<ul class="nav nav-tabs" id="reportTabs" role="tablist"> <ul class="nav nav-tabs" id="reportTabs" role="tablist">
<li class="nav-item"> <li class="nav-item">
<button class="nav-link active" id="tr-tab" data-bs-toggle="tab" data-bs-target="#tr" <button class="nav-link active" id="tr-tab" data-bs-toggle="tab" data-bs-target="#tr"
type="button">Tr.Ex Comparison</button> type="button">Tr.Ex </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" id="mh-tab" data-bs-toggle="tab" data-bs-target="#mh" type="button">Mh.Ex <button class="nav-link" id="mh-tab" data-bs-toggle="tab" data-bs-target="#mh" type="button">Mh.Ex
Comparison</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" id="dc-tab" data-bs-toggle="tab" data-bs-target="#dc" type="button">MH & DC <button class="nav-link" id="dc-tab" data-bs-toggle="tab" data-bs-target="#dc" type="button">MH & DC
Comparison</button> </button>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<button class="nav-link" id="laying-tab" data-bs-toggle="tab" data-bs-target="#laying" type="button">Laying <button class="nav-link" id="laying-tab" data-bs-toggle="tab" data-bs-target="#laying" type="button">Laying
& Bedding Comparison</button> & Bedding </button>
</ul> </ul>
<div class="tab-content mt-3" id="reportTabsContent"> <div class="tab-content mt-3" id="reportTabsContent">

View File

@@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
<div class="container-fluid px-2 px-md-4">
<h4 class="mb-3 text-center text-md-start">Subcontractor Dashboard </h4>
<!-- Charts -->
<div class="row g-3">
<!-- Bar Chart -->
<div class="col-12 col-md-6">
<div class="card shadow-sm h-100">
<div class="card-header bg-dark text-white text-center text-md-start">
Work Category Bar Chart
</div>
<div class="card-body text-center">
<img src="data:image/png;base64,{{ bar_chart }}" class="img-fluid" style="max-height:300px;">
</div>
</div>
</div>
</div>
</div>
{% endblock %}