MSU576 commited on
Commit
98c10c3
Β·
verified Β·
1 Parent(s): 534aca5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -14
app.py CHANGED
@@ -23,6 +23,13 @@ from reportlab.lib.units import mm
23
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage, PageBreak
24
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
25
 
 
 
 
 
 
 
 
26
  # Optional imports handled gracefully
27
  try:
28
  import geemap
@@ -485,6 +492,71 @@ def build_full_geotech_pdf(site: Dict[str, Any], filename: str, include_map_imag
485
  bullet = ParagraphStyle("bullet", parent=body, leftIndent=12, bulletIndent=6)
486
  doc = SimpleDocTemplate(filename, pagesize=A4, leftMargin=18*mm, rightMargin=18*mm, topMargin=18*mm, bottomMargin=18*mm)
487
  elems = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
488
  # Cover
489
  elems.append(Paragraph("GEOTECHNICAL INVESTIGATION REPORT", title_style))
490
  elems.append(Spacer(1,6))
@@ -839,6 +911,31 @@ with st.sidebar:
839
 
840
  # 7) Pages implementation
841
  def landing_page():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
842
  st.markdown("<div style='display:flex;align-items:center;gap:12px'>"
843
  "<div style='width:76px;height:76px;border-radius:14px;background:linear-gradient(135deg,#ff7a00,#ff3a3a);display:flex;align-items:center;justify-content:center;box-shadow:0 8px 24px rgba(0,0,0,0.6)'>"
844
  "<span style='font-size:34px'>πŸ›°οΈ</span></div>"
@@ -1386,7 +1483,17 @@ def locator_page():
1386
  pass
1387
 
1388
  # Enable drawing
1389
- m.add_draw_control(polyline=False, circle=False, circlemarker=False, rectangle=True, polygon=True)
 
 
 
 
 
 
 
 
 
 
1390
  m.to_streamlit(height=700, responsive=True)
1391
  st.markdown("πŸ‘‰ Draw a polygon/rectangle on the map (draw tool). After drawing click **Compute Summaries**.")
1392
 
@@ -1690,19 +1797,19 @@ def rag_page():
1690
  site["Relative Compaction"] = f"{val} {unit}"
1691
  st.success("Response saved and any recognized numeric fields auto-stored in the site data.")
1692
 
1693
- # Reports page β€” conversational missing-parameter bot & PDF generation
1694
- REPORT_FIELDS = [
1695
- ("Load Bearing Capacity","kPa or psf"),
1696
- ("Skin Shear Strength","kPa"),
1697
- ("Relative Compaction","%"),
1698
- ("Rate of Consolidation","mm/yr or days"),
1699
- ("Nature of Construction","text"),
1700
- ("Borehole Count","number"),
1701
- ("Max Depth (m)","m"),
1702
- ("SPT N (avg)","blows/ft"),
1703
- ("CBR (%)","%"),
1704
- ("Allowable Bearing (kPa)","kPa")
1705
- ]
1706
 
1707
  def reports_page():
1708
  st.header("πŸ“‘ Reports β€” Classification & Full Geotechnical")
@@ -1729,6 +1836,77 @@ def reports_page():
1729
  else:
1730
  st.info("No classification saved for this site yet. Use the Classifier page.")
1731
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1732
  st.markdown("---")
1733
  st.subheader("Full Geotechnical Report (chatbot will gather missing fields)")
1734
  if st.button("Start Report Chatbot"):
 
23
  from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image as RLImage, PageBreak
24
  from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
25
 
26
+ # Ensure icon fonts load (fix desktop icon display for option_menu)
27
+ st.markdown("""
28
+ <!-- Load icon fonts used by streamlit_option_menu -->
29
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css">
30
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
31
+ """, unsafe_allow_html=True)
32
+
33
  # Optional imports handled gracefully
34
  try:
35
  import geemap
 
492
  bullet = ParagraphStyle("bullet", parent=body, leftIndent=12, bulletIndent=6)
493
  doc = SimpleDocTemplate(filename, pagesize=A4, leftMargin=18*mm, rightMargin=18*mm, topMargin=18*mm, bottomMargin=18*mm)
494
  elems = []
495
+ # --- Title page (collect company/user info from site if present) ---
496
+ elems.append(Paragraph("GEOTECHNICAL INVESTIGATION REPORT", title_style))
497
+ elems.append(Spacer(1,12))
498
+ # Company / client block (if present)
499
+ company = site.get("Company Name", "Client / Company: Not provided")
500
+ contact = site.get("Company Contact", "")
501
+ elems.append(Paragraph(f"<b>{company}</b>", body))
502
+ if contact:
503
+ elems.append(Paragraph(contact, body))
504
+ elems.append(Spacer(1,12))
505
+ elems.append(Paragraph(f"<b>Project:</b> {site.get('Project Name','-')}", body))
506
+ elems.append(Paragraph(f"<b>Site:</b> {site.get('Site Name','-')}", body))
507
+ elems.append(Paragraph(f"<b>Date:</b> {datetime.today().strftime('%Y-%m-%d')}", body))
508
+ elems.append(PageBreak())
509
+
510
+ # --- Table of contents (simple listing with section titles) ---
511
+ elems.append(Paragraph("TABLE OF CONTENTS", h1))
512
+ toc_items = [
513
+ "1.0 Introduction",
514
+ "2.0 Site description and geology",
515
+ "3.0 Field investigation & laboratory testing",
516
+ "4.0 Evaluation of geotechnical properties",
517
+ "5.0 Provisional site classification",
518
+ "6.0 Recommendations",
519
+ "7.0 Figures & Tables",
520
+ "8.0 Appendices & References"
521
+ ]
522
+ for i, t in enumerate(toc_items, start=1):
523
+ elems.append(Paragraph(f"{i}. {t}", body))
524
+ elems.append(PageBreak())
525
+
526
+ # --- (existing summary and subsequent sections as before) ---
527
+ elems.append(Paragraph("SUMMARY", h1))
528
+ ...
529
+ # after '6.0 RECOMMENDATIONS' insertion, include LLM analysis if present
530
+ elems.append(Paragraph("7.0 LLM Analysis (GeoMate)", h1))
531
+ llm_text = site.get("LLM_Report_Text", None)
532
+ if llm_text:
533
+ elems.append(Paragraph(llm_text.replace("\n","\n\n"), body))
534
+ else:
535
+ elems.append(Paragraph("No LLM analysis saved for this site.", body))
536
+
537
+ # --- Figures/Tables index (simple lists from site data, if present) ---
538
+ elems.append(PageBreak())
539
+ elems.append(Paragraph("List of Tables & Figures", h1))
540
+ # Example: list lab samples and any GSD figure
541
+ if site.get("Laboratory Results"):
542
+ elems.append(Paragraph("Tables:", body))
543
+ for r in site.get("Laboratory Results", []):
544
+ elems.append(Paragraph(f"- Laboratory sample: {r.get('sampleId','-')}", body))
545
+ if site.get("GSD"):
546
+ elems.append(Paragraph("- GSD Curve (see section 3)", body))
547
+
548
+ # --- Appendices & References ---
549
+ elems.append(PageBreak())
550
+ elems.append(Paragraph("Appendices", h1))
551
+ elems.append(Paragraph("A. Test certificates and raw data (provided as CSV or appendices).", body))
552
+ elems.append(PageBreak())
553
+ elems.append(Paragraph("References", h1))
554
+ if ext_refs:
555
+ for r in ext_refs:
556
+ elems.append(Paragraph(f"- {r}", body))
557
+ else:
558
+ elems.append(Paragraph("- No external references provided.", body))
559
+
560
  # Cover
561
  elems.append(Paragraph("GEOTECHNICAL INVESTIGATION REPORT", title_style))
562
  elems.append(Spacer(1,6))
 
911
 
912
  # 7) Pages implementation
913
  def landing_page():
914
+ def landing_page():
915
+ # Background hero with placeholder image (replace BACKGROUND_URL with your image path or URL)
916
+ BACKGROUND_URL = "/app/background_placeholder.jpg" # <- replace this (or provide URL)
917
+ st.markdown(f"""
918
+ <div style="
919
+ background-image: url('{BACKGROUND_URL}');
920
+ background-size: cover;
921
+ background-position: center;
922
+ padding: 48px 28px;
923
+ border-radius: 12px;
924
+ margin-bottom: 18px;
925
+ position: relative;
926
+ ">
927
+ <div style="background: rgba(11,11,11,0.55); padding:22px; border-radius:10px; max-width:900px;">
928
+ <h1 style='color:#FF8C00; margin:0'>GeoMate V2</h1>
929
+ <p style='color:#e8eef6; margin:6px 0 0; font-size:16px'>
930
+ AI geotechnical copilot β€” soil recognition, classification, locator (EE), RAG-powered Q&A, and dynamic reports.
931
+ </p>
932
+ <div style='margin-top:8px; color:#cfcfcf; font-size:13px'>
933
+ Quick: Classifier β€’ GSD β€’ Locator β€’ RAG β€’ Reports
934
+ </div>
935
+ </div>
936
+ </div>
937
+ """, unsafe_allow_html=True)
938
+
939
  st.markdown("<div style='display:flex;align-items:center;gap:12px'>"
940
  "<div style='width:76px;height:76px;border-radius:14px;background:linear-gradient(135deg,#ff7a00,#ff3a3a);display:flex;align-items:center;justify-content:center;box-shadow:0 8px 24px rgba(0,0,0,0.6)'>"
941
  "<span style='font-size:34px'>πŸ›°οΈ</span></div>"
 
1483
  pass
1484
 
1485
  # Enable drawing
1486
+ from geemap import Draw
1487
+ # Add draw control (rectangle + polygon only)
1488
+ draw_control = Draw(
1489
+ export=False,
1490
+ draw_polygon=True,
1491
+ draw_rectangle=True,
1492
+ draw_circle=False,
1493
+ draw_polyline=False,
1494
+ draw_marker=False
1495
+ )
1496
+ m.add_control(draw_control)
1497
  m.to_streamlit(height=700, responsive=True)
1498
  st.markdown("πŸ‘‰ Draw a polygon/rectangle on the map (draw tool). After drawing click **Compute Summaries**.")
1499
 
 
1797
  site["Relative Compaction"] = f"{val} {unit}"
1798
  st.success("Response saved and any recognized numeric fields auto-stored in the site data.")
1799
 
1800
+ # Reports page β€” conversational missing-parameter bot & PDF generation
1801
+ REPORT_FIELDS = [
1802
+ ("Load Bearing Capacity","kPa or psf"),
1803
+ ("Skin Shear Strength","kPa"),
1804
+ ("Relative Compaction","%"),
1805
+ ("Rate of Consolidation","mm/yr or days"),
1806
+ ("Nature of Construction","text"),
1807
+ ("Borehole Count","number"),
1808
+ ("Max Depth (m)","m"),
1809
+ ("SPT N (avg)","blows/ft"),
1810
+ ("CBR (%)","%"),
1811
+ ("Allowable Bearing (kPa)","kPa")
1812
+ ]
1813
 
1814
  def reports_page():
1815
  st.header("πŸ“‘ Reports β€” Classification & Full Geotechnical")
 
1836
  else:
1837
  st.info("No classification saved for this site yet. Use the Classifier page.")
1838
 
1839
+ # --------- Dynamic form for quick report inputs + LLM analysis ----------
1840
+ st.markdown("### Quick report form (edit values and request LLM analysis)")
1841
+ site = st.session_state["sites"][st.session_state["active_site"]]
1842
+
1843
+ # Build form-style table
1844
+ with st.form(key="report_quick_form"):
1845
+ cols = st.columns([2,1,1]) # name, value, unit/notes
1846
+ # header row visually
1847
+ cols[0].markdown("**Parameter**")
1848
+ cols[1].markdown("**Value**")
1849
+ cols[2].markdown("**Unit / Notes**")
1850
+
1851
+ # build inputs dynamically from REPORT_FIELDS
1852
+ inputs = {}
1853
+ for (fld, unit) in REPORT_FIELDS:
1854
+ c1, c2, c3 = st.columns([2,1,1])
1855
+ c1.markdown(f"**{fld}**")
1856
+ default_val = site.get(fld, "")
1857
+ inputs[fld] = c2.text_input(fld, value=str(default_val), key=f"quick_{fld}")
1858
+ c3.markdown(unit)
1859
+
1860
+ submitted = st.form_submit_button("Save values to site")
1861
+ if submitted:
1862
+ for fld, _ in REPORT_FIELDS:
1863
+ val = inputs.get(fld, "").strip()
1864
+ site[fld] = val if val != "" else "Not provided"
1865
+ st.success("Saved quick report values to active site.")
1866
+
1867
+ # LLM analysis button
1868
+ st.markdown("#### LLM-powered analysis")
1869
+ if st.button("Ask GeoMate (generate analysis & recommendations)"):
1870
+ # prepare context for the LLM from the site
1871
+ context = {
1872
+ "site_name": site.get("Site Name"),
1873
+ "project": site.get("Project Name"),
1874
+ "site_summary": {
1875
+ "USCS": site.get("USCS"), "AASHTO": site.get("AASHTO"), "GI": site.get("GI"),
1876
+ "Soil Profile": site.get("Soil Profile"),
1877
+ "Key lab results": [r.get("sampleId") for r in site.get("Laboratory Results",[])]
1878
+ },
1879
+ "inputs": {fld: site.get(fld,"Not provided") for fld,_ in REPORT_FIELDS}
1880
+ }
1881
+ prompt = (
1882
+ "You are GeoMate AI, an engineering assistant. Given the following site context and "
1883
+ "engineering parameters (some may be 'Not provided'), produce:\n1) short executive summary, "
1884
+ "2) geotechnical interpretation (classification, key risks), 3) recommended remedial/improvement "
1885
+ "options and 4) short design notes. Provide any numeric outputs in the format [[FIELD: value unit]].\n\n"
1886
+ f"Context: {json.dumps(context)}\n\nAnswer concisely and professionally."
1887
+ )
1888
+ resp = groq_generate(prompt, model=st.session_state["llm_model"], max_tokens=600)
1889
+ # display
1890
+ st.markdown("**GeoMate analysis**")
1891
+ st.markdown(resp)
1892
+ # try to extract numeric fields (same bracket format as elsewhere)
1893
+ import re
1894
+ matches = re.findall(r"\[\[([A-Za-z0-9 _/-]+):\s*([0-9.+-eE]+)\s*([A-Za-z%\/]*)\]\]", resp)
1895
+ for m in matches:
1896
+ field = m[0].strip()
1897
+ val = m[1].strip()
1898
+ unit = m[2].strip()
1899
+ # map likely field names:
1900
+ if "bearing" in field.lower():
1901
+ site["Load Bearing Capacity"] = f"{val} {unit}"
1902
+ elif "skin" in field.lower():
1903
+ site["Skin Shear Strength"] = f"{val} {unit}"
1904
+ elif "compaction" in field.lower():
1905
+ site["Relative Compaction"] = f"{val} {unit}"
1906
+ # store the analysis text so it can be included in the PDF later
1907
+ site["LLM_Report_Text"] = resp
1908
+ st.success("LLM analysis saved to site under 'LLM_Report_Text'.")
1909
+
1910
  st.markdown("---")
1911
  st.subheader("Full Geotechnical Report (chatbot will gather missing fields)")
1912
  if st.button("Start Report Chatbot"):