Compare commits
7 Commits
8cba86323e
...
pankaj-dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b557efd80 | |||
| 71ce8ca819 | |||
| 68a694d2c7 | |||
| 90c18383da | |||
| ac9d964ef4 | |||
| f22dc0cccd | |||
| 0007d6e87d |
2
.env
2
.env
@@ -3,7 +3,7 @@
|
|||||||
# -----------------------------
|
# -----------------------------
|
||||||
FLASK_ENV=development
|
FLASK_ENV=development
|
||||||
FLASK_DEBUG=True
|
FLASK_DEBUG=True
|
||||||
FLASK_HOST='0.0.0.0'
|
FLASK_HOST=0.0.0.0
|
||||||
FLASK_PORT=5001
|
FLASK_PORT=5001
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
|
|||||||
@@ -5,16 +5,19 @@ class LayingClient(db.Model):
|
|||||||
__tablename__ = "laying_client"
|
__tablename__ = "laying_client"
|
||||||
|
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
# Foreign Key to Subcontractor tables
|
|
||||||
# subcontractor_id = db.Column(db.Integer, db.ForeignKey("subcontractors.id"), nullable=False)
|
|
||||||
# Relationship for easy access (subcontractor.subcontractor_name)
|
|
||||||
# subcontractor = db.relationship("Subcontractor", backref="laying_records")
|
|
||||||
|
|
||||||
# Basic Fields
|
# Basic Fields
|
||||||
Location = db.Column(db.String(500))
|
Location = db.Column(db.String(500))
|
||||||
MH_NO = db.Column(db.String(100))
|
MH_NO = db.Column(db.String(100))
|
||||||
CC_length = db.Column(db.Float)
|
CC_length = db.Column(db.Float)
|
||||||
|
# Bedding Qty.
|
||||||
|
Outer_dia_of_MH_m = db.Column(db.Float)
|
||||||
|
Bedding_Length = db.Column(db.Float)
|
||||||
|
Width = db.Column(db.Float)
|
||||||
|
Depth = db.Column(db.Float)
|
||||||
|
Qty = db.Column(db.Float)
|
||||||
|
|
||||||
|
# PIPE LAYING Qty.
|
||||||
Pipe_Dia_mm = db.Column(db.Float)
|
Pipe_Dia_mm = db.Column(db.Float)
|
||||||
ID_of_MH_m = db.Column(db.Float)
|
ID_of_MH_m = db.Column(db.Float)
|
||||||
Laying_Length = db.Column(db.Float)
|
Laying_Length = db.Column(db.Float)
|
||||||
@@ -32,7 +35,6 @@ class LayingClient(db.Model):
|
|||||||
pipe_900_mm = db.Column(db.Float)
|
pipe_900_mm = db.Column(db.Float)
|
||||||
pipe_1200_mm = db.Column(db.Float)
|
pipe_1200_mm = db.Column(db.Float)
|
||||||
|
|
||||||
|
|
||||||
Total = db.Column(db.Float)
|
Total = db.Column(db.Float)
|
||||||
Remarks = db.Column(db.String(500))
|
Remarks = db.Column(db.String(500))
|
||||||
RA_Bill_No=db.Column(db.String(500))
|
RA_Bill_No=db.Column(db.String(500))
|
||||||
@@ -45,3 +47,12 @@ class LayingClient(db.Model):
|
|||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
|
||||||
|
def sum_laying_fields():
|
||||||
|
return [
|
||||||
|
"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"
|
||||||
|
]
|
||||||
@@ -10,11 +10,10 @@ class Laying(db.Model):
|
|||||||
# Relationship for easy access (subcontractor.subcontractor_name)
|
# Relationship for easy access (subcontractor.subcontractor_name)
|
||||||
subcontractor = db.relationship("Subcontractor", backref="laying_records")
|
subcontractor = db.relationship("Subcontractor", backref="laying_records")
|
||||||
|
|
||||||
# Basic Fields
|
# Pipe Laying Fields
|
||||||
Location = db.Column(db.String(500))
|
Location = db.Column(db.String(500))
|
||||||
MH_NO = db.Column(db.String(100))
|
MH_NO = db.Column(db.String(100))
|
||||||
CC_length = db.Column(db.Float)
|
CC_length = db.Column(db.Float)
|
||||||
|
|
||||||
Pipe_Dia_mm = db.Column(db.Float)
|
Pipe_Dia_mm = db.Column(db.Float)
|
||||||
ID_of_MH_m = db.Column(db.Float)
|
ID_of_MH_m = db.Column(db.Float)
|
||||||
Laying_Length = db.Column(db.Float)
|
Laying_Length = db.Column(db.Float)
|
||||||
@@ -32,7 +31,6 @@ class Laying(db.Model):
|
|||||||
pipe_900_mm = db.Column(db.Float)
|
pipe_900_mm = db.Column(db.Float)
|
||||||
pipe_1200_mm = db.Column(db.Float)
|
pipe_1200_mm = db.Column(db.Float)
|
||||||
|
|
||||||
|
|
||||||
Total = db.Column(db.Float)
|
Total = db.Column(db.Float)
|
||||||
Remarks = db.Column(db.String(500))
|
Remarks = db.Column(db.String(500))
|
||||||
RA_Bill_No=db.Column(db.String(500))
|
RA_Bill_No=db.Column(db.String(500))
|
||||||
@@ -45,3 +43,12 @@ class Laying(db.Model):
|
|||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
|
||||||
|
def sum_laying_fields():
|
||||||
|
return [
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
|||||||
@@ -50,3 +50,14 @@ class ManholeDomesticChamber(db.Model):
|
|||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
|
||||||
|
def sum_mh_dc_fields():
|
||||||
|
return [
|
||||||
|
"d_0_to_0_75", "d_0_76_to_1_05", "d_1_06_to_1_65",
|
||||||
|
"d_1_66_to_2_15", "d_2_16_to_2_65", "d_2_66_to_3_15",
|
||||||
|
"d_3_16_to_3_65", "d_3_66_to_4_15", "d_4_16_to_4_65",
|
||||||
|
"d_4_66_to_5_15", "d_5_16_to_5_65", "d_5_66_to_6_15",
|
||||||
|
"d_6_16_to_6_65", "d_6_66_to_7_15", "d_7_16_to_7_65",
|
||||||
|
"d_7_66_to_8_15", "d_8_16_to_8_65", "d_8_66_to_9_15",
|
||||||
|
"d_9_16_to_9_65"]
|
||||||
|
|
||||||
|
|||||||
@@ -44,3 +44,14 @@ class ManholeDomesticChamberClient(db.Model):
|
|||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
|
||||||
|
def sum_mh_dc_fields():
|
||||||
|
return [
|
||||||
|
"d_0_to_0_75", "d_0_76_to_1_05", "d_1_06_to_1_65",
|
||||||
|
"d_1_66_to_2_15", "d_2_16_to_2_65", "d_2_66_to_3_15",
|
||||||
|
"d_3_16_to_3_65", "d_3_66_to_4_15", "d_4_16_to_4_65",
|
||||||
|
"d_4_66_to_5_15", "d_5_16_to_5_65", "d_5_66_to_6_15",
|
||||||
|
"d_6_16_to_6_65", "d_6_66_to_7_15", "d_7_16_to_7_65",
|
||||||
|
"d_7_66_to_8_15", "d_8_16_to_8_65", "d_8_66_to_9_15",
|
||||||
|
"d_9_16_to_9_65" ]
|
||||||
@@ -77,3 +77,34 @@ class TrenchExcavation(db.Model):
|
|||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||||
|
|
||||||
|
def excavation_category_sums(self):
|
||||||
|
|
||||||
|
def safe(val):
|
||||||
|
return val or 0
|
||||||
|
|
||||||
|
return {
|
||||||
|
"Soft_Murum_Total": (
|
||||||
|
safe(self.Soft_Murum_0_to_1_5)
|
||||||
|
+ safe(self.Soft_Murum_1_5_to_3_0)
|
||||||
|
+ safe(self.Soft_Murum_3_0_to_4_5)
|
||||||
|
),
|
||||||
|
|
||||||
|
"Hard_Murum_Total": (
|
||||||
|
safe(self.Hard_Murum_0_to_1_5)
|
||||||
|
+ safe(self.Hard_Murum_1_5_to_3_0)
|
||||||
|
),
|
||||||
|
|
||||||
|
"Soft_Rock_Total": (
|
||||||
|
safe(self.Soft_Rock_0_to_1_5)
|
||||||
|
+ safe(self.Soft_Rock_1_5_to_3_0)
|
||||||
|
),
|
||||||
|
|
||||||
|
"Hard_Rock_Total": (
|
||||||
|
safe(self.Hard_Rock_0_to_1_5)
|
||||||
|
+ safe(self.Hard_Rock_1_5_to_3_0)
|
||||||
|
+ safe(self.Hard_Rock_3_0_to_4_5)
|
||||||
|
+ safe(self.Hard_Rock_4_5_to_6_0)
|
||||||
|
+ safe(self.Hard_Rock_6_0_to_7_5)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,16 +5,20 @@ from flask import Blueprint, render_template, session, redirect, url_for
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import io
|
import io
|
||||||
import base64
|
import base64
|
||||||
|
from app.utils.plot_utils import plot_to_base64
|
||||||
|
from app.services.dashboard_service import DashboardService
|
||||||
|
|
||||||
dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/dashboard")
|
dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/dashboard")
|
||||||
|
|
||||||
|
# dashboard_bp = Blueprint("dashboard", __name__)
|
||||||
|
|
||||||
# charts
|
# charts
|
||||||
def plot_to_base64():
|
# def plot_to_base64():
|
||||||
img = io.BytesIO()
|
# img = io.BytesIO()
|
||||||
plt.savefig(img, format="png", bbox_inches="tight")
|
# plt.savefig(img, format="png", bbox_inches="tight")
|
||||||
plt.close()
|
# plt.close()
|
||||||
img.seek(0)
|
# img.seek(0)
|
||||||
return base64.b64encode(img.getvalue()).decode()
|
# return base64.b64encode(img.getvalue()).decode()
|
||||||
|
|
||||||
# bar chart
|
# bar chart
|
||||||
def bar_chart():
|
def bar_chart():
|
||||||
@@ -24,11 +28,11 @@ def bar_chart():
|
|||||||
plt.figure()
|
plt.figure()
|
||||||
plt.bar(categories, values)
|
plt.bar(categories, values)
|
||||||
plt.title("Work Category Report")
|
plt.title("Work Category Report")
|
||||||
plt.xlabel("Category")
|
plt.xlabel("test Category")
|
||||||
plt.ylabel("Count")
|
plt.ylabel("test Quantity")
|
||||||
|
|
||||||
|
|
||||||
return plot_to_base64()
|
return plot_to_base64(plt)
|
||||||
|
|
||||||
# Pie chart
|
# Pie chart
|
||||||
def pie_chart():
|
def pie_chart():
|
||||||
@@ -39,7 +43,7 @@ def pie_chart():
|
|||||||
plt.pie(sizes, labels=labels, autopct="%1.1f%%", startangle=140)
|
plt.pie(sizes, labels=labels, autopct="%1.1f%%", startangle=140)
|
||||||
plt.title("Project Status")
|
plt.title("Project Status")
|
||||||
|
|
||||||
return plot_to_base64()
|
return plot_to_base64(plt)
|
||||||
|
|
||||||
# Histogram chart
|
# Histogram chart
|
||||||
def histogram_chart():
|
def histogram_chart():
|
||||||
@@ -51,7 +55,7 @@ def histogram_chart():
|
|||||||
plt.xlabel("Work Units")
|
plt.xlabel("Work Units")
|
||||||
plt.ylabel("Frequency")
|
plt.ylabel("Frequency")
|
||||||
|
|
||||||
return plot_to_base64()
|
return plot_to_base64(plt)
|
||||||
|
|
||||||
# Dashboaed page
|
# Dashboaed page
|
||||||
@dashboard_bp.route("/")
|
@dashboard_bp.route("/")
|
||||||
@@ -66,3 +70,18 @@ def dashboard():
|
|||||||
pie_chart=pie_chart(),
|
pie_chart=pie_chart(),
|
||||||
histogram=histogram_chart()
|
histogram=histogram_chart()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# subcontractor dashboard
|
||||||
|
@dashboard_bp.route("/subcontractor_dashboard", methods=["GET", "POST"])
|
||||||
|
def subcontractor_dashboard():
|
||||||
|
if not session.get("user_id"):
|
||||||
|
return redirect(url_for("auth.login"))
|
||||||
|
|
||||||
|
tr_dash = DashboardService().bar_chart_of_tr_ex
|
||||||
|
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"subcontractor_dashboard.html",
|
||||||
|
title="Dashboard",
|
||||||
|
bar_chart=tr_dash
|
||||||
|
)
|
||||||
@@ -73,6 +73,7 @@ class SubcontractorBill:
|
|||||||
if not df.empty:
|
if not df.empty:
|
||||||
df.drop(columns=drop_cols, errors="ignore", inplace=True)
|
df.drop(columns=drop_cols, errors="ignore", inplace=True)
|
||||||
|
|
||||||
|
|
||||||
# --- subcontractor report only ---
|
# --- subcontractor report only ---
|
||||||
@file_report_bp.route("/Subcontractor_report", methods=["GET", "POST"])
|
@file_report_bp.route("/Subcontractor_report", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@@ -91,7 +92,7 @@ def report_file():
|
|||||||
|
|
||||||
if not subcontractor_id:
|
if not subcontractor_id:
|
||||||
flash("Please select a subcontractor.", "danger")
|
flash("Please select a subcontractor.", "danger")
|
||||||
return render_template("report.html", subcontractors=subcontractors)
|
return render_template("subcontractor_report.html", subcontractors=subcontractors)
|
||||||
|
|
||||||
subcontractor = Subcontractor.query.get(subcontractor_id)
|
subcontractor = Subcontractor.query.get(subcontractor_id)
|
||||||
bill_gen = SubcontractorBill()
|
bill_gen = SubcontractorBill()
|
||||||
@@ -102,13 +103,13 @@ def report_file():
|
|||||||
else:
|
else:
|
||||||
if not ra_bill_no:
|
if not ra_bill_no:
|
||||||
flash("Please enter an RA Bill Number.", "danger")
|
flash("Please enter an RA Bill Number.", "danger")
|
||||||
return render_template("report.html", subcontractors=subcontractors)
|
return render_template("subcontractor_report.html", subcontractors=subcontractors)
|
||||||
bill_gen.Fetch(RA_Bill_No=ra_bill_no, subcontractor_id=subcontractor_id)
|
bill_gen.Fetch(RA_Bill_No=ra_bill_no, subcontractor_id=subcontractor_id)
|
||||||
file_name = f"{subcontractor.subcontractor_name}_RA_{ra_bill_no}_Report.xlsx"
|
file_name = f"{subcontractor.subcontractor_name}_RA_{ra_bill_no}_Report.xlsx"
|
||||||
|
|
||||||
if bill_gen.df_tr.empty and bill_gen.df_mh.empty and bill_gen.df_dc.empty:
|
if bill_gen.df_tr.empty and bill_gen.df_mh.empty and bill_gen.df_dc.empty:
|
||||||
flash("No data found for this selection.", "warning")
|
flash("No data found for this selection.", "warning")
|
||||||
return render_template("report.html", subcontractors=subcontractors)
|
return render_template("subcontractor_report.html", subcontractors=subcontractors)
|
||||||
|
|
||||||
# If download is clicked, return file immediately
|
# If download is clicked, return file immediately
|
||||||
if action == "download":
|
if action == "download":
|
||||||
@@ -153,7 +154,7 @@ def client_vs_all_subcontractor():
|
|||||||
|
|
||||||
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)
|
||||||
@@ -163,7 +164,7 @@ def client_vs_all_subcontractor():
|
|||||||
# --- SAFETY CHECK: Verify data exists before merging ---
|
# --- SAFETY CHECK: Verify data exists before merging ---
|
||||||
if clientBill.df_tr.empty and clientBill.df_mh.empty:
|
if clientBill.df_tr.empty and clientBill.df_mh.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)
|
qty_cols = [...] # (Keep your existing list)
|
||||||
mh_dc_qty_cols = [...] # (Keep your existing list)
|
mh_dc_qty_cols = [...] # (Keep your existing list)
|
||||||
@@ -199,7 +200,7 @@ def client_vs_all_subcontractor():
|
|||||||
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"))
|
||||||
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)}. Check if 'Location' is defined in your database models.", "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)
|
||||||
|
|
||||||
|
|
||||||
# Convert to HTML for preview
|
# Convert to HTML for preview
|
||||||
|
|||||||
@@ -4,16 +4,28 @@ 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
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
generate_report_bp = Blueprint("generate_report", __name__, url_prefix="/report")
|
generate_report_bp = Blueprint("generate_report", __name__, url_prefix="/report")
|
||||||
|
|
||||||
|
|
||||||
|
# sum field of pipe laying (pipe_150_mm)
|
||||||
|
PIPE_MM_PATTERN = re.compile(r"^pipe_\d+_mm$")
|
||||||
|
# sum fields of MH dc (d_0_to_0_75)
|
||||||
|
D_RANGE_PATTERN = re.compile( r"^d_\d+(?:_\d+)?_to_\d+(?:_\d+)?$")
|
||||||
|
|
||||||
|
|
||||||
# NORMALIZER
|
# NORMALIZER
|
||||||
def normalize_key(value):
|
def normalize_key(value):
|
||||||
if value is None:
|
if value is None:
|
||||||
@@ -77,13 +89,13 @@ def build_comparison(client_rows, contractor_rows, key_field):
|
|||||||
client_total = sum(
|
client_total = sum(
|
||||||
float(v or 0)
|
float(v or 0)
|
||||||
for k, v in c.items()
|
for k, v in c.items()
|
||||||
if k.endswith("_total")
|
if k.endswith("_total") or D_RANGE_PATTERN.match(k) or PIPE_MM_PATTERN.match(k)
|
||||||
)
|
)
|
||||||
|
|
||||||
sub_total = sum(
|
sub_total = sum(
|
||||||
float(v or 0)
|
float(v or 0)
|
||||||
for k, v in s.items()
|
for k, v in s.items()
|
||||||
if k.endswith("_total")
|
if k.endswith("_total") or D_RANGE_PATTERN.match(k) or PIPE_MM_PATTERN.match(k)
|
||||||
)
|
)
|
||||||
|
|
||||||
diff = client_total - sub_total
|
diff = client_total - sub_total
|
||||||
@@ -125,10 +137,10 @@ def write_sheet(writer, df, sheet_name, subcontractor_name):
|
|||||||
ws = writer.sheets[sheet_name]
|
ws = writer.sheets[sheet_name]
|
||||||
|
|
||||||
title_fmt = workbook.add_format({"bold": True, "font_size": 14})
|
title_fmt = workbook.add_format({"bold": True, "font_size": 14})
|
||||||
client_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#D9EDF7"})
|
client_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#B6DAED"})
|
||||||
sub_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#F7E1D9"})
|
sub_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#F3A081"})
|
||||||
total_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#FFF2CC"})
|
total_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#F7D261"})
|
||||||
diff_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#E2EFDA"})
|
diff_fmt = workbook.add_format({"bold": True, "border": 1, "bg_color": "#82DD49"})
|
||||||
default_header_fmt = workbook.add_format({"bold": True,"border": 1,"bg_color": "#E7E6E6","align": "center","valign": "vcenter"})
|
default_header_fmt = workbook.add_format({"bold": True,"border": 1,"bg_color": "#E7E6E6","align": "center","valign": "vcenter"})
|
||||||
|
|
||||||
|
|
||||||
@@ -143,12 +155,13 @@ def write_sheet(writer, df, sheet_name, subcontractor_name):
|
|||||||
title_fmt
|
title_fmt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
for col_num, col_name in enumerate(df.columns):
|
for col_num, col_name in enumerate(df.columns):
|
||||||
if col_name.startswith("Client-"):
|
if col_name.startswith("Client-"):
|
||||||
ws.write(3, col_num, col_name, client_fmt)
|
ws.write(3, col_num, col_name, client_fmt)
|
||||||
elif col_name.startswith("Subcontractor-"):
|
elif col_name.startswith("Subcontractor-"):
|
||||||
ws.write(3, col_num, col_name, sub_fmt)
|
ws.write(3, col_num, col_name, sub_fmt)
|
||||||
elif col_name.endswith("Total"):
|
elif col_name.endswith("_total") or col_name.endswith("_total") :
|
||||||
ws.write(3, col_num, col_name, total_fmt)
|
ws.write(3, col_num, col_name, total_fmt)
|
||||||
elif col_name == "Diff":
|
elif col_name == "Diff":
|
||||||
ws.write(3, col_num, col_name, diff_fmt)
|
ws.write(3, col_num, col_name, diff_fmt)
|
||||||
@@ -190,6 +203,15 @@ def comparison_report():
|
|||||||
subcontractor_id=subcontractor_id
|
subcontractor_id=subcontractor_id
|
||||||
).all()]
|
).all()]
|
||||||
df_dc = build_comparison(dc_client, dc_sub, "MH_NO")
|
df_dc = build_comparison(dc_client, dc_sub, "MH_NO")
|
||||||
|
# df_dc = build_comparison_mh_dc(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")
|
||||||
|
# df_lay = build_comparison_laying(lay_client, lay_sub, "MH_NO")
|
||||||
|
|
||||||
|
|
||||||
# -------- EXCEL --------
|
# -------- EXCEL --------
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
@@ -199,6 +221,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(
|
||||||
@@ -211,3 +234,104 @@ def comparison_report():
|
|||||||
return render_template("generate_comparison_report.html",subcontractors=subcontractors)
|
return render_template("generate_comparison_report.html",subcontractors=subcontractors)
|
||||||
|
|
||||||
|
|
||||||
|
# def build_comparison_mh_dc(client_rows, contractor_rows, key_field):
|
||||||
|
# contractor_lookup = make_lookup(contractor_rows, key_field)
|
||||||
|
# mh_dc_fields = ManholeDomesticChamberClient.sum_mh_dc_fields()
|
||||||
|
|
||||||
|
# output = []
|
||||||
|
|
||||||
|
# for c in client_rows:
|
||||||
|
# loc = normalize_key(c.get("Location"))
|
||||||
|
# key = normalize_key(c.get(key_field))
|
||||||
|
# if not loc or not key:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# s = contractor_lookup.get((loc, key))
|
||||||
|
# if not s:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# client_total = sum(float(c.get(f, 0) or 0) for f in mh_dc_fields)
|
||||||
|
# sub_total = sum(float(s.get(f, 0) or 0) for f in mh_dc_fields)
|
||||||
|
|
||||||
|
# row = {
|
||||||
|
# "Location": loc,
|
||||||
|
# key_field.replace("_", " "): key
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # CLIENT – ALL FIELDS
|
||||||
|
# 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 – ALL FIELDS
|
||||||
|
# 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(client_total - sub_total, 2)
|
||||||
|
|
||||||
|
# output.append(row)
|
||||||
|
|
||||||
|
# df = pd.DataFrame(output)
|
||||||
|
# df.columns = [format_header(col) for col in df.columns]
|
||||||
|
# return df
|
||||||
|
|
||||||
|
|
||||||
|
# def build_comparison_laying(client_rows, contractor_rows, key_field):
|
||||||
|
# contractor_lookup = make_lookup(contractor_rows, key_field)
|
||||||
|
# laying_fields = Laying.sum_laying_fields()
|
||||||
|
|
||||||
|
# output = []
|
||||||
|
|
||||||
|
# for c in client_rows:
|
||||||
|
# loc = normalize_key(c.get("Location"))
|
||||||
|
# key = normalize_key(c.get(key_field))
|
||||||
|
# if not loc or not key:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# s = contractor_lookup.get((loc, key))
|
||||||
|
# if not s:
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# client_total = sum(float(c.get(f, 0) or 0) for f in laying_fields)
|
||||||
|
# sub_total = sum(float(s.get(f, 0) or 0) for f in laying_fields)
|
||||||
|
|
||||||
|
# print("--------------",key,"----------")
|
||||||
|
# print("sum -client_total ",client_total)
|
||||||
|
# print("sum -sub_total ",sub_total)
|
||||||
|
# print("Diff ---- ",client_total - sub_total)
|
||||||
|
# print("------------------------")
|
||||||
|
# row = {
|
||||||
|
# "Location": loc,
|
||||||
|
# key_field.replace("_", " "): key
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # CLIENT – ALL FIELDS
|
||||||
|
# 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 – ALL FIELDS
|
||||||
|
# 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(client_total - sub_total, 2)
|
||||||
|
|
||||||
|
# output.append(row)
|
||||||
|
|
||||||
|
# df = pd.DataFrame(output)
|
||||||
|
# df.columns = [format_header(col) for col in df.columns]
|
||||||
|
# return df
|
||||||
|
|||||||
49
app/services/dashboard_service.py
Normal file
49
app/services/dashboard_service.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from app.config import Config
|
||||||
|
from app import db
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
from app.routes.dashboard import plot_to_base64
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import io
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from app.utils.plot_utils import plot_to_base64
|
||||||
|
|
||||||
|
# Subcontractor models import
|
||||||
|
from app.models.trench_excavation_model import TrenchExcavation
|
||||||
|
|
||||||
|
# Client models import
|
||||||
|
# from app.models.tr_ex_client_model import TrenchExcavationClient
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardService:
|
||||||
|
|
||||||
|
|
||||||
|
# bar chart
|
||||||
|
def bar_chart_of_tr_ex():
|
||||||
|
categories = ["Soft Murum", "Hard Murum", "Soft Rock", "Hard Rock"]
|
||||||
|
values = [120, 80, 150, 60]
|
||||||
|
|
||||||
|
tr = TrenchExcavation()
|
||||||
|
|
||||||
|
record = TrenchExcavation.query.first()
|
||||||
|
print(" RA_Bill_No::",record["RA_Bill_No"])
|
||||||
|
|
||||||
|
totals = tr.excavation_category_sums()
|
||||||
|
|
||||||
|
# print(totals["Soft_Murum_Total"])
|
||||||
|
# print(totals["Hard_Rock_Total"])
|
||||||
|
|
||||||
|
|
||||||
|
plt.figure()
|
||||||
|
plt.bar(categories, values)
|
||||||
|
plt.title("Trench Excavation Work Category Report")
|
||||||
|
plt.xlabel("Excavation category")
|
||||||
|
plt.ylabel("Quantity")
|
||||||
|
|
||||||
|
return plot_to_base64(plt)
|
||||||
|
|
||||||
|
|
||||||
|
def subcontractor_dash():
|
||||||
|
return True
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -4,17 +4,6 @@
|
|||||||
<div class="container-fluid mt-4">
|
<div class="container-fluid mt-4">
|
||||||
<h2 class="mb-4">Client File Reports</h2>
|
<h2 class="mb-4">Client File Reports</h2>
|
||||||
|
|
||||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
||||||
{% if messages %}
|
|
||||||
{% for category, message in messages %}
|
|
||||||
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
|
|
||||||
{{ message }}
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
<div class="card p-4 shadow-sm mb-5">
|
<div class="card p-4 shadow-sm mb-5">
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<label class="form-label fw-bold">RA Bill No</label>
|
<label class="form-label fw-bold">RA Bill No</label>
|
||||||
@@ -33,7 +22,7 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if tables.tr or tables.mh or tables.dc %}
|
{% if tables.tr or tables.mh or tables.dc or tables.laying %}
|
||||||
<div class="card shadow-sm p-3">
|
<div class="card shadow-sm p-3">
|
||||||
<h4 class="mb-3">Comparison Preview</h4>
|
<h4 class="mb-3">Comparison Preview</h4>
|
||||||
|
|
||||||
@@ -50,6 +39,10 @@
|
|||||||
<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>
|
Comparison</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<button class="nav-link" id="laying-tab" data-bs-toggle="tab" data-bs-target="#laying"
|
||||||
|
type="button">Laying
|
||||||
|
& Bedding Comparison</button>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="tab-content mt-3" id="reportTabsContent">
|
<div class="tab-content mt-3" id="reportTabsContent">
|
||||||
@@ -68,9 +61,14 @@
|
|||||||
{{ tables.dc|safe }}
|
{{ tables.dc|safe }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane fade" id="laying" role="tabpanel">
|
||||||
|
<div class="table-responsive" style="max-height: 500px;">
|
||||||
|
{{ tables.laying|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Contact Person</label>
|
<label class="form-label">Contact Person Name</label>
|
||||||
<input type="text" class="form-control" name="contact_person">
|
<input type="text" class="form-control" name="contact_person">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
29
app/templates/subcontractor_dashboard.html
Normal file
29
app/templates/subcontractor_dashboard.html
Normal 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 %}
|
||||||
10
app/utils/plot_utils.py
Normal file
10
app/utils/plot_utils.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import base64
|
||||||
|
from io import BytesIO
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use("Agg")
|
||||||
|
|
||||||
|
def plot_to_base64(plt):
|
||||||
|
img = BytesIO()
|
||||||
|
plt.savefig(img, format="png", bbox_inches="tight")
|
||||||
|
img.seek(0)
|
||||||
|
return base64.b64encode(img.read()).decode("utf-8")
|
||||||
@@ -7,3 +7,5 @@ python-dotenv
|
|||||||
cryptography
|
cryptography
|
||||||
xlsxwriter
|
xlsxwriter
|
||||||
matplotlib
|
matplotlib
|
||||||
|
flask_sqlalchemy
|
||||||
|
flask_migrate
|
||||||
Reference in New Issue
Block a user