MSU576 commited on
Commit
38a4bbc
·
verified ·
1 Parent(s): 0f7ab73

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -66
app.py CHANGED
@@ -1533,12 +1533,31 @@ def rag_ui():
1533
  # --------------------------
1534
  # REPORTS: PDF builders
1535
  # --------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1536
  def build_classification_pdf_bytes(site: dict):
1537
  """
1538
  Build classification-only PDF (returns bytes)
1539
  """
1540
  buf = io.BytesIO()
1541
- doc = SimpleDocTemplate(buf, pagesize=A4, leftMargin=20*mm, rightMargin=20*mm, topMargin=20*mm, bottomMargin=20*mm)
 
 
 
 
1542
  styles = getSampleStyleSheet()
1543
  title = ParagraphStyle("title", parent=styles["Title"], fontSize=20, textColor=THEME["accent"], alignment=1)
1544
  h1 = ParagraphStyle("h1", parent=styles["Heading1"], fontSize=14, textColor=THEME["accent"])
@@ -1546,46 +1565,60 @@ def build_classification_pdf_bytes(site: dict):
1546
 
1547
  elems = []
1548
  elems.append(Paragraph("Classification Report — GeoMate V2", title))
1549
- elems.append(Spacer(1,8))
1550
  elems.append(Paragraph(f"Site: {site.get('Site Name','-')}", h1))
1551
- elems.append(Spacer(1,6))
 
1552
 
1553
  # Inputs table
1554
  inputs = site.get("classifier_inputs", {})
1555
- data = [["Parameter","Value"]]
1556
- for k,v in inputs.items():
1557
- data.append([k, str(v)])
1558
- t = Table(data, colWidths=[80*mm, 80*mm])
1559
- t.setStyle(TableStyle([("GRID",(0,0),(-1,-1),0.5,colors.grey),
1560
- ("BACKGROUND",(0,0),(-1,0),THEME["accent"]),
1561
- ("TEXTCOLOR",(0,0),(-1,0),colors.white)]))
1562
- elems.append(t)
1563
- elems.append(Spacer(1,8))
 
 
 
1564
 
1565
  # Results
1566
  elems.append(Paragraph("Results", h1))
1567
  elems.append(Paragraph(f"USCS: {site.get('USCS','N/A')}", body))
1568
  elems.append(Paragraph(f"AASHTO: {site.get('AASHTO','N/A')} (GI: {site.get('GI','N/A')})", body))
1569
- elems.append(Spacer(1,8))
 
 
 
 
 
1570
 
1571
  # GSD curve inclusion if present
1572
  gsd = site.get("GSD")
1573
  if gsd:
1574
- elems.append(Paragraph("GSD Parameters", h1))
 
1575
  elems.append(Paragraph(f"D10: {gsd.get('D10')}, D30: {gsd.get('D30')}, D60: {gsd.get('D60')}", body))
1576
- # If a plot image is stored in site, include (we store last plot as /tmp/gsd_plot.png)
1577
  gsd_img_path = "/tmp/geomate_gsd_plot.png"
1578
  if os.path.exists(gsd_img_path):
1579
- elems.append(Spacer(1,6))
1580
  elems.append(RLImage(gsd_img_path, width=150*mm, height=80*mm))
1581
- elems.append(Spacer(1,10))
 
1582
  elems.append(Paragraph("Decision path", h1))
1583
  elems.append(Paragraph(site.get("classifier_decision_path","Not recorded"), body))
 
1584
  doc.build(elems)
1585
  pdf = buf.getvalue()
1586
  buf.close()
1587
  return pdf
1588
 
 
 
 
1589
  def build_full_geotech_pdf_bytes(sites_list: list, external_refs: list):
1590
  """
1591
  Build a full geotechnical report covering all selected sites.
@@ -1593,7 +1626,11 @@ def build_full_geotech_pdf_bytes(sites_list: list, external_refs: list):
1593
  Returns bytes of PDF.
1594
  """
1595
  buf = io.BytesIO()
1596
- doc = SimpleDocTemplate(buf, pagesize=A4, leftMargin=20*mm, rightMargin=20*mm, topMargin=20*mm, bottomMargin=20*mm)
 
 
 
 
1597
  styles = getSampleStyleSheet()
