Download pages/3_Termite_Control_Report.py from sterdil/wercore: direct link, hf CLI and curl.
- Browser
- Download file 12 kB
-
https://huggingface.co/spaces/sterdil/wercore/resolve/main/pages/3_Termite_Control_Report.py
- Command line
-
hf download hf://spaces/sterdil/wercore/pages/3_Termite_Control_Report.py
-
curl -L -o 3_Termite_Control_Report.py https://huggingface.co/spaces/sterdil/wercore/resolve/main/pages/3_Termite_Control_Report.py
12 kB
| import streamlit as st | |
| import io, math, os, tempfile, subprocess | |
| from PIL import Image, ImageDraw, ImageFont, ImageOps | |
| from docxtpl import DocxTemplate | |
| from pypdf import PdfWriter, PdfReader | |
| from datetime import date | |
| from dateutil.relativedelta import relativedelta | |
| # ββ 1. CONFIG & ASSETS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.set_page_config(page_title="Wercore | Anti Termite Engine", page_icon="π", layout="wide") | |
| st.markdown(""" | |
| <style> | |
| [data-testid="stLogo"] { height: 6rem !important; object-fit: contain !important; } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| st.logo("WERCORE logo.png") | |
| if not st.session_state.get("authenticated", False): | |
| st.error("π **Access Denied.** Please log in through the main Wercore Portal.") | |
| st.stop() | |
| TPL_TERMITE = "Inspection_Template_Termite.docx" | |
| TPL_WARRANTY = "Warranty_Template_Termite.docx" | |
| def convert_to_pdf(docx_path, output_dir): | |
| subprocess.run(["libreoffice", "--headless", "--convert-to", "pdf", docx_path, "--outdir", output_dir], check=True) | |
| def load_font(size): | |
| try: return ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", size) | |
| except: return ImageFont.load_default() | |
| def build_photo_grid_pages(all_photos, photos_per_page=4): | |
| n = math.ceil(len(all_photos) / photos_per_page) | |
| return [all_photos[i * photos_per_page:(i + 1) * photos_per_page] for i in range(n)] | |
| def photo_grid_to_pdf_bytes(page_photos, page_num, total_pages, client_name, date_str): | |
| PAGE_W, PAGE_H = 1240, 1754 | |
| PADDING, HEADER_H, FOOTER_H, CELL_GAP = 40, 70, 40, 20 | |
| canvas = Image.new("RGB", (PAGE_W, PAGE_H), (255, 255, 255)) | |
| draw = ImageDraw.Draw(canvas) | |
| font_header, font_footer, font_wm = load_font(28), load_font(20), load_font(24) | |
| draw.rectangle([(0, 0), (PAGE_W, HEADER_H)], fill=(0, 84, 166)) | |
| draw.text((PADDING, 18), f"Photo Documentation β {client_name} | {date_str}", font=font_header, fill=(255, 255, 255)) | |
| grid_top = HEADER_H + PADDING | |
| grid_bot = PAGE_H - FOOTER_H - PADDING | |
| cell_w = (PAGE_W - 2 * PADDING - CELL_GAP) // 2 | |
| cell_h = (grid_bot - grid_top - CELL_GAP) // 2 | |
| for idx, photo in enumerate(page_photos): | |
| col, row = idx % 2, idx // 2 | |
| x0 = PADDING + col * (cell_w + CELL_GAP) | |
| y0 = grid_top + row * (cell_h + CELL_GAP) | |
| img = Image.open(io.BytesIO(photo)).convert("RGB") | |
| img = ImageOps.exif_transpose(img) | |
| img = ImageOps.fit(img, (cell_w, cell_h), Image.LANCZOS) | |
| canvas.paste(img, (x0, y0)) | |
| wm_margin = 15 | |
| bbox = draw.textbbox((0, 0), date_str, font=font_wm) | |
| wm_x = x0 + cell_w - (bbox[2] - bbox[0]) - wm_margin | |
| wm_y = y0 + cell_h - (bbox[3] - bbox[1]) - wm_margin | |
| draw.text((wm_x + 2, wm_y + 2), date_str, font=font_wm, fill=(0, 0, 0, 180)) | |
| draw.text((wm_x, wm_y), date_str, font=font_wm, fill=(255, 255, 255, 230)) | |
| draw.text((PADDING, PAGE_H - FOOTER_H + 8), "CONFIDENTIAL β Anti Termite Compliance Report", font=font_footer, fill=(150, 150, 150)) | |
| draw.text((PAGE_W - 180, PAGE_H - FOOTER_H + 8), f"Page {page_num} of {total_pages}", font=font_footer, fill=(150, 150, 150)) | |
| buf = io.BytesIO() | |
| canvas.save(buf, format="PDF") | |
| return buf.getvalue() | |
| # ββ 2. UI: SIDEBAR ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| with st.sidebar: | |
| st.markdown("### Project & Warranty Details") | |
| client_name = st.text_input("π’ Client Name*", placeholder="e.g. AL GHURAIR PROPERTIES") | |
| project_location = st.text_input("π Project Location*", placeholder="e.g. Muteena 4428, Deira, Dubai") | |
| quotation_ref = st.text_input("π Quotation Reference*", placeholder="e.g. QTN/WRCR/TRM/09/2025/2813") | |
| st.markdown("---") | |
| service_date = st.date_input("π Date of Service*", value=date.today(), format="DD/MM/YYYY") | |
| date_str = service_date.strftime("%d-%m-%Y") | |
| # Auto-calculate standard 5-year warranty | |
| default_warranty_end = service_date + relativedelta(years=5) - relativedelta(days=1) | |
| override_warranty = st.checkbox("Custom Warranty Period?") | |
| if override_warranty: | |
| warranty_end_date = st.date_input("π Warranty End Date", value=default_warranty_end, format="DD/MM/YYYY") | |
| else: | |
| st.info(f"5-Year Warranty Expiry: **{default_warranty_end.strftime('%d-%m-%Y')}**") | |
| warranty_end_date = default_warranty_end | |
| warranty_end_str = warranty_end_date.strftime("%d-%m-%Y") | |
| st.markdown("---") | |
| handwritten_files = st.file_uploader("Upload physical reports (PDFs only - Max 5MB)*", type=["pdf", "PDF"], accept_multiple_files=True) | |
| # ββ 3. UI: DATA ENTRY βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| st.header("Anti Termite Report Maker") | |
| st.caption("Compliance, Inspection & Warranty Documentation") | |
| st.markdown("### Treatment Details") | |
| col_t1, col_t2 = st.columns(2) | |
| with col_t1: | |
| app_method = st.text_input("Application Method", value="Drilling, injection") | |
| spray_internal = st.text_area("Spray Treatment (Internal)", value="Targeted spray application of termiticide on wall areas") | |
| drill_injection = st.text_area("Drilling and Injection", value="Drill-Fill-Seal techniques in immediate external perimeter of the building") | |
| with col_t2: | |
| cov_internal = st.text_input("Internal Coverage Area", placeholder="e.g. Basement parking area") | |
| cov_external = st.text_input("External Coverage Area", value="Immediate surrounding of entire building") | |
| product_details = st.text_area("Product Details", value="IMIFORCE 20SC (Imidacloprid 20% W/V%, Other Ingredients 80%)\nApproved by Dubai Municipality") | |
| st.markdown("### Observations & Recommendations") | |
| col_o1, col_o2 = st.columns(2) | |
| with col_o1: | |
| infestation_level = st.text_input("Infestation Level", placeholder="e.g. Severe infestation in basement parking area") | |
| obs_external = st.text_area("External Observations", value="No visible signs of external infestation or termite activity around the building outer walls were noticed at the time of service.") | |
| with col_o2: | |
| recommendations = st.text_area("Recommendations", value="Due to the severe level of infestation, we strongly recommend a repeat service after 5 years in the same areas to ensure continued protection and prevent recurrence of subterranean termite activity.", height=110) | |
| st.markdown("### πΈ Site Photographs") | |
| uploaded_photos = st.file_uploader("Upload up to 20+ images (Max 2MB each)", type=["jpg","jpeg","png"], accept_multiple_files=True) | |
| st.markdown("---") | |
| # ββ 4. ENGINE: MASTER PDF ASSEMBLY ββββββββββββββββββββββββββββββββββββββββββββ | |
| notification_zone = st.empty() | |
| if st.button("π Generate Anti Termite Dossier", type="primary", use_container_width=True): | |
| errors = [] | |
| if not client_name: errors.append("Client Name is missing.") | |
| if not project_location: errors.append("Project Location is missing.") | |
| if not quotation_ref: errors.append("Quotation Reference is missing.") | |
| if not cov_internal: errors.append("Internal Coverage Area is missing.") | |
| if not infestation_level: errors.append("Infestation Level is missing.") | |
| if not handwritten_files: errors.append("At least one Physical Report PDF must be uploaded.") | |
| if handwritten_files: | |
| for f in handwritten_files: | |
| if f.size > 5 * 1024 * 1024: errors.append(f"Physical report '{f.name}' exceeds the 5MB limit.") | |
| if uploaded_photos: | |
| for f in uploaded_photos: | |
| if f.size > 2 * 1024 * 1024: errors.append(f"Photo '{f.name}' exceeds 2MB.") | |
| if errors: | |
| with notification_zone.container(): | |
| st.error("β οΈ **Cannot generate report. Please fix the following issues:**") | |
| for e in errors: st.error(f"β {e}") | |
| st.stop() | |
| with notification_zone.container(): | |
| with st.spinner("βοΈ Assembling Termite Dossier & Warranty... Please wait."): | |
| with tempfile.TemporaryDirectory() as tmp_dir: | |
| doc_context = { | |
| "client_name": client_name, | |
| "project_location": project_location, | |
| "quotation_ref": quotation_ref, | |
| "date_str": date_str, | |
| "app_method": app_method, | |
| "spray_internal": spray_internal, | |
| "drill_injection": drill_injection, | |
| "cov_internal": cov_internal, | |
| "cov_external": cov_external, | |
| "product_details": product_details, | |
| "infestation_level": infestation_level, | |
| "obs_external": obs_external, | |
| "recommendations": recommendations, | |
| "warranty_start": date_str, | |
| "warranty_end": warranty_end_str | |
| } | |
| # 1. Render Main Report | |
| if os.path.exists(TPL_TERMITE): | |
| path_report = os.path.join(tmp_dir, "termite_report.docx") | |
| doc = DocxTemplate(TPL_TERMITE) | |
| doc.render(doc_context) | |
| doc.save(path_report) | |
| convert_to_pdf(path_report, tmp_dir) | |
| else: | |
| st.error(f"Template '{TPL_TERMITE}' not found in repository.") | |
| st.stop() | |
| # 2. Render Warranty Certificate | |
| if os.path.exists(TPL_WARRANTY): | |
| path_warr = os.path.join(tmp_dir, "termite_warranty.docx") | |
| doc_warr = DocxTemplate(TPL_WARRANTY) | |
| doc_warr.render(doc_context) | |
| doc_warr.save(path_warr) | |
| convert_to_pdf(path_warr, tmp_dir) | |
| else: | |
| st.error(f"Template '{TPL_WARRANTY}' not found in repository.") | |
| st.stop() | |
| # 3. Process Photos into Grid | |
| grid_pdfs = [] | |
| if uploaded_photos: | |
| photo_bytes = [f.read() for f in uploaded_photos] | |
| photo_pages = build_photo_grid_pages(photo_bytes, 4) | |
| for idx, pg in enumerate(photo_pages): | |
| grid_pdfs.append(photo_grid_to_pdf_bytes(pg, idx+1, len(photo_pages), client_name, date_str)) | |
| # 4. Staple Everything Together | |
| writer = PdfWriter() | |
| def staple_pdf(file_source): | |
| try: | |
| reader = PdfReader(file_source) | |
| for page in reader.pages: writer.add_page(page) | |
| except Exception: pass | |
| # Document Assembly Order | |
| staple_pdf(os.path.join(tmp_dir, "termite_report.pdf")) # Main Report | |
| for pdf_bytes in grid_pdfs: staple_pdf(io.BytesIO(pdf_bytes)) # Photos | |
| for hw_pdf in handwritten_files: | |
| hw_pdf.seek(0) | |
| staple_pdf(hw_pdf) # Scanned Service Sheets | |
| staple_pdf(os.path.join(tmp_dir, "termite_warranty.pdf")) # Warranty Certificate at the very end | |
| output = io.BytesIO() | |
| writer.write(output) | |
| st.success("β Anti Termite Report & Warranty generated successfully!") | |
| st.download_button("β¬οΈ Download Master PDF", output.getvalue(), f"Anti_Termite_{client_name.replace(' ', '_')}.pdf", "application/pdf", type="primary", use_container_width=True) |