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
This commit is contained in:
parent
f294ac155e
commit
39245e0999
@ -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)
|
||||
|
||||
@ -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:
|
||||
<h3>{summary.get('total_open', 0)}</h3>
|
||||
<p>Open</p>
|
||||
</div>
|
||||
<div class="summary-card danger">
|
||||
<h3>{summary.get('total_incomplete', 0)}</h3>
|
||||
<p>Incomplete</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs-container">
|
||||
@ -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:
|
||||
<div class="stat-value" style="color: #ef4444;">{open_count}</div>
|
||||
<div class="stat-label">Open</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-value" style="color: #dc2626;">{incomplete_count}</div>
|
||||
<div class="stat-label">Incomplete</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1263,6 +1282,7 @@ def generate_vendor_section(vendor: Dict) -> str:
|
||||
<button class="status-tab" onclick="switchStatusTab(this, '{vendor_name}')" data-status="closed">Closed ({closed_count})</button>
|
||||
<button class="status-tab" onclick="switchStatusTab(this, '{vendor_name}')" data-status="monitor">Monitor ({monitor_count})</button>
|
||||
<button class="status-tab" onclick="switchStatusTab(this, '{vendor_name}')" data-status="open">Open ({open_count})</button>
|
||||
<button class="status-tab" onclick="switchStatusTab(this, '{vendor_name}')" data-status="incomplete">Incomplete ({incomplete_count})</button>
|
||||
</div>
|
||||
|
||||
<div class="status-tab-content active" data-status="all" data-vendor="{vendor_name}">
|
||||
@ -1309,6 +1329,15 @@ def generate_vendor_section(vendor: Dict) -> str:
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-tab-content" data-status="incomplete" data-vendor="{vendor_name}">
|
||||
<div class="section">
|
||||
<div class="section-title">Incomplete Items ({len(incomplete_items)})</div>
|
||||
<ul class="item-list">
|
||||
{''.join([generate_item_html(item) for item in incomplete_items]) if incomplete_items else '<li class="empty">No incomplete items</li>'}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user