1598
  title = ParagraphStyle("title", parent=styles["Title"], fontSize=20, textColor=THEME["accent"], alignment=1)
1599
  h1 = ParagraphStyle("h1", parent=styles["Heading1"], fontSize=14, textColor=THEME["accent"])
@@ -1601,65 +1638,69 @@ def build_full_geotech_pdf_bytes(sites_list: list, external_refs: list):
1601
 
1602
  elems = []
1603
  elems.append(Paragraph("Full Geotechnical Investigation Report — GeoMate V2", title))
1604
- elems.append(Spacer(1,6))
1605
  elems.append(Paragraph(f"Date: {datetime.today().strftime('%Y-%m-%d')}", body))
1606
- elems.append(Spacer(1,10))
1607
 
1608
- # For each site: include summary, field data, lab results, GSD, map link (if snapshot exists)
1609
  for s in sites_list:
1610
  elems.append(Paragraph(f"Site: {s.get('Site Name','Unnamed')}", h1))
1611
- elems.append(Paragraph(f"Location: {s.get('Coordinates','Not provided')}", body))
1612
- elems.append(Spacer(1,6))
1613
- elems.append(Paragraph("1. Site Description & Geology", body))
1614
- elems.append(Paragraph(s.get("site_description","Not provided"), body))
1615
- elems.append(Spacer(1,6))
1616
-
1617
- elems.append(Paragraph("2. Field Investigation & Laboratory Testing", body))
1618
- # show available fields
1619
- lines = [
1620
- f"Load Bearing Capacity: {s.get('Load Bearing Capacity','Not provided')}",
1621
- f"Skin Shear Strength: {s.get('Skin Shear Strength','Not provided')}",
1622
- f"Relative Compaction: {s.get('Relative Compaction','Not provided')}",
1623
- f"Rate of Consolidation: {s.get('Rate of Consolidation','Not provided')}",
1624
- f"Nature of Construction: {s.get('Nature of Construction','Not provided')}"
1625
- ]
1626
- for L in lines:
1627
- elems.append(Paragraph(L, body))
1628
- elems.append(Spacer(1,8))
1629
-
1630
- # GSD & plot
 
1631
  gsd = s.get("GSD")
1632
  if gsd:
1633
- elems.append(Paragraph("3. Grain Size Distribution", body))
1634
- elems.append(Paragraph(f"D10: {gsd.get('D10')}, D30: {gsd.get('D30')}, D60: {gsd.get('D60')}, Cu: {gsd.get('Cu')}, Cc: {gsd.get('Cc')}", body))
1635
- # Include saved image path if exists
 
 
 
 
1636
  gsd_img = "/tmp/geomate_gsd_plot.png"
1637
  if os.path.exists(gsd_img):
1638
- elems.append(Spacer(1,6))
1639
  elems.append(RLImage(gsd_img, width=150*mm, height=80*mm))
1640
- elems.append(Spacer(1,8))
1641
-
1642
- # Earth Engine extracted data
1643
- elems.append(Paragraph("4. Locator-derived Data", body))
1644
- elems.append(Paragraph(f"Flood Data: {json.dumps(s.get('Flood Data','Not provided'))[:300]}", body))
1645
- elems.append(Paragraph(f"Seismic Data: {json.dumps(s.get('Seismic Data','Not provided'))[:300]}", body))
1646
- elems.append(Paragraph(f"Topography: {json.dumps(s.get('Topography','Not provided'))[:300]}", body))
1647
- elems.append(Spacer(1,8))
1648
-
1649
- # Recommendations (simple placeholder text derived from site properties)
1650
- elems.append(Paragraph("5. Recommendations (preliminary)", body))
1651
- # Basic logic to create recommendations
1652
- if s.get("USCS") and s.get("USCS").startswith("C"):
1653
- elems.append(Paragraph(" - Soils have clayey character; consider consolidation and settlement checks. Use stiffened raft or piles for heavy loads.", body))
1654
  else:
