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 from app.models.subcontractor_rate_model import SubcontractorRate # 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 from app.models.client_rate_model import ClientRate # Services from app.services.subcontractor_rate_service import SubcontractorRateService from app.services.client_rate_service import ClientRateService 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] # }) @dashboard_bp.route("/api/tr-analysis") def trench_analysis(): # ========================================================= # CONSTANT # ========================================================= CATEGORY = "trench_excavation" # ========================================================= # REQUEST PARAMETERS # ========================================================= subcontractor_id = request.args.get( "subcontractor", "" ).strip() ra_bill = request.args.get( "ra_bill", "" ).strip() # ========================================================= # VALIDATE SUBCONTRACTOR # ========================================================= subcontractor_id_int = None if subcontractor_id: try: subcontractor_id_int = int(subcontractor_id) except ValueError: return jsonify({ "success": False, "message": "Invalid subcontractor." }), 400 # ========================================================= # RA BILL LIST # ========================================================= ra_bill_list = [] if ra_bill: ra_bill_list = [ bill.strip() for bill in ra_bill.split(",") if bill.strip() ] # ========================================================= # SUBCONTRACTOR QUERY # ========================================================= sub_query = TrenchExcavation.query if subcontractor_id_int: sub_query = sub_query.filter( TrenchExcavation.subcontractor_id == subcontractor_id_int ) if ra_bill_list: sub_query = sub_query.filter( TrenchExcavation.RA_Bill_No.in_(ra_bill_list) ) sub_records = sub_query.all() # ========================================================= # SUBCONTRACTOR KEYS # ========================================================= sub_keys = [ ( (record.MH_NO or "").strip().upper(), (record.Location or "").strip().upper() ) for record in sub_records ] # ========================================================= # CLIENT QUERY # ========================================================= 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() # ========================================================= # GET CLIENT RATES # # Matching: # category = trench_excavation # item_name = actual model field name # ========================================================= client_rates = ( ClientRate.query .filter( func.lower( func.trim(ClientRate.category) ) == CATEGORY.lower(), func.lower( func.trim(ClientRate.status) ) == "active" ) .all() ) # ========================================================= # GET SUBCONTRACTOR RATES # # Matching: # subcontractor_id # category = trench_excavation # item_name = actual model field name # ========================================================= sub_rates = [] if subcontractor_id_int: sub_rates = ( SubcontractorRate.query .filter( SubcontractorRate.subcontractor_id == subcontractor_id_int, func.lower( func.trim( SubcontractorRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( SubcontractorRate.status ) ) == "active" ) .all() ) # ========================================================= # RATE MAP # # Example: # # Hard_Rock_6_0_to_7_5_total : 600 # Hard_Rock_4_5_to_6_0_total : 600 # ========================================================= client_rate_map = { (rate.item_name or "").strip().lower(): float(rate.rate or 0) for rate in client_rates } sub_rate_map = { (rate.item_name or "").strip().lower(): float(rate.rate or 0) for rate in sub_rates } # ========================================================= # TOTAL HELPER # ========================================================= def total(records, field): value = 0 for record in records: field_value = getattr( record, field, 0 ) if field_value is not None: value += float(field_value) return round(value, 2) # ========================================================= # TRECH MAPPING # # label = Dashboard display name # field = Client/Subcontractor model field # item_name = Rate Master item_name # # item_name intentionally uses the actual model field name. # ========================================================= TRENCH_MAPPING = [ { "label": "Marshi 0 to 1.5", "field": "Marshi_Muddy_Slushy_0_to_1_5_total", "item_name": "Marshi_Muddy_Slushy_0_to_1_5_total" }, { "label": "Marshi 1.5 to 3.0", "field": "Marshi_Muddy_Slushy_1_5_to_3_0_total", "item_name": "Marshi_Muddy_Slushy_1_5_to_3_0_total" }, { "label": "Marshi 3.0 to 4.5", "field": "Marshi_Muddy_Slushy_3_0_to_4_5_total", "item_name": "Marshi_Muddy_Slushy_3_0_to_4_5_total" }, { "label": "Soft Murum 0 to 1.5", "field": "Soft_Murum_0_to_1_5_total", "item_name": "Soft_Murum_0_to_1_5_total" }, { "label": "Soft Murum 1.5 to 3.0", "field": "Soft_Murum_1_5_to_3_0_total", "item_name": "Soft_Murum_1_5_to_3_0_total" }, { "label": "Soft Murum 3.0 to 4.5", "field": "Soft_Murum_3_0_to_4_5_total", "item_name": "Soft_Murum_3_0_to_4_5_total" }, { "label": "Hard Murum 0 to 1.5", "field": "Hard_Murum_0_to_1_5_total", "item_name": "Hard_Murum_0_to_1_5_total" }, { "label": "Hard Murum 1.5 to 3.0", "field": "Hard_Murum_1_5_to_3_0_total", "item_name": "Hard_Murum_1_5_to_3_0_total" }, { "label": "Soft Rock 0 to 1.5", "field": "Soft_Rock_0_to_1_5_total", "item_name": "Soft_Rock_0_to_1_5_total" }, { "label": "Soft Rock 1.5 to 3.0", "field": "Soft_Rock_1_5_to_3_0_total", "item_name": "Soft_Rock_1_5_to_3_0_total" }, { "label": "Hard Rock 0 to 1.5", "field": "Hard_Rock_0_to_1_5_total", "item_name": "Hard_Rock_0_to_1_5_total" }, { "label": "Hard Rock 1.5 to 3.0", "field": "Hard_Rock_1_5_to_3_0_total", "item_name": "Hard_Rock_1_5_to_3_0_total" }, { "label": "Hard Rock 3.0 to 4.5", "field": "Hard_Rock_3_0_to_4_5_total", "item_name": "Hard_Rock_3_0_to_4_5_total" }, { "label": "Hard Rock 4.5 to 6.0", "field": "Hard_Rock_4_5_to_6_0_total", "item_name": "Hard_Rock_4_5_to_6_0_total" }, { "label": "Hard Rock 6.0 to 7.5", "field": "Hard_Rock_6_0_to_7_5_total", "item_name": "Hard_Rock_6_0_to_7_5_total" } ] # ========================================================= # BUILD CHART DATA # ========================================================= chart_data = [] for mapping in TRENCH_MAPPING: field = mapping["field"] client_qty = total( client_records, field ) # ----------------------------------------------------- # SUBCONTRACTOR QUANTITY # ----------------------------------------------------- sub_qty = 0 if sub_records: sub_qty = total( sub_records, field ) # ----------------------------------------------------- # RATE ITEM NAME # ----------------------------------------------------- item_name = mapping["item_name"] rate_key = item_name.strip().lower() # ----------------------------------------------------- # CLIENT RATE # ----------------------------------------------------- client_rate = client_rate_map.get( rate_key, 0 ) # ----------------------------------------------------- # SUBCONTRACTOR RATE # ----------------------------------------------------- sub_rate = sub_rate_map.get( rate_key, 0 ) # ----------------------------------------------------- # AMOUNT # ----------------------------------------------------- client_amount = ( client_qty * client_rate ) sub_amount = ( sub_qty * sub_rate ) # ----------------------------------------------------- # DIFFERENCES # ----------------------------------------------------- qty_difference = ( client_qty - sub_qty ) rate_difference = ( client_rate - sub_rate ) amount_difference = ( client_amount - sub_amount ) # ----------------------------------------------------- # FINAL ROW # ----------------------------------------------------- chart_data.append({ "label": mapping["label"], # Actual model field "field": field, # Rate Master item_name "item_name": item_name, # Quantity "client": round( client_qty, 2 ), "sub": round( sub_qty, 2 ), # Rate "client_rate": round( client_rate, 2 ), "sub_rate": round( sub_rate, 2 ), # Amount "client_amount": round( client_amount, 2 ), "sub_amount": round( sub_amount, 2 ), # Difference "qty_difference": round( qty_difference, 2 ), "rate_difference": round( rate_difference, 2 ), "amount_difference": round( amount_difference, 2 ) }) # ========================================================= # RESPONSE # ========================================================= return jsonify({ "success": True, "title": "Trench Excavation Comparison", "y_title": "Excavation Qty (Cum)", # ----------------------------------------------------- # CHART # ----------------------------------------------------- "labels": [ item["label"] for item in chart_data ], "client_qty": [ item["client"] for item in chart_data ], "sub_qty": [ item["sub"] for item in chart_data ], # ----------------------------------------------------- # RATE # ----------------------------------------------------- "client_rate": [ item["client_rate"] for item in chart_data ], "sub_rate": [ item["sub_rate"] for item in chart_data ], # ----------------------------------------------------- # AMOUNT # ----------------------------------------------------- "client_amount": [ item["client_amount"] for item in chart_data ], "sub_amount": [ item["sub_amount"] for item in chart_data ], # ----------------------------------------------------- # DIFFERENCE # ----------------------------------------------------- "qty_difference": [ item["qty_difference"] for item in chart_data ], "rate_difference": [ item["rate_difference"] for item in chart_data ], "amount_difference": [ item["amount_difference"] for item in chart_data ], # ----------------------------------------------------- # COMPLETE ROW DATA # Useful for table/export # ----------------------------------------------------- "rows": chart_data }) @dashboard_bp.route("/api/mh-analysis") def manhole_analysis(): # ========================================================= # CATEGORY # ========================================================= CATEGORY = "manhole_excavation" # ========================================================= # REQUEST PARAMETERS # ========================================================= subcontractor_id = request.args.get( "subcontractor", "" ).strip() ra_bill = request.args.get( "ra_bill", "" ).strip() # ========================================================= # VALIDATE SUBCONTRACTOR # ========================================================= subcontractor_id_int = None if subcontractor_id: try: subcontractor_id_int = int( subcontractor_id ) except ValueError: return jsonify({ "success": False, "message": "Invalid subcontractor." }), 400 # ========================================================= # RA BILL LIST # ========================================================= ra_bill_list = [] if ra_bill: ra_bill_list = [ bill.strip() for bill in ra_bill.split(",") if bill.strip() ] # ========================================================= # SUBCONTRACTOR QUERY # ========================================================= sub_query = ManholeExcavation.query if subcontractor_id_int: sub_query = sub_query.filter( ManholeExcavation.subcontractor_id == subcontractor_id_int ) if ra_bill_list: sub_query = sub_query.filter( ManholeExcavation.RA_Bill_No.in_( ra_bill_list ) ) sub_records = sub_query.all() # ========================================================= # SUBCONTRACTOR KEYS # MH_NO + LOCATION # ========================================================= sub_keys = [ ( (record.MH_NO or "").strip().upper(), (record.Location or "").strip().upper() ) for record in sub_records ] # ========================================================= # CLIENT QUERY # ========================================================= 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() # ========================================================= # GET CLIENT RATES # # Category: # manhole_excavation # # item_name: # actual model field name # ========================================================= client_rates = ( ClientRate.query .filter( func.lower( func.trim( ClientRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( ClientRate.status ) ) == "active" ) .all() ) # ========================================================= # GET SUBCONTRACTOR RATES # # Selected Subcontractor # + # Category # + # Active # ========================================================= sub_rates = [] if subcontractor_id_int: sub_rates = ( SubcontractorRate.query .filter( SubcontractorRate.subcontractor_id == subcontractor_id_int, func.lower( func.trim( SubcontractorRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( SubcontractorRate.status ) ) == "active" ) .all() ) # ========================================================= # RATE MAP # # Example: # # hard_rock_0_to_1_5_total : 450 # hard_rock_1_5_to_3_0_total : 500 # ========================================================= client_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in client_rates } sub_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in sub_rates } # ========================================================= # TOTAL HELPER # ========================================================= def total(records, field): value = 0 for record in records: field_value = getattr( record, field, 0 ) if field_value is not None: value += float( field_value ) return round(value, 2) # ========================================================= # MANHOLE MAPPING # # label: # Dashboard display name # # field: # Actual model field # # item_name: # Rate Master item_name # # IMPORTANT: # item_name must be the actual model field name. # ========================================================= MANHOLE_MAPPING = [ { "label": "Marshi 0 to 1.5", "field": "Marshi_Muddy_Slushy_0_to_1_5_total", "item_name": "Marshi_Muddy_Slushy_0_to_1_5_total" }, { "label": "Marshi 1.5 to 3.0", "field": "Marshi_Muddy_Slushy_1_5_to_3_0_total", "item_name": "Marshi_Muddy_Slushy_1_5_to_3_0_total" }, { "label": "Marshi 3.0 to 4.5", "field": "Marshi_Muddy_Slushy_3_0_to_4_5_total", "item_name": "Marshi_Muddy_Slushy_3_0_to_4_5_total" }, { "label": "Soft Murum 0 to 1.5", "field": "Soft_Murum_0_to_1_5_total", "item_name": "Soft_Murum_0_to_1_5_total" }, { "label": "Soft Murum 1.5 to 3.0", "field": "Soft_Murum_1_5_to_3_0_total", "item_name": "Soft_Murum_1_5_to_3_0_total" }, { "label": "Soft Murum 3.0 to 4.5", "field": "Soft_Murum_3_0_to_4_5_total", "item_name": "Soft_Murum_3_0_to_4_5_total" }, { "label": "Hard Murum 0 to 1.5", "field": "Hard_Murum_0_to_1_5_total", "item_name": "Hard_Murum_0_to_1_5_total" }, { "label": "Hard Murum 1.5 to 3.0", "field": "Hard_Murum_1_5_to_3_0_total", "item_name": "Hard_Murum_1_5_to_3_0_total" }, { "label": "Soft Rock 0 to 1.5", "field": "Soft_Rock_0_to_1_5_total", "item_name": "Soft_Rock_0_to_1_5_total" }, { "label": "Soft Rock 1.5 to 3.0", "field": "Soft_Rock_1_5_to_3_0_total", "item_name": "Soft_Rock_1_5_to_3_0_total" }, { "label": "Hard Rock 0 to 1.5", "field": "Hard_Rock_0_to_1_5_total", "item_name": "Hard_Rock_0_to_1_5_total" }, { "label": "Hard Rock 1.5 to 3.0", "field": "Hard_Rock_1_5_to_3_0_total", "item_name": "Hard_Rock_1_5_to_3_0_total" }, { "label": "Hard Rock 3.0 to 4.5", "field": "Hard_Rock_3_0_to_4_5_total", "item_name": "Hard_Rock_3_0_to_4_5_total" }, { "label": "Hard Rock 4.5 to 6.0", "field": "Hard_Rock_4_5_to_6_0_total", "item_name": "Hard_Rock_4_5_to_6_0_total" }, { "label": "Hard Rock 6.0 to 7.5", "field": "Hard_Rock_6_0_to_7_5_total", "item_name": "Hard_Rock_6_0_to_7_5_total" } ] # ========================================================= # BUILD CHART DATA # ========================================================= chart_data = [] for mapping in MANHOLE_MAPPING: field = mapping["field"] # ===================================================== # CLIENT QUANTITY # ===================================================== client_qty = total( client_records, field ) # ===================================================== # SUBCONTRACTOR QUANTITY # ===================================================== sub_qty = 0 if sub_records: sub_qty = total( sub_records, field ) # ===================================================== # RATE MASTER ITEM NAME # ===================================================== item_name = mapping[ "item_name" ] rate_key = ( item_name .strip() .lower() ) # ===================================================== # CLIENT RATE # ===================================================== client_rate = client_rate_map.get( rate_key, 0 ) # ===================================================== # SUBCONTRACTOR RATE # ===================================================== sub_rate = sub_rate_map.get( rate_key, 0 ) # ===================================================== # AMOUNT # ===================================================== client_amount = ( client_qty * client_rate ) sub_amount = ( sub_qty * sub_rate ) # ===================================================== # DIFFERENCE # ===================================================== qty_difference = ( client_qty - sub_qty ) rate_difference = ( client_rate - sub_rate ) amount_difference = ( client_amount - sub_amount ) # ===================================================== # FINAL ROW # ===================================================== chart_data.append({ # Display "label": mapping["label"], # Actual model field "field": field, # Rate Master item_name "item_name": item_name, # Quantity "client": round(client_qty, 2), "sub": round(sub_qty, 2), # Rate "client_rate": round(client_rate, 2), "sub_rate": round(sub_rate, 2), # Amount "client_amount": round(client_amount, 2), "sub_amount": round(sub_amount, 2), # Difference "qty_difference": round(qty_difference, 2), "rate_difference": round(rate_difference, 2), "amount_difference": round(amount_difference, 2) }) # ========================================================= # RESPONSE # ========================================================= return jsonify({ "success": True, "title": "Manhole Excavation Comparison", "y_title": "Manhole Qty (Nos)", # ===================================================== # CHART DATA # ===================================================== "labels": [ item["label"] for item in chart_data ], "client_qty": [ item["client"] for item in chart_data ], "sub_qty": [ item["sub"] for item in chart_data ], # ===================================================== # RATE DATA # ===================================================== "client_rate": [ item["client_rate"] for item in chart_data ], "sub_rate": [ item["sub_rate"] for item in chart_data ], # ===================================================== # AMOUNT DATA # ===================================================== "client_amount": [ item["client_amount"] for item in chart_data ], "sub_amount": [ item["sub_amount"] for item in chart_data ], # ===================================================== # DIFFERENCE DATA # ===================================================== "qty_difference": [ item["qty_difference"] for item in chart_data ], "rate_difference": [ item["rate_difference"] for item in chart_data ], "amount_difference": [ item["amount_difference"] for item in chart_data ], # ===================================================== # COMPLETE TABLE ROWS # ===================================================== "rows": chart_data }) @dashboard_bp.route("/api/mdc-analysis") def Manhole_Domestic_Chamber_analysis(): # ========================================================= # CATEGORY # ========================================================= CATEGORY = "Manhole_Domestic_Chamber" # ========================================================= # REQUEST PARAMETERS # ========================================================= subcontractor_id = request.args.get( "subcontractor", "" ).strip() ra_bill = request.args.get( "ra_bill", "" ).strip() # ========================================================= # VALIDATE SUBCONTRACTOR # ========================================================= subcontractor_id_int = None if subcontractor_id: try: subcontractor_id_int = int( subcontractor_id ) except ValueError: return jsonify({ "success": False, "message": "Invalid subcontractor." }), 400 # ========================================================= # RA BILL LIST # ========================================================= 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_int: sub_query = sub_query.filter( ManholeDomesticChamber.subcontractor_id == subcontractor_id_int ) if ra_bill_list: sub_query = sub_query.filter( ManholeDomesticChamber.RA_Bill_No.in_( ra_bill_list ) ) sub_records = sub_query.all() # ========================================================= # MATCHING KEYS # MH_NO + LOCATION # ========================================================= sub_keys = [ ( (r.MH_NO or "").strip().upper(), (r.Location or "").strip().upper() ) for r in sub_records ] # ========================================================= # CLIENT QUERY # ========================================================= 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() # ========================================================= # GET CLIENT RATES # # Category: # Manhole_Domestic_Chamber # ========================================================= client_rates = ( ClientRate.query .filter( func.lower( func.trim( ClientRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( ClientRate.status ) ) == "active" ) .all() ) # ========================================================= # GET SUBCONTRACTOR RATES # # Selected subcontractor # + # Category # + # Active # ========================================================= sub_rates = [] if subcontractor_id_int: sub_rates = ( SubcontractorRate.query .filter( SubcontractorRate.subcontractor_id == subcontractor_id_int, func.lower( func.trim( SubcontractorRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( SubcontractorRate.status ) ) == "active" ) .all() ) # ========================================================= # CLIENT RATE MAP # ========================================================= client_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in client_rates } # ========================================================= # SUBCONTRACTOR RATE MAP # ========================================================= sub_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in sub_rates } # ========================================================= # TOTAL HELPER # ========================================================= def total(records, field): value = 0 for record in records: field_value = getattr( record, field, 0 ) if field_value is not None: value += float( field_value ) return round(value, 2) # ========================================================= # MDC MAPPING # # label = Dashboard display name # field = Actual model field # item_name = Rate Master item_name # ========================================================= MDC_MAPPING = [ { "label": "Depth of MH", "field": "Depth_of_MH", "item_name": "Depth_of_MH" }, { "label": "Domestic Chambers Total", "field": "Total", "item_name": "Total" } ] # ========================================================= # BUILD DATA # ========================================================= chart_data = [] for item in MDC_MAPPING: field = item["field"] item_name = item["item_name"] # ===================================================== # QUANTITY # ===================================================== client_qty = total( client_records, field ) sub_qty = total( sub_records, field ) # ===================================================== # RATE KEY # ===================================================== rate_key = ( item_name .strip() .lower() ) # ===================================================== # CLIENT RATE # ===================================================== client_rate = client_rate_map.get( rate_key, 0 ) # ===================================================== # SUBCONTRACTOR RATE # ===================================================== sub_rate = sub_rate_map.get( rate_key, 0 ) # ===================================================== # AMOUNT # ===================================================== client_amount = ( client_qty * client_rate ) sub_amount = ( sub_qty * sub_rate ) # ===================================================== # DIFFERENCES # ===================================================== qty_difference = ( client_qty - sub_qty ) rate_difference = ( client_rate - sub_rate ) amount_difference = ( client_amount - sub_amount ) # ===================================================== # FINAL ROW # ===================================================== chart_data.append({ "label": item["label"], "field": field, "item_name": item_name, # Quantity "client": round(client_qty, 2), "sub": round(sub_qty, 2), # Rate "client_rate": round(client_rate, 2), "sub_rate": round(sub_rate, 2), # Amount "client_amount": round(client_amount, 2), "sub_amount": round(sub_amount, 2), # Difference "qty_difference": round(qty_difference, 2), "rate_difference": round(rate_difference, 2), "amount_difference": round(amount_difference, 2) }) # ========================================================= # RESPONSE # ========================================================= return jsonify({ "success": True, "title": "Domestic Chamber Comparison", "y_title": "Quantity (Nos)", # ===================================================== # CHART # ===================================================== "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 ], # ===================================================== # RATE # ===================================================== "client_rate": [ x["client_rate"] for x in chart_data ], "sub_rate": [ x["sub_rate"] for x in chart_data ], # ===================================================== # AMOUNT # ===================================================== "client_amount": [ x["client_amount"] for x in chart_data ], "sub_amount": [ x["sub_amount"] for x in chart_data ], # ===================================================== # DIFFERENCE # ===================================================== "qty_difference": [ x["qty_difference"] for x in chart_data ], "rate_difference": [ x["rate_difference"] for x in chart_data ], "amount_difference": [ x["amount_difference"] for x in chart_data ], # Complete table data "rows": chart_data }) @dashboard_bp.route("/api/laying-analysis") def laying_analysis(): # ========================================================= # CATEGORY # ========================================================= CATEGORY = "Laying" # ========================================================= # REQUEST PARAMETERS # ========================================================= subcontractor_id = request.args.get( "subcontractor", "" ).strip() ra_bill = request.args.get( "ra_bill", "" ).strip() # ========================================================= # VALIDATE SUBCONTRACTOR # ========================================================= subcontractor_id_int = None if subcontractor_id: try: subcontractor_id_int = int( subcontractor_id ) except ValueError: return jsonify({ "success": False, "message": "Invalid subcontractor." }), 400 # ========================================================= # RA BILL LIST # ========================================================= 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_int: sub_query = sub_query.filter( Laying.subcontractor_id == subcontractor_id_int ) if ra_bill_list: sub_query = sub_query.filter( Laying.RA_Bill_No.in_( ra_bill_list ) ) sub_records = sub_query.all() # ========================================================= # MATCHING KEYS # MH_NO + LOCATION # ========================================================= sub_keys = [ ( (r.MH_NO or "").strip().upper(), (r.Location or "").strip().upper() ) for r in sub_records ] # ========================================================= # CLIENT QUERY # ========================================================= 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() # ========================================================= # GET CLIENT RATES # # Category = Laying # ========================================================= client_rates = ( ClientRate.query .filter( func.lower( func.trim( ClientRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( ClientRate.status ) ) == "active" ) .all() ) # ========================================================= # GET SUBCONTRACTOR RATES # # Selected Subcontractor # + # Category = Laying # + # Active # ========================================================= sub_rates = [] if subcontractor_id_int: sub_rates = ( SubcontractorRate.query .filter( SubcontractorRate.subcontractor_id == subcontractor_id_int, func.lower( func.trim( SubcontractorRate.category ) ) == CATEGORY.lower(), func.lower( func.trim( SubcontractorRate.status ) ) == "active" ) .all() ) # ========================================================= # CLIENT RATE MAP # ========================================================= client_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in client_rates } # ========================================================= # SUBCONTRACTOR RATE MAP # ========================================================= sub_rate_map = { (rate.item_name or "") .strip() .lower(): float(rate.rate or 0) for rate in sub_rates } # ========================================================= # TOTAL HELPER # ========================================================= def total(records, field): value = 0 for record in records: field_value = getattr( record, field, 0 ) if field_value is not None: value += float( field_value ) return round(value, 2) # ========================================================= # LAYING MAPPING # # label: # Dashboard display name # # field: # Actual model field # # item_name: # Rate Master item_name # ========================================================= LAYING_MAPPING = [ { "label": "150 mm", "field": "pipe_150_mm", "item_name": "pipe_150_mm" }, { "label": "200 mm", "field": "pipe_200_mm", "item_name": "pipe_200_mm" }, { "label": "250 mm", "field": "pipe_250_mm", "item_name": "pipe_250_mm" }, { "label": "300 mm", "field": "pipe_300_mm", "item_name": "pipe_300_mm" }, { "label": "350 mm", "field": "pipe_350_mm", "item_name": "pipe_350_mm" }, { "label": "400 mm", "field": "pipe_400_mm", "item_name": "pipe_400_mm" }, { "label": "450 mm", "field": "pipe_450_mm", "item_name": "pipe_450_mm" }, { "label": "500 mm", "field": "pipe_500_mm", "item_name": "pipe_500_mm" }, { "label": "600 mm", "field": "pipe_600_mm", "item_name": "pipe_600_mm" }, { "label": "700 mm", "field": "pipe_700_mm", "item_name": "pipe_700_mm" }, { "label": "900 mm", "field": "pipe_900_mm", "item_name": "pipe_900_mm" }, { "label": "1200 mm", "field": "pipe_1200_mm", "item_name": "pipe_1200_mm" } ] # ========================================================= # BUILD DATA # ========================================================= chart_data = [] for item in LAYING_MAPPING: field = item["field"] item_name = item["item_name"] # ===================================================== # QUANTITY # ===================================================== client_qty = total( client_records, field ) sub_qty = total( sub_records, field ) # ===================================================== # RATE KEY # ===================================================== rate_key = ( item_name .strip() .lower() ) # ===================================================== # CLIENT RATE # ===================================================== client_rate = client_rate_map.get( rate_key, 0 ) # ===================================================== # SUBCONTRACTOR RATE # ===================================================== sub_rate = sub_rate_map.get( rate_key, 0 ) # ===================================================== # AMOUNT # ===================================================== client_amount = ( client_qty * client_rate ) sub_amount = ( sub_qty * sub_rate ) # ===================================================== # DIFFERENCES # ===================================================== qty_difference = ( client_qty - sub_qty ) rate_difference = ( client_rate - sub_rate ) amount_difference = ( client_amount - sub_amount ) # ===================================================== # FINAL ROW # ===================================================== chart_data.append({ "label": item["label"], # Actual model field "field": field, # Rate Master item_name "item_name": item_name, # Quantity "client": round(client_qty, 2), "sub": round(sub_qty, 2), # Rate "client_rate": round(client_rate, 2), "sub_rate": round(sub_rate, 2), # Amount "client_amount": round(client_amount, 2), "sub_amount": round(sub_amount, 2), # Difference "qty_difference": round(qty_difference, 2), "rate_difference": round(rate_difference, 2), "amount_difference": round(amount_difference, 2) }) # ========================================================= # RESPONSE # ========================================================= return jsonify({ "success": True, "title": "Pipe Laying Comparison", "y_title": "Pipe Length (Mtr)", # ===================================================== # CHART DATA # ===================================================== "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 ], # ===================================================== # RATE DATA # ===================================================== "client_rate": [ x["client_rate"] for x in chart_data ], "sub_rate": [ x["sub_rate"] for x in chart_data ], # ===================================================== # AMOUNT DATA # ===================================================== "client_amount": [ x["client_amount"] for x in chart_data ], "sub_amount": [ x["sub_amount"] for x in chart_data ], # ===================================================== # DIFFERENCE DATA # ===================================================== "qty_difference": [ x["qty_difference"] for x in chart_data ], "rate_difference": [ x["rate_difference"] for x in chart_data ], "amount_difference": [ x["amount_difference"] for x in chart_data ], # Complete table rows "rows": chart_data })