Files
Comparison_Project/app/routes/dashboard.py

614 lines
19 KiB
Python

import matplotlib
matplotlib.use("Agg")
from flask import Blueprint, render_template, session, redirect, url_for, jsonify, request
import matplotlib.pyplot as plt
import io
import base64
from app.utils.plot_utils import plot_to_base64
from app.utils.helpers import login_required
from app.services.dashboard_service import DashboardService
from sqlalchemy import func
from app import db
from sqlalchemy import tuple_
# Subcontractor models import
from app.models.subcontractor_model import Subcontractor
from app.models.trench_excavation_model import TrenchExcavation
from app.models.manhole_excavation_model import ManholeExcavation
from app.models.manhole_domestic_chamber_model import ManholeDomesticChamber
from app.models.laying_model import Laying
# client models import
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.laying_client_model import LayingClient
dashboard_bp = Blueprint("dashboard", __name__, url_prefix="/dashboard")
@dashboard_bp.route("/")
def dashboard():
if not session.get("user_id"):
return redirect(url_for("auth.login"))
return render_template("dashboard.html", title="Business Intelligence Dashboard")
@dashboard_bp.route("/api/live-stats")
@login_required
def live_stats():
try:
# 1. Overall Volume
t_count = TrenchExcavation.query.count()
m_count = ManholeExcavation.query.count()
l_count = Laying.query.count()
# 2. Location Distribution (Business reach)
loc_results = db.session.query(
TrenchExcavation.Location,
func.count(TrenchExcavation.id)
).group_by(TrenchExcavation.Location).all()
# 3. Work Timeline (Business productivity trend)
# Assuming your models have a 'created_at' field
timeline_results = db.session.query(
func.date(TrenchExcavation.created_at),
func.count(TrenchExcavation.id)
).group_by(func.date(TrenchExcavation.created_at)).order_by(func.date(TrenchExcavation.created_at)).all()
return jsonify({
"summary": {
"trench": t_count,
"manhole": m_count,
"laying": l_count,
"total": t_count + m_count + l_count
},
"locations": {row[0]: row[1] for row in loc_results if row[0]},
"timeline": {str(row[0]): row[1] for row in timeline_results}
})
except Exception as e:
return jsonify({"error": str(e)}), 500
# subcontractor dashboard
@dashboard_bp.route("/subcontractor_dashboard")
@login_required
def subcontractor_dashboard():
if not session.get("user_id"):
return redirect(url_for("auth.login"))
subcontractors = Subcontractor.query.all()
return render_template(
"subcontractor_dashboard.html",
subcontractors=subcontractors
)
# API: Get Unique RA Bills
@dashboard_bp.route("/api/get-ra-bills")
@login_required
def get_ra_bills():
subcontractor_id = request.args.get("subcontractor")
category = request.args.get("category")
if not subcontractor_id or not category:
return {"ra_bills": []}
match category:
case "trench_excavation":
results = db.session.query(
TrenchExcavation.RA_Bill_No
).filter(
TrenchExcavation.subcontractor_id == subcontractor_id
).distinct().order_by(TrenchExcavation.RA_Bill_No).all()
# (Add others same pattern later)
case "manhole_excavation":
results = db.session.query(
ManholeExcavation.RA_Bill_No
).filter(
ManholeExcavation.subcontractor_id == subcontractor_id
).distinct().order_by(ManholeExcavation.RA_Bill_No).all()
case "Manhole_Domestic_Chamber":
results = db.session.query(
ManholeDomesticChamber.RA_Bill_No
).filter(
ManholeDomesticChamber.subcontractor_id == subcontractor_id
).distinct().order_by(ManholeDomesticChamber.RA_Bill_No).all()
case "Laying":
results = db.session.query(
Laying.RA_Bill_No
).filter(
Laying.subcontractor_id == subcontractor_id
).distinct().order_by(Laying.RA_Bill_No).all()
ra_bills = [r[0] for r in results if r[0]]
return {"ra_bills": ra_bills}
def total(records, field):
return float(sum(getattr(r, field) or 0 for r in records))
# category= trench_excavation
@dashboard_bp.route("/api/tr-analysis")
def trench_analysis():
subcontractor_id = request.args.get("subcontractor", "").strip()
ra_bill = request.args.get("ra_bill", "").strip()
# Convert "1,2,3" -> ["1", "2", "3"]
ra_bill_list = []
if ra_bill:
ra_bill_list = [x.strip() for x in ra_bill.split(",") if x.strip()]
# Subcontractor Query
sub_query = TrenchExcavation.query
if subcontractor_id:
sub_query = sub_query.filter(
TrenchExcavation.subcontractor_id == int(subcontractor_id)
)
if ra_bill_list:
sub_query = sub_query.filter(
TrenchExcavation.RA_Bill_No.in_(ra_bill_list)
)
sub_records = sub_query.all()
sub_keys = [
(
(r.MH_NO or "").strip().upper(),
(r.Location or "").strip().upper()
)
for r in sub_records
]
client_query = TrenchExcavationClient.query
if sub_keys:
client_query = client_query.filter(
tuple_(
func.upper(func.trim(TrenchExcavationClient.MH_NO)),
func.upper(func.trim(TrenchExcavationClient.Location))
).in_(sub_keys)
)
client_records = client_query.all()
chart_data = [
{
"label": "Marshi 0 to 1.5",
"client": total(client_records, "Marshi_Muddy_Slushy_0_to_1_5_total"),
"sub": 0
},
{
"label": "Marshi 1.5 to 3.0",
"client": total(client_records, "Marshi_Muddy_Slushy_1_5_to_3_0_total"),
"sub": 0
},
{
"label": "Marshi 3.0 to 4.5",
"client": total(client_records, "Marshi_Muddy_Slushy_3_0_to_4_5_total"),
"sub": 0
},
{
"label": "Soft Murum 0 to 1.5",
"client": total(client_records, "Soft_Murum_0_to_1_5_total"),
"sub": total(sub_records, "Soft_Murum_0_to_1_5_total")
},
{
"label": "Soft Murum 1.5 to 3.0",
"client": total(client_records, "Soft_Murum_1_5_to_3_0_total"),
"sub": total(sub_records, "Soft_Murum_1_5_to_3_0_total")
},
{
"label": "Soft Murum 3.0 to 4.5",
"client": total(client_records, "Soft_Murum_3_0_to_4_5_total"),
"sub": total(sub_records, "Soft_Murum_3_0_to_4_5_total")
},
{
"label": "Hard Murum 0 to 1.5",
"client": total(client_records, "Hard_Murum_0_to_1_5_total"),
"sub": total(sub_records, "Hard_Murum_0_to_1_5_total")
},
{
"label": "Hard Murum 1.5 to 3.0",
"client": total(client_records, "Hard_Murum_1_5_to_3_0_total"),
"sub": total(sub_records, "Hard_Murum_1_5_and_above_total")
},
{
"label": "Soft Rock 0 to 1.5",
"client": total(client_records, "Soft_Rock_0_to_1_5_total"),
"sub": total(sub_records, "Soft_Rock_0_to_1_5_total")
},
{
"label": "Soft Rock 1.5 to 3.0",
"client": total(client_records, "Soft_Rock_1_5_to_3_0_total"),
"sub": total(sub_records, "Soft_Rock_1_5_and_above_total")
},
{
"label": "Hard Rock 0 to 1.5",
"client": total(client_records, "Hard_Rock_0_to_1_5_total"),
"sub": total(sub_records, "Hard_Rock_0_to_1_5_total")
},
{
"label": "Hard Rock 1.5 to 3.0",
"client": total(client_records, "Hard_Rock_1_5_to_3_0_total"),
"sub": total(sub_records, "Hard_Rock_1_5_to_3_0_total")
},
{
"label": "Hard Rock 3.0 to 4.5",
"client": total(client_records, "Hard_Rock_3_0_to_4_5_total"),
"sub": total(sub_records, "Hard_Rock_3_0_to_4_5_total")
},
{
"label": "Hard Rock 4.5 to 6.0",
"client": total(client_records, "Hard_Rock_4_5_to_6_0_total"),
"sub": total(sub_records, "Hard_Rock_4_5_to_6_0_total")
},
{
"label": "Hard Rock 6.0 to 7.5",
"client": total(client_records, "Hard_Rock_6_0_to_7_5_total"),
"sub": total(sub_records, "Hard_Rock_6_0_to_7_5_total")
}
]
return jsonify({
"title": "Trench Excavation Comparison",
"y_title": "Excavation Qty (Cum)",
"labels": [x["label"] for x in chart_data],
"client_qty": [x["client"] for x in chart_data],
"sub_qty": [x["sub"] for x in chart_data]
})
# category = manhole_excavation
@dashboard_bp.route("/api/mh-analysis")
def manhole_analysis():
subcontractor_id = request.args.get("subcontractor", "").strip()
ra_bill = request.args.get("ra_bill", "").strip()
# Convert "1,2,3" -> ["1", "2", "3"]
ra_bill_list = []
if ra_bill:
ra_bill_list = [x.strip() for x in ra_bill.split(",") if x.strip()]
# Subcontractor Query
sub_query = ManholeExcavation.query
if subcontractor_id:
sub_query = sub_query.filter(
ManholeExcavation.subcontractor_id == int(subcontractor_id)
)
if ra_bill_list:
sub_query = sub_query.filter(
ManholeExcavation.RA_Bill_No.in_(ra_bill_list)
)
sub_records = sub_query.all()
sub_keys = [
(
(r.MH_NO or "").strip().upper(),
(r.Location or "").strip().upper()
)
for r in sub_records
]
client_query = ManholeExcavationClient.query
if sub_keys:
client_query = client_query.filter(
tuple_(
func.upper(func.trim(ManholeExcavationClient.MH_NO)),
func.upper(func.trim(ManholeExcavationClient.Location))
).in_(sub_keys)
)
client_records = client_query.all()
chart_data = [
{
"label": "Marshi 0 to 1.5",
"client": total(client_records, "Marshi_Muddy_Slushy_0_to_1_5_total"),
"sub": 0
},
{
"label": "Marshi 1.5 to 3.0",
"client": total(client_records, "Marshi_Muddy_Slushy_1_5_to_3_0_total"),
"sub": 0
},
{
"label": "Marshi 3.0 to 4.5",
"client": total(client_records, "Marshi_Muddy_Slushy_3_0_to_4_5_total"),
"sub": 0
},
{
"label": "Soft Murum 0 to 1.5",
"client": total(client_records, "Soft_Murum_0_to_1_5_total"),
"sub": total(sub_records, "Soft_Murum_0_to_1_5_total")
},
{
"label": "Soft Murum 1.5 to 3.0",
"client": total(client_records, "Soft_Murum_1_5_to_3_0_total"),
"sub": total(sub_records, "Soft_Murum_1_5_to_3_0_total")
},
{
"label": "Soft Murum 3.0 to 4.5",
"client": total(client_records, "Soft_Murum_3_0_to_4_5_total"),
"sub": total(sub_records, "Soft_Murum_3_0_to_4_5_total")
},
{
"label": "Hard Murum 0 to 1.5",
"client": total(client_records, "Hard_Murum_0_to_1_5_total"),
"sub": total(sub_records, "Hard_Murum_0_to_1_5_total")
},
{
"label": "Hard Murum 1.5 to 3.0",
"client": total(client_records, "Hard_Murum_1_5_to_3_0_total"),
"sub": total(sub_records, "Hard_Murum_1_5_and_above_total")
},
{
"label": "Soft Rock 0 to 1.5",
"client": total(client_records, "Soft_Rock_0_to_1_5_total"),
"sub": total(sub_records, "Soft_Rock_0_to_1_5_total")
},
{
"label": "Soft Rock 1.5 to 3.0",
"client": total(client_records, "Soft_Rock_1_5_to_3_0_total"),
"sub": total(sub_records, "Soft_Rock_1_5_and_above_total")
},
{
"label": "Hard Rock 0 to 1.5",
"client": total(client_records, "Hard_Rock_0_to_1_5_total"),
"sub": total(sub_records, "Hard_Rock_0_to_1_5_total")
},
{
"label": "Hard Rock 1.5 to 3.0",
"client": total(client_records, "Hard_Rock_1_5_to_3_0_total"),
"sub": total(sub_records, "Hard_Rock_1_5_to_3_0_total")
},
{
"label": "Hard Rock 3.0 to 4.5",
"client": total(client_records, "Hard_Rock_3_0_to_4_5_total"),
"sub": total(sub_records, "Hard_Rock_3_0_to_4_5_total")
},
{
"label": "Hard Rock 4.5 to 6.0",
"client": total(client_records, "Hard_Rock_4_5_to_6_0_total"),
"sub": total(sub_records, "Hard_Rock_4_5_to_6_0_total")
},
{
"label": "Hard Rock 6.0 to 7.5",
"client": total(client_records, "Hard_Rock_6_0_to_7_5_total"),
"sub": total(sub_records, "Hard_Rock_6_0_to_7_5_total")
}
]
return jsonify({
"title": "Manhole Excavation Comparison",
"y_title": "Manhole Qty (Nos)",
"labels": [x["label"] for x in chart_data],
"client_qty": [x["client"] for x in chart_data],
"sub_qty": [x["sub"] for x in chart_data]
})
# category = Manhole_Domestic_Chamber
@dashboard_bp.route("/api/mdc-analysis")
def Manhole_Domestic_Chamber_analysis():
subcontractor_id = request.args.get("subcontractor", "").strip()
ra_bill = request.args.get("ra_bill", "").strip()
# Convert "1,2,3" -> ["1", "2", "3"]
ra_bill_list = []
if ra_bill:
ra_bill_list = [x.strip() for x in ra_bill.split(",") if x.strip()]
# Subcontractor Query
sub_query = ManholeDomesticChamber.query
if subcontractor_id:
sub_query = sub_query.filter(
ManholeDomesticChamber.subcontractor_id == int(subcontractor_id)
)
if ra_bill_list:
sub_query = sub_query.filter(
ManholeDomesticChamber.RA_Bill_No.in_(ra_bill_list)
)
sub_records = sub_query.all()
sub_keys = [
(
(r.MH_NO or "").strip().upper(),
(r.Location or "").strip().upper()
)
for r in sub_records
]
client_query = ManholeDomesticChamberClient.query
if sub_keys:
client_query = client_query.filter(
tuple_(
func.upper(func.trim(ManholeDomesticChamberClient.MH_NO)),
func.upper(func.trim(ManholeDomesticChamberClient.Location))
).in_(sub_keys)
)
client_records = client_query.all()
chart_data = [
{
"label": "Depth of MH",
"client": total(client_records, "Depth_of_MH"),
"sub": total(sub_records, "Depth_of_MH")
},
{
"label": "Domestic_Chambers Total",
"client": total(client_records, "Total"),
"sub": total(sub_records, "Total")
}
]
return jsonify({
"title": "Domestic Chamber Comparison",
"y_title": "Quantity (Nos)",
"labels": [x["label"] for x in chart_data],
"client_qty": [x["client"] for x in chart_data],
"sub_qty": [x["sub"] for x in chart_data]
})
# category = Laying
@dashboard_bp.route("/api/laying-analysis")
def laying_analysis():
subcontractor_id = request.args.get("subcontractor", "").strip()
ra_bill = request.args.get("ra_bill", "").strip()
# Convert "1,2,3" -> ["1", "2", "3"]
ra_bill_list = []
if ra_bill:
ra_bill_list = [x.strip() for x in ra_bill.split(",") if x.strip()]
# Subcontractor Query
sub_query = Laying.query
if subcontractor_id:
sub_query = sub_query.filter(
Laying.subcontractor_id == int(subcontractor_id)
)
if ra_bill_list:
sub_query = sub_query.filter(
Laying.RA_Bill_No.in_(ra_bill_list)
)
sub_records = sub_query.all()
sub_keys = [
(
(r.MH_NO or "").strip().upper(),
(r.Location or "").strip().upper()
)
for r in sub_records
]
client_query = LayingClient.query
if sub_keys:
client_query = client_query.filter(
tuple_(
func.upper(func.trim(LayingClient.MH_NO)),
func.upper(func.trim(LayingClient.Location))
).in_(sub_keys)
)
client_records = client_query.all()
chart_data = [
{
"label": "150 mm",
"client": total(client_records, "pipe_150_mm"),
"sub": total(sub_records, "pipe_150_mm")
},
{
"label": "200 mm",
"client": total(client_records, "pipe_200_mm"),
"sub": total(sub_records, "pipe_200_mm")
},
{
"label": "250 mm",
"client": total(client_records, "pipe_250_mm"),
"sub": total(sub_records, "pipe_250_mm")
},
{
"label": "300 mm",
"client": total(client_records, "pipe_300_mm"),
"sub": total(sub_records, "pipe_300_mm")
},
{
"label": "350 mm",
"client": total(client_records, "pipe_350_mm"),
"sub": total(sub_records, "pipe_350_mm")
},
{
"label": "400 mm",
"client": total(client_records, "pipe_400_mm"),
"sub": total(sub_records, "pipe_400_mm")
},
{
"label": "450 mm",
"client": total(client_records, "pipe_450_mm"),
"sub": total(sub_records, "pipe_450_mm")
},
{
"label": "500 mm",
"client": total(client_records, "pipe_500_mm"),
"sub": total(sub_records, "pipe_500_mm")
},
{
"label": "600 mm",
"client": total(client_records, "pipe_600_mm"),
"sub": total(sub_records, "pipe_600_mm")
},
{
"label": "700 mm",
"client": total(client_records, "pipe_700_mm"),
"sub": total(sub_records, "pipe_700_mm")
},
{
"label": "900 mm",
"client": total(client_records, "pipe_900_mm"),
"sub": total(sub_records, "pipe_900_mm")
},
{
"label": "1200 mm",
"client": total(client_records, "pipe_1200_mm"),
"sub": total(sub_records, "pipe_1200_mm")
}
]
return jsonify({
"title": "Pipe Laying Comparison",
"y_title": "Pipe Length (Mtr)",
"labels": [x["label"] for x in chart_data],
"client_qty": [x["client"] for x in chart_data],
"sub_qty": [x["sub"] for x in chart_data]
})