1655
- elems.append(Paragraph(" - Soils are likely granular; shallow foundations possible with suitable compaction and drainage.", body))
1656
 
1657
  elems.append(PageBreak())
1658
 
1659
- # External references
1660
- if external_refs:
 
 
 
 
 
 
 
1661
  elems.append(Paragraph("References", h1))
1662
- for r in external_refs:
1663
  elems.append(Paragraph(r, body))
1664
 
1665
  doc.build(elems)
@@ -1676,12 +1717,21 @@ def reports_ui():
1676
  # Classification-only
1677
  st.subheader("Classification-only report")
1678
  sites = ss.get("sites", [])
 
 
 
 
1679
  site_names = [s.get("Site Name","Unnamed") for s in sites]
1680
  sel_cls = st.selectbox("Select site", site_names, index=ss.get("active_site_idx",0))
1681
  if st.button("Generate Classification PDF"):
1682
  site = ss["sites"][site_names.index(sel_cls)]
1683
  pdf_bytes = build_classification_pdf_bytes(site)
1684
- st.download_button("Download Classification PDF", data=pdf_bytes, file_name=f"classification_{sel_cls}.pdf", mime="application/pdf")
 
 
 
 
 
1685
 
1686
  st.markdown("---")
1687
 
@@ -1697,8 +1747,13 @@ def reports_ui():
1697
  ext_refs = [l.strip() for l in ext_refs_text.splitlines() if l.strip()]
1698
  with st.spinner("Building PDF (this may take a few seconds)..."):
1699
  pdf_bytes = build_full_geotech_pdf_bytes(chosen_sites, ext_refs)
1700
- st.download_button("Download Full Geotechnical Report", data=pdf_bytes, file_name="geomate_full_report.pdf", mime="application/pdf")
1701
-
 
 
 
 
 
1702
  # --------------------------
1703
  # Final UI main function (glue)
1704
  # --------------------------
 
1533
  # --------------------------
1534
  # REPORTS: PDF builders
1535
  # --------------------------
1536
+ import io, os, json
1537
+ from datetime import datetime
1538
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Image as RLImage
1539
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
1540
+ from reportlab.lib.pagesizes import A4
1541
+ from reportlab.lib.units import mm
1542
+ from reportlab.lib import colors
1543
+ import streamlit as st
1544
+
1545
+ # Theme colors
1546
+ THEME = {"accent": colors.HexColor("#FF6600")}
1547
+
1548
+ # --------------------------
1549
+ # CLASSIFICATION REPORT BUILDER
1550
+ # --------------------------
1551
  def build_classification_pdf_bytes(site: dict):
1552
  """
1553
  Build classification-only PDF (returns bytes)
1554
  """
1555
  buf = io.BytesIO()
1556
+ doc = SimpleDocTemplate(
1557
+ buf, pagesize=A4,
1558
+ leftMargin=20*mm, rightMargin=20*mm,
1559
+ topMargin=20*mm, bottomMargin=20*mm
1560
+ )
1561
  styles = getSampleStyleSheet()
1562
  title = ParagraphStyle("title", parent=styles["Title"], fontSize=20, textColor=THEME["accent"], alignment=1)
1563
  h1 = ParagraphStyle("h1", parent=styles["Heading1"], fontSize=14, textColor=THEME["accent"])
 
1565
 
1566
  elems = []
1567
  elems.append(Paragraph("Classification Report — GeoMate V2", title))
1568
+ elems.append(Spacer(1, 8))
1569
  elems.append(Paragraph(f"Site: {site.get('Site Name','-')}", h1))
1570
+ elems.append(Paragraph(f"Coordinates: {site.get('Coordinates','-')}", body))
1571
+ elems.append(Spacer(1, 6))
1572
 
1573
  # Inputs table
1574
  inputs = site.get("classifier_inputs", {})
1575
+ if inputs:
1576
+ data = [["Parameter", "Value"]]
1577
+ for k, v in inputs.items():
1578
+ data.append([k, str(v)])
1579
+ t = Table(data, colWidths=[80*mm, 80*mm])
1580
+ t.setStyle(TableStyle([
1581
+ ("GRID", (0,0), (-1,-1), 0.5, colors.grey),
1582
+ ("BACKGROUND", (0,0), (-1,0), THEME["accent"]),
1583
+ ("TEXTCOLOR", (0,0), (-1,0), colors.white)
1584
+ ]))
1585
+ elems.append(t)
1586
+ elems.append(Spacer(1, 8))
1587
 
