Selaa lähdekoodia

updated to include report downloading feature

Dr-Swopt 2 viikkoa sitten
vanhempi
commit
e2173bbfbb
2 muutettua tiedostoa jossa 101 lisäystä ja 1 poistoa
  1. 25 1
      README.md
  2. 76 0
      demo_app.py

+ 25 - 1
README.md

@@ -48,6 +48,29 @@ The following features are currently **KIV (Keep In View)** and are disabled in
 
 ---
 
+## ⚙️ Environment Setup
+
+Before running the demonstration, ensure you have Python 3.9+ installed. It is highly recommended to use a virtual environment.
+
+### 1. Initialize & Activate Virtual Environment
+```powershell
+# Create the virtual environment
+python -m venv venv
+
+# Activate on Windows (PowerShell)
+.\venv\Scripts\Activate.ps1
+
+# Activate on macOS/Linux
+source venv/bin/activate
+```
+
+### 2. Install Dependencies
+```powershell
+pip install -r requirements.txt
+```
+
+---
+
 ## 🔌 Running the Demonstration
 
 ### 1. Start the FastAPI Backend
@@ -57,8 +80,9 @@ python main.py
 ```
 
 ### 2. Launch the Operations Dashboard
-In a separate terminal, run the Streamlit UI:
+In a separate terminal, run the Streamlit UI (ensuring the venv is activated):
 ```powershell
+# Ensure venv is active first
 streamlit run demo_app.py
 ```
 

+ 76 - 0
demo_app.py

@@ -10,6 +10,7 @@ import plotly.express as px
 import plotly.graph_objects as go
 import json
 import os
+import zipfile
 from datetime import datetime
 from fpdf import FPDF
 
@@ -463,6 +464,71 @@ def generate_batch_report(data, uploaded_files_map=None):
     
     return bytes(pdf.output(dest='S'))
 
+def generate_batch_zip(data, uploaded_files, pdf_bytes=None):
+    """Generates a complete ZIP bundle containing raw, annotated images and metadata."""
+    zip_buffer = io.BytesIO()
+    
+    # Map uploaded files for easy lookup
+    files_map = {f.name: f.getvalue() for f in uploaded_files}
+    
+    with zipfile.ZipFile(zip_buffer, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
+        # 1. Save Raw Images
+        for fname, content in files_map.items():
+            zip_file.writestr(f"raw/{fname}", content)
+            
+        # 2. Save Annotated Images
+        # Group detections by filename
+        results_by_file = {}
+        for res in data.get('detailed_results', []):
+            fname = res['filename']
+            if fname not in results_by_file:
+                results_by_file[fname] = []
+            results_by_file[fname].append(res['detection'])
+            
+        for fname, detections in results_by_file.items():
+            # The filename in detailed_results might be the unique one from backend (e.g. "uuid_name.jpg")
+            # We need to find the matching original file based on the suffix or name
+            original_fname = None
+            for ofname in files_map.keys():
+                if ofname in fname: # Basic match
+                    original_fname = ofname
+                    break
+            
+            if original_fname:
+                img = Image.open(io.BytesIO(files_map[original_fname])).convert("RGB")
+                img_annotated = annotate_image(img, detections)
+                
+                # Save annotated to bytes
+                img_byte_arr = io.BytesIO()
+                img_annotated.save(img_byte_arr, format='JPEG')
+                zip_file.writestr(f"annotated/annotated_{original_fname}", img_byte_arr.getvalue())
+
+        # 3. Save Summary Metadata (CSV)
+        summary_rows = []
+        for res in data.get('detailed_results', []):
+            det = res['detection']
+            summary_rows.append({
+                "filename": res['filename'],
+                "bunch_id": det.get('bunch_id'),
+                "class": det.get('class'),
+                "confidence": det.get('confidence'),
+                "is_health_alert": det.get('is_health_alert')
+            })
+        
+        if summary_rows:
+            df = pd.DataFrame(summary_rows)
+            csv_data = df.to_csv(index=False)
+            zip_file.writestr("detection_summary.csv", csv_data)
+
+        # 4. Save Backend Manifest
+        if 'manifest_preview' in data:
+            zip_file.writestr("manifest.json", json.dumps(data['manifest_preview'], indent=4))
+            
+        # 5. Save PDF Report if provided
+        if pdf_bytes:
+            zip_file.writestr(f"PalmOil_ExecutiveReport_{data.get('batch_id', 'Batch')}.pdf", pdf_bytes)
+
+    return zip_buffer.getvalue()
 
 
 # --- Tabs ---
@@ -782,6 +848,16 @@ with tab2:
                 width='stretch'
             )
 
+            # --- Full Batch ZIP ---
+            with st.spinner("📦 Preparing Batch Bundle..."):
+                zip_bytes = generate_batch_zip(res_data, uploaded_files, pdf_bytes)
+                st.download_button(
+                    label="📦 Download Full Batch Bundle (ZIP)",
+                    data=zip_bytes,
+                    file_name=f"PalmOil_BatchBundle_{res_data.get('batch_id', 'Bundle')}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip",
+                    mime="application/zip",
+                    width='stretch'
+                )
 
             if st.button("Clear Results & Start New Batch", width='stretch'):
                 st.session_state.last_batch_results = None