This commit is contained in:
2026-08-08 12:55:03 +05:30
4 changed files with 728 additions and 107 deletions

View File

@@ -1,3 +1,4 @@
import re
from sqlalchemy import func
from app import db
@@ -7,6 +8,13 @@ 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.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.utils.regex_utils import RegularExpression
class AbstractReportService:
@@ -356,4 +364,215 @@ class AbstractReportService:
</div>
"""
return html
# ================================================================
# CLIENT ABSTRACT REPORT
def _format_range_text(raw):
"""'6_0_to_7_5' -> '6.0-7.5' '0_to_1_5' -> '0-1.5'"""
raw = re.sub(r'(\d+)_(\d+)', lambda m: f"{m.group(1)}.{m.group(2)}", raw)
return raw.replace("_to_", "-")
def _label_for_total_column(col_name):
"""'Soft_Murum_0_to_1_5_total' -> 'Soft Murum 0-1.5 mm'"""
value = col_name[:-6] if col_name.endswith("_total") else col_name
m = re.search(r'(\d[\d_]*_to_[\d_]+)$', value)
if m:
prefix = value[:m.start()].rstrip("_").replace("_", " ")
range_text = _format_range_text(m.group(1))
return f"{prefix} {range_text} mm".strip()
return value.replace("_", " ").title()
def _label_for_d_range_column(col_name):
"""'d_6_0_to_6_5' -> '6.0-6.5 mm'"""
raw = col_name[2:] # strip leading "d_"
return f"{_format_range_text(raw)} mm"
def _label_for_pipe_column(col_name):
"""'pipe_150_mm' -> '150 mm Dia'"""
m = re.match(r"pipe_(\d+)_mm", col_name)
if m:
return f"{m.group(1)} mm Dia"
return col_name.replace("_", " ").title()
class ClientAbstractReportService:
def __init__(self, ra_bill_no=None):
self.ra_bill_no = ra_bill_no
def filters(self):
f = {}
if self.ra_bill_no:
f["RA_Bill_No"] = self.ra_bill_no
return f
def _summary(self, model, matcher, uom, label_fn):
f = self.filters()
summary = []
for column in model.__table__.columns:
if matcher(column.name):
qty = (
db.session.query(func.sum(getattr(model, column.name)))
.filter_by(**f)
.scalar()
)
summary.append({
"Description": label_fn(column.name),
"UOM": uom,
"Qty": float(qty or 0)
})
return summary
# ------------------------------------------------------------
def trench_summary(self):
return self._summary(
TrenchExcavationClient,
RegularExpression.STR_TOTAL_PATTERN.match,
"Cum",
_label_for_total_column
)
def manhole_summary(self):
return self._summary(
ManholeExcavationClient,
RegularExpression.STR_TOTAL_PATTERN.match,
"Cum",
_label_for_total_column
)
def domestic_summary(self):
return self._summary(
ManholeDomesticChamberClient,
RegularExpression.D_RANGE_PATTERN.match,
"Nos",
_label_for_d_range_column
)
def laying_summary(self):
return self._summary(
LayingClient,
RegularExpression.PIPE_MM_PATTERN.match,
"RM",
_label_for_pipe_column
)
# ------------------------------------------------------------
# EXCEL SHEET
# ------------------------------------------------------------
def generate(self, workbook):
worksheet = workbook.add_worksheet("Abstract")
title = workbook.add_format({
"bold": True, "font_size": 16, "align": "center",
"valign": "vcenter", "border": 1
})
heading = workbook.add_format({
"bold": True, "bg_color": "#D9EAD3", "border": 1, "align": "center"
})
cell = workbook.add_format({"border": 1})
number = workbook.add_format({"border": 1, "num_format": "#,##0.00"})
worksheet.merge_range("A1:D1", "ABSTRACT OF QUANTITY (CLIENT)", title)
worksheet.write("A3", "RA Bill No", heading)
worksheet.write("B3", self.ra_bill_no or "", cell)
worksheet.write_row("A5", ["Sr", "Description", "UOM", "Qty"], heading)
row = 5
sr = 1
sections = [
("TRENCH EXCAVATION", self.trench_summary()),
("MANHOLE EXCAVATION", self.manhole_summary()),
("DOMESTIC CHAMBER", self.domestic_summary()),
("PIPE LAYING", self.laying_summary()),
]
for title_text, rows in sections:
worksheet.write(row, 1, title_text, heading)
row += 1
for item in rows:
worksheet.write(row, 0, sr, cell)
worksheet.write(row, 1, item["Description"], cell)
worksheet.write(row, 2, item["UOM"], cell)
worksheet.write(row, 3, item["Qty"], number)
sr += 1
row += 1
worksheet.set_column("A:A", 8)
worksheet.set_column("B:B", 55)
worksheet.set_column("C:C", 10)
worksheet.set_column("D:D", 18)
# ------------------------------------------------------------
# HTML (for web preview)
# ------------------------------------------------------------
def generate_html(self):
html = """
<div class="table-responsive">
<table class="table table-bordered table-hover table-striped">
<thead class="table-success">
<tr>
<th colspan="4" class="text-center fs-4">
ABSTRACT OF QUANTITY
</th>
</tr>
<tr>
<th>RA Bill NO</th>
<td colspan="3">{}</td>
</tr>
<tr>
<th width="8%">Sr</th>
<th>Description</th>
<th width="10%">UOM</th>
<th width="15%">Qty</th>
</tr>
</thead>
<tbody>
""".format(self.ra_bill_no or "")
sr = 1
sections = [
("TRENCH EXCAVATION", self.trench_summary()),
("MANHOLE EXCAVATION", self.manhole_summary()),
("DOMESTIC CHAMBER", self.domestic_summary()),
("PIPE LAYING", self.laying_summary()),
]
for title_text, rows in sections:
html += f"""
<tr class="table-secondary fw-bold">
<td colspan="4">{title_text}</td>
</tr>
"""
for item in rows:
html += f"""
<tr>
<td>{sr}</td>
<td>{item['Description']}</td>
<td>{item['UOM']}</td>
<td class="text-end">{item['Qty']:.2f}</td>
</tr>
"""
sr += 1
html += """
</tbody>
</table>
</div>
"""
return html