1588
  # Results
1589
  elems.append(Paragraph("Results", h1))
1590
  elems.append(Paragraph(f"USCS: {site.get('USCS','N/A')}", body))
1591
  elems.append(Paragraph(f"AASHTO: {site.get('AASHTO','N/A')} (GI: {site.get('GI','N/A')})", body))
1592
+
1593
+ # OCR data inclusion
1594
+ if site.get("ocr_text"):
1595
+ elems.append(Spacer(1, 8))
1596
+ elems.append(Paragraph("OCR Extracted Notes", h1))
1597
+ elems.append(Paragraph(site.get("ocr_text","No OCR data found."), body))
1598
 
1599
  # GSD curve inclusion if present
1600
  gsd = site.get("GSD")
1601
  if gsd:
1602
+ elems.append(Spacer(1, 8))
1603
+ elems.append(Paragraph("Grain Size Distribution (GSD)", h1))
1604
  elems.append(Paragraph(f"D10: {gsd.get('D10')}, D30: {gsd.get('D30')}, D60: {gsd.get('D60')}", body))
 
1605
  gsd_img_path = "/tmp/geomate_gsd_plot.png"
1606
  if os.path.exists(gsd_img_path):
1607
+ elems.append(Spacer(1, 6))
1608
  elems.append(RLImage(gsd_img_path, width=150*mm, height=80*mm))
1609
+
1610
+ elems.append(Spacer(1, 10))
1611
  elems.append(Paragraph("Decision path", h1))
1612
  elems.append(Paragraph(site.get("classifier_decision_path","Not recorded"), body))
1613
+
1614
  doc.build(elems)
1615
  pdf = buf.getvalue()
1616
  buf.close()
1617
  return pdf
1618
 
1619
+ # --------------------------
1620
+ # FULL REPORT BUILDER
1621
+ # --------------------------
1622
  def build_full_geotech_pdf_bytes(sites_list: list, external_refs: list):
1623
  """
1624
  Build a full geotechnical report covering all selected sites.
 
1626
  Returns bytes of PDF.
1627
  """
1628
  buf = io.BytesIO()
1629
+ doc = SimpleDocTemplate(
1630
+ buf, pagesize=A4,
1631
+ leftMargin=20*mm, rightMargin=20*mm,
1632
+ topMargin=20*mm, bottomMargin=20*mm
1633
+ )
1634
  styles = getSampleStyleSheet()
1635
  title = ParagraphStyle("title", parent=styles["Title"], fontSize=20, textColor=THEME["accent"], alignment=1)
1636
  h1 = ParagraphStyle("h1", parent=styles["Heading1"], fontSize=14, textColor=THEME["accent"])
 
1638
 
1639
  elems = []
1640
  elems.append(Paragraph("Full Geotechnical Investigation Report — GeoMate V2", title))
1641
+ elems.append(Spacer(1, 6))
1642
  elems.append(Paragraph(f"Date: {datetime.today().strftime('%Y-%m-%d')}", body))
1643
+ elems.append(Spacer(1, 10))
1644
 
1645
+ # For each site
1646
  for s in sites_list:
1647
  elems.append(Paragraph(f"Site: {s.get('Site Name','Unnamed')}", h1))
