From 39245e0999aaead1ad413d9acb49f46fa701d505 Mon Sep 17 00:00:00 2001 From: ilia gu Date: Thu, 6 Nov 2025 00:59:12 +0400 Subject: [PATCH] Add Incomplete status tab and filtering, update incomplete color consistency - Add separate Incomplete tab in vendor sections - Add incomplete_count stat card in vendor headers - Add total_incomplete summary card - Track incomplete_items separately from open_items in report generator - Standardize incomplete status color to #dc2626 across all UI elements - Remove AI/LLM references from documentation --- README.md | 2 +- html_generator.py | 33 +++++++++++++++++++++++++++++++-- report_generator.py | 23 +++++++++++++++++------ 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3cdab14..e01f089 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ A Python tool that generates comprehensive vendor punchlist reports from Excel f ## Features -- **Direct Excel Processing**: Reads Excel files directly using pandas (no LLM required) +- **Direct Excel Processing**: Reads Excel files directly using pandas - **Data Normalization**: Automatically normalizes vendor names, statuses, and priorities - **24-Hour Updates**: Tracks items added, closed, or changed to monitor status in the last 24 hours (based on Baltimore/Eastern timezone) - **Priority Tracking**: Groups items by priority levels (Very High, High, Medium, Low) diff --git a/html_generator.py b/html_generator.py index 0e662d2..290273d 100644 --- a/html_generator.py +++ b/html_generator.py @@ -510,8 +510,8 @@ def generate_html_content(report_data: Dict) -> str: }} .badge-danger {{ - background: #fee2e2; - color: #991b1b; + background: #dc2626; + color: white; }} .badge-secondary {{ @@ -777,6 +777,10 @@ def generate_html_content(report_data: Dict) -> str:

{summary.get('total_open', 0)}

Open

+
+

{summary.get('total_incomplete', 0)}

+

Incomplete

+
@@ -1160,6 +1164,7 @@ def generate_vendor_section(vendor: Dict) -> str: closed_count = vendor.get('closed_count', 0) open_count = vendor.get('open_count', 0) monitor_count = vendor.get('monitor_count', 0) + incomplete_count = vendor.get('incomplete_count', 0) updates_24h = vendor.get('updates_24h', {}) oldest_unaddressed = vendor.get('oldest_unaddressed', []) @@ -1171,6 +1176,7 @@ def generate_vendor_section(vendor: Dict) -> str: closed_items = vendor.get('closed_items', []) monitor_items = vendor.get('monitor_items', []) open_items = vendor.get('open_items', []) + incomplete_items = vendor.get('incomplete_items', []) # Convert to lists if needed (they should already be lists) if not isinstance(closed_items, list): @@ -1179,6 +1185,8 @@ def generate_vendor_section(vendor: Dict) -> str: monitor_items = [] if not isinstance(open_items, list): open_items = [] + if not isinstance(incomplete_items, list): + incomplete_items = [] # Group all items by priority for the "All" tab # Combine all items first @@ -1206,6 +1214,13 @@ def generate_vendor_section(vendor: Dict) -> str: seen_names.add(name) all_items_combined.append(item) + # Add all incomplete items + for item in incomplete_items: + name = item.get('punchlist_name', '') + if name and name not in seen_names: + seen_names.add(name) + all_items_combined.append(item) + # Group items by priority level very_high_all = [] high_all = [] @@ -1252,6 +1267,10 @@ def generate_vendor_section(vendor: Dict) -> str:
{open_count}
Open
+
+
{incomplete_count}
+
Incomplete
+
@@ -1263,6 +1282,7 @@ def generate_vendor_section(vendor: Dict) -> str: +
@@ -1309,6 +1329,15 @@ def generate_vendor_section(vendor: Dict) -> str:
+ +
+
+
Incomplete Items ({len(incomplete_items)})
+
    + {''.join([generate_item_html(item) for item in incomplete_items]) if incomplete_items else '
  • No incomplete items
  • '} +
+
+
""" diff --git a/report_generator.py b/report_generator.py index 1358926..ac5b36a 100644 --- a/report_generator.py +++ b/report_generator.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 """ -Report Generator - Direct Generation (No LLM Required) +Report Generator Generates vendor reports directly from preprocessed Excel data. -All requirements fulfilled programmatically - no AI needed! """ import json @@ -38,7 +37,7 @@ def generate_report( verbose: bool = True ) -> dict: """ - Generate vendor report directly from preprocessed data - NO LLM required! + Generate vendor report directly from preprocessed data. Args: reports_dir: Directory containing Excel files @@ -50,7 +49,7 @@ def generate_report( """ if verbose: print("=" * 70) - print("DIRECT REPORT GENERATION (No LLM Required)") + print("REPORT GENERATION") print("=" * 70) print(f"Loading and preprocessing Excel files from '{reports_dir}'...") @@ -115,9 +114,14 @@ def generate_report( if item.get('status', '').lower() == 'complete' or item.get('is_closed', False)] monitor_items = [convert_item_to_punchlist_item(item) for item in all_items if item.get('status', '').lower() == 'monitor'] + incomplete_items = [convert_item_to_punchlist_item(item) for item in all_items + if item.get('status', '').lower() == 'incomplete' and not item.get('is_closed', False)] open_items = [convert_item_to_punchlist_item(item) for item in all_items if item.get('status', '').lower() == 'incomplete' and not item.get('is_closed', False)] + # Calculate incomplete count + incomplete_count = len(incomplete_items) + # Create vendor metrics vendor_metrics = VendorMetrics( vendor_name=vendor_name, @@ -137,6 +141,8 @@ def generate_report( vendor_dict['closed_items'] = [item.model_dump() for item in closed_items] vendor_dict['monitor_items'] = [item.model_dump() for item in monitor_items] vendor_dict['open_items'] = [item.model_dump() for item in open_items] + vendor_dict['incomplete_items'] = [item.model_dump() for item in incomplete_items] + vendor_dict['incomplete_count'] = incomplete_count vendors.append(vendor_dict) @@ -149,7 +155,8 @@ def generate_report( "total_items": sum(v.get('total_items', 0) if isinstance(v, dict) else v.total_items for v in vendors), "total_closed": sum(v.get('closed_count', 0) if isinstance(v, dict) else v.closed_count for v in vendors), "total_open": sum(v.get('open_count', 0) if isinstance(v, dict) else v.open_count for v in vendors), - "total_monitor": sum(v.get('monitor_count', 0) if isinstance(v, dict) else v.monitor_count for v in vendors) + "total_monitor": sum(v.get('monitor_count', 0) if isinstance(v, dict) else v.monitor_count for v in vendors), + "total_incomplete": sum(v.get('incomplete_count', 0) if isinstance(v, dict) else 0 for v in vendors) } ) @@ -167,6 +174,10 @@ def generate_report( report_data['vendors'][i]['monitor_items'] = vendor_dict['monitor_items'] if 'open_items' in vendor_dict: report_data['vendors'][i]['open_items'] = vendor_dict['open_items'] + if 'incomplete_items' in vendor_dict: + report_data['vendors'][i]['incomplete_items'] = vendor_dict['incomplete_items'] + if 'incomplete_count' in vendor_dict: + report_data['vendors'][i]['incomplete_count'] = vendor_dict['incomplete_count'] # Save to file if specified if output_file: @@ -203,7 +214,7 @@ def generate_report( if __name__ == "__main__": import argparse - parser = argparse.ArgumentParser(description="Generate vendor reports from Excel files (no LLM required)") + parser = argparse.ArgumentParser(description="Generate vendor reports from Excel files") parser.add_argument( "--reports-dir", type=str,