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 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)) @dashboard_bp.route("/api/trench-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({ "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] })