1648
+ elems.append(Paragraph(f"Coordinates: {s.get('Coordinates','Not provided')}", body))
1649
+ elems.append(Spacer(1, 6))
1650
+
1651
+ # OCR notes
1652
+ if s.get("ocr_text"):
1653
+ elems.append(Paragraph("OCR Extracted Notes", h1))
1654
+ elems.append(Paragraph(s.get("ocr_text"), body))
1655
+ elems.append(Spacer(1, 6))
1656
+
1657
+ # Classification
1658
+ elems.append(Paragraph("Classification", h1))
1659
+ elems.append(Paragraph(f"USCS: {s.get('USCS','N/A')}", body))
1660
+ elems.append(Paragraph(f"AASHTO: {s.get('AASHTO','N/A')} (GI: {s.get('GI','N/A')})", body))
1661
+
1662
+ # Earth Engine / Map snapshots
1663
+ if s.get("map_snapshot") and os.path.exists(s["map_snapshot"]):
1664
+ elems.append(Spacer(1, 6))
1665
+ elems.append(Paragraph("Site Map Snapshot", h1))
1666
+ elems.append(RLImage(s["map_snapshot"], width=140*mm, height=80*mm))
1667
+
1668
+ # GSD
1669
  gsd = s.get("GSD")
1670
  if gsd:
1671
+ elems.append(Spacer(1, 6))
1672
+ elems.append(Paragraph("Grain Size Distribution", h1))
1673
+ elems.append(Paragraph(
1674
+ f"D10: {gsd.get('D10')}, D30: {gsd.get('D30')}, "
1675
+ f"D60: {gsd.get('D60')}, Cu: {gsd.get('Cu')}, Cc: {gsd.get('Cc')}",
1676
+ body
1677
+ ))
1678
  gsd_img = "/tmp/geomate_gsd_plot.png"
1679
  if os.path.exists(gsd_img):
1680
+ elems.append(Spacer(1, 6))
1681
  elems.append(RLImage(gsd_img, width=150*mm, height=80*mm))
1682
+
1683
+ # Recommendations (basic rules from classification)
1684
+ elems.append(Spacer(1, 8))
1685
+ elems.append(Paragraph("Recommendations", h1))
1686
+ if s.get("USCS") and s["USCS"].startswith("C"):
1687
+ elems.append(Paragraph(" - Clayey soils: check consolidation/settlement. Consider raft or pile foundations.", body))
 
 
 
 
 
 
 
 
1688
  else:
1689
+ elems.append(Paragraph(" - Granular soils: shallow foundations possible with compaction and drainage.", body))
1690
 
1691
  elems.append(PageBreak())
1692
 
1693
+ # References (FAISS RAG + manual)
1694
+ refs = []
1695
+ for s in sites_list:
1696
+ if s.get("rag_sources"):
1697
+ refs.extend(s["rag_sources"])
1698
+ refs = list(set(refs)) # remove duplicates
1699
+ refs.extend(external_refs)
1700
+
1701
+ if refs:
1702
  elems.append(Paragraph("References", h1))
1703
+ for r in refs:
1704
  elems.append(Paragraph(r, body))
1705
 
1706
  doc.build(elems)
 
1717
  # Classification-only
1718
  st.subheader("Classification-only report")
1719
  sites = ss.get("sites", [])
1720
+ if not sites:
1721
+ st.warning("No sites available.")
1722
+ return
1723
+
1724
  site_names = [s.get("Site Name","Unnamed") for s in sites]
1725
  sel_cls = st.selectbox("Select site", site_names, index=ss.get("active_site_idx",0))
1726
  if st.button("Generate Classification PDF"):
1727
  site = ss["sites"][site_names.index(sel_cls)]
1728
  pdf_bytes = build_classification_pdf_bytes(site)
1729
+ st.download_button(
1730
+ "Download Classification PDF",
1731
+ data=pdf_bytes,
1732
+ file_name=f"classification_{sel_cls}.pdf",
1733
+ mime="application/pdf"
1734
+ )
1735
 
1736
  st.markdown("---")
1737
 
 
1747
  ext_refs = [l.strip() for l in ext_refs_text.splitlines() if l.strip()]
1748
  with st.spinner("Building PDF (this may take a few seconds)..."):
1749
  pdf_bytes = build_full_geotech_pdf_bytes(chosen_sites, ext_refs)
1750
+ st.download_button(
1751
+ "Download Full Geotechnical Report",
1752
+ data=pdf_bytes,
1753
+ file_name="geomate_full_report.pdf",
1754
+ mime="application/pdf"
1755
+ )
1756
+
1757
  # --------------------------
1758
  # Final UI main function (glue)
1759
  # --------------------------