Skip to content

Commit 7c8256c

Browse files
committed
🧵 all methods async, minor refactoring, removed unused methods
- Minor refactoring in UIComponents apart from amking all methods async - Removed time format conversion method - 2 lines single use function - removed int type casting - already int type from API response - removed other unused functions Signed-off-by: Krishna Murti <krishna.murti@intel.com>
1 parent e1e2ebd commit 7c8256c

File tree

1 file changed

+23
-124
lines changed

1 file changed

+23
-124
lines changed

metro-ai-suite/smart-traffic-intersection-agent/src/ui/ui_components.py

Lines changed: 23 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,10 @@
66
import base64
77
import io
88
from PIL import Image
9-
from typing import Any, Optional, List, Tuple
9+
from typing import Optional, List, Tuple
1010

11-
try:
12-
from ui.models import MonitoringData
13-
except (ModuleNotFoundError, ImportError):
14-
from models import MonitoringData
15-
16-
try:
17-
from ui.config import Config
18-
except (ModuleNotFoundError, ImportError):
19-
from config import Config
11+
from models import MonitoringData
12+
from config import Config
2013

2114

2215
class ThemeColors:
@@ -43,7 +36,7 @@ class UIComponents:
4336
"""UI component generator class"""
4437

4538
@staticmethod
46-
def _render_markdown(md_text: str) -> str:
39+
async def _render_markdown(md_text: str) -> str:
4740
"""Render markdown text to HTML. Falls back to simple replacements if markdown package not installed."""
4841

4942
if not md_text:
@@ -59,7 +52,7 @@ def _render_markdown(md_text: str) -> str:
5952

6053

6154
@staticmethod
62-
def _get_traffic_density_color(density: int) -> str:
55+
async def _get_traffic_density_color(density: int) -> str:
6356
"""Get background color based on traffic density
6457
6558
Args:
@@ -74,8 +67,9 @@ def _get_traffic_density_color(density: int) -> str:
7467
return "#ffff99" # Yellow for moderate density
7568
else:
7669
return "#ffffff" # Default white for low density
70+
7771
@staticmethod
78-
def create_header(monitoring_data: Optional[MonitoringData] = None) -> str:
72+
async def create_header(monitoring_data: Optional[MonitoringData] = None) -> str:
7973
"""Create the header section with system title and status"""
8074
colors = ThemeColors.get_colors()
8175

@@ -96,7 +90,7 @@ def create_header(monitoring_data: Optional[MonitoringData] = None) -> str:
9690
"""
9791

9892
@staticmethod
99-
def create_traffic_summary(monitoring_data: Optional[MonitoringData]) -> str:
93+
async def create_traffic_summary(monitoring_data: Optional[MonitoringData]) -> str:
10094
"""Create traffic summary cards"""
10195
if not monitoring_data:
10296
return "<p style='text-align: center; color: #ef4444;'>No traffic data available</p>"
@@ -106,10 +100,10 @@ def create_traffic_summary(monitoring_data: Optional[MonitoringData]) -> str:
106100
total_pedestrians = monitoring_data.get_total_pedestrians()
107101

108102
# Get background colors for each direction based on traffic density
109-
north_bg_color = UIComponents._get_traffic_density_color(data.northbound_density)
110-
south_bg_color = UIComponents._get_traffic_density_color(data.southbound_density)
111-
east_bg_color = UIComponents._get_traffic_density_color(data.eastbound_density)
112-
west_bg_color = UIComponents._get_traffic_density_color(data.westbound_density)
103+
north_bg_color = await UIComponents._get_traffic_density_color(data.northbound_density)
104+
south_bg_color = await UIComponents._get_traffic_density_color(data.southbound_density)
105+
east_bg_color = await UIComponents._get_traffic_density_color(data.eastbound_density)
106+
west_bg_color = await UIComponents._get_traffic_density_color(data.westbound_density)
113107

114108
return f"""
115109
<div style="background: {colors['bg_primary']}; border-radius: 12px; padding: 20px; margin: 10px 0; border: 1px solid {colors['border']}; box-shadow: {colors['shadow']};">
@@ -150,7 +144,7 @@ def create_traffic_summary(monitoring_data: Optional[MonitoringData]) -> str:
150144
"""
151145

152146
@staticmethod
153-
def create_debug_panel(monitoring_data: Optional[MonitoringData]) -> str:
147+
async def create_debug_panel(monitoring_data: Optional[MonitoringData]) -> str:
154148
"""
155149
Create a hidden debug panel which is shown only when the debug checkbox is enabled.
156150
Contains timestamp info about data and images for each direction.
@@ -193,7 +187,7 @@ def create_debug_panel(monitoring_data: Optional[MonitoringData]) -> str:
193187

194188

195189
@staticmethod
196-
def create_environmental_panel(monitoring_data: Optional[MonitoringData]) -> str:
190+
async def create_environmental_panel(monitoring_data: Optional[MonitoringData]) -> str:
197191
"""Create environmental data panel"""
198192
if not monitoring_data:
199193
return "<p style='text-align: center; color: #ef4444;'>No environmental data available</p>"
@@ -244,17 +238,6 @@ def create_environmental_panel(monitoring_data: Optional[MonitoringData]) -> str
244238
daytime_status = "Night time"
245239
daytime_icon = "🌙"
246240

247-
# Format forecast period
248-
forecast_period = "Current"
249-
if weather.start_time and weather.end_time:
250-
try:
251-
from datetime import datetime
252-
start_dt = datetime.fromisoformat(weather.start_time.replace('Z', '+00:00'))
253-
end_dt = datetime.fromisoformat(weather.end_time.replace('Z', '+00:00'))
254-
forecast_period = f"{start_dt.strftime('%H:%M')} - {end_dt.strftime('%H:%M')}"
255-
except:
256-
forecast_period = "Current Hour"
257-
258241
# Use relative humidity from API if available, otherwise use the estimated value
259242
display_humidity = weather.relative_humidity if weather.relative_humidity is not None else weather.humidity_percent
260243

@@ -265,7 +248,7 @@ def create_environmental_panel(monitoring_data: Optional[MonitoringData]) -> str
265248
<!-- Primary Weather Metrics -->
266249
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; margin-bottom: 18px;">
267250
<div style="text-align: center; background: {colors['bg_card']}; padding: 15px; border-radius: 8px; box-shadow: {colors['shadow']}; border: 1px solid {colors['border']};">
268-
<div style="font-size: 1.5em; color: #fbbf24; font-weight: bold; margin-bottom: 5px;">{int(weather.temperature_fahrenheit)}°{weather.temperature_unit}</div>
251+
<div style="font-size: 1.5em; color: #fbbf24; font-weight: bold; margin-bottom: 5px;">{weather.temperature_fahrenheit}°{weather.temperature_unit}</div>
269252
<div style="color: {colors['text_secondary']}; font-size: 0.9em; font-weight: 500;">TEMPERATURE</div>
270253
</div>
271254
<div style="text-align: center; background: {colors['bg_card']}; padding: 15px; border-radius: 8px; box-shadow: {colors['shadow']}; border: 1px solid {colors['border']};">
@@ -302,7 +285,7 @@ def create_environmental_panel(monitoring_data: Optional[MonitoringData]) -> str
302285
"""
303286

304287
@staticmethod
305-
def create_alerts_panel(monitoring_data: Optional[MonitoringData]) -> str:
288+
async def create_alerts_panel(monitoring_data: Optional[MonitoringData]) -> str:
306289
"""Create alerts panel with structured alerts and recommendations"""
307290
if not monitoring_data:
308291
return "<p style='text-align: center; color: #ef4444;'>No alerts data available</p>"
@@ -394,7 +377,7 @@ def create_alerts_panel(monitoring_data: Optional[MonitoringData]) -> str:
394377
</div>
395378
"""
396379

397-
analysis_html = UIComponents._render_markdown(monitoring_data.vlm_analysis.analysis)
380+
analysis_html = await UIComponents._render_markdown(monitoring_data.vlm_analysis.analysis)
398381

399382
return f"""
400383
<div style="background: {colors['bg_primary']}; border-radius: 12px; padding: 20px; margin: 10px 0; box-shadow: {colors['shadow']}; border: 1px solid {colors['border']};">
@@ -417,7 +400,7 @@ def create_alerts_panel(monitoring_data: Optional[MonitoringData]) -> str:
417400
"""
418401

419402
@staticmethod
420-
def create_camera_images(monitoring_data: Optional[MonitoringData]) -> List[Tuple[str, str]]:
403+
async def create_camera_images(monitoring_data: Optional[MonitoringData]) -> List[Tuple[str, str]]:
421404
"""Create camera images for display in Gradio Gallery"""
422405
if not monitoring_data or not monitoring_data.camera_images:
423406
return []
@@ -459,98 +442,12 @@ def create_camera_images(monitoring_data: Optional[MonitoringData]) -> List[Tupl
459442
continue
460443

461444
return image_list
462-
463-
@staticmethod
464-
def create_camera_grid_html(monitoring_data: Optional[MonitoringData]) -> str:
465-
"""Create an HTML grid display of camera images"""
466-
if not monitoring_data or not monitoring_data.camera_images:
467-
return "<p style='text-align: center; color: #ef4444;'>No camera images available</p>"
468-
469-
colors = ThemeColors.get_colors()
470-
cameras_html = ""
471-
472-
# Define camera order for consistent layout - updated for new API format
473-
camera_order = ["north_camera", "east_camera", "south_camera", "west_camera"]
474-
475-
# If the expected camera keys don't exist, use whatever keys are available
476-
available_cameras = list(monitoring_data.camera_images.keys())
477-
cameras_to_display = [cam for cam in camera_order if cam in available_cameras] or available_cameras
478-
479-
for camera_key in cameras_to_display:
480-
if camera_key in monitoring_data.camera_images:
481-
camera_data = monitoring_data.camera_images[camera_key]
482-
483-
# Handle both CameraData objects and dict structures from API
484-
if hasattr(camera_data, 'image_base64'):
485-
# CameraData object
486-
image_base64 = camera_data.image_base64
487-
direction = camera_data.direction
488-
camera_id = camera_data.camera_id
489-
elif isinstance(camera_data, dict):
490-
# Dict from API
491-
image_base64 = camera_data.get('image_base64')
492-
direction = camera_data.get('direction', 'unknown')
493-
camera_id = camera_data.get('camera_id', 'unknown')
494-
else:
495-
continue
496-
497-
if image_base64:
498-
# Create data URL for image
499-
image_src = f"data:image/jpeg;base64,{image_base64}"
500-
border_color = colors['border']
501-
502-
cameras_html += f"""
503-
<div style="background: {colors['bg_card']}; border-radius: 8px; padding: 15px; text-align: center;
504-
box-shadow: {colors['shadow']}; width: 100%; border: 1px solid {border_color};">
505-
<h4 style="color: {colors['text_primary']}; margin: 0 0 12px 0; font-size: 0.95em; font-weight: 600;">
506-
{direction.upper()} VIEW - {camera_id}
507-
</h4>
508-
<div style="position: relative; display: block; width: 100%;">
509-
<img src="{image_src}"
510-
style="width: 100%; height: 200px; object-fit: cover;
511-
border-radius: 6px; border: 2px solid {border_color};
512-
box-shadow: {colors['shadow']}; display: block;"
513-
alt="{direction} view">
514-
<div style="position: absolute; top: 8px; right: 8px;
515-
background: rgba(220,38,38,0.9); color: white;
516-
padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: bold;
517-
box-shadow: 0 2px 4px rgba(0,0,0,0.3);">
518-
● LIVE
519-
</div>
520-
</div>
521-
</div>
522-
"""
523-
else:
524-
cameras_html += f"""
525-
<div style="background: {colors['bg_card']}; border-radius: 8px; padding: 15px; text-align: center;
526-
box-shadow: {colors['shadow']}; width: 100%; border: 1px solid {colors['border']};">
527-
<h4 style="color: {colors['text_primary']}; margin: 0 0 12px 0; font-size: 0.95em; font-weight: 600;">
528-
{direction.upper()} VIEW - {camera_id}
529-
</h4>
530-
<div style="background: {colors['bg_secondary']}; border-radius: 6px; padding: 40px; color: {colors['text_secondary']};
531-
height: 200px; display: flex; flex-direction: column; justify-content: center;
532-
align-items: center; border: 2px solid {colors['border']}; box-shadow: {colors['shadow']};">
533-
<div style="font-size: 48px; margin-bottom: 12px; opacity: 0.7;">📷</div>
534-
<div style="font-size: 13px; font-weight: 500;">No image available</div>
535-
</div>
536-
</div>
537-
"""
538-
539-
return f"""
540-
<div style="background: {colors['bg_primary']}; border-radius: 12px; padding: 20px; margin: 10px 0; box-shadow: {colors['shadow']}; border: 1px solid {colors['border']};">
541-
<h3 style="color: {colors['text_primary']}; margin: 0 0 20px 0; text-align: center; font-size: 1.2em;">📹 Camera Feeds</h3>
542-
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px;">
543-
{cameras_html}
544-
</div>
545-
</div>
546-
"""
547445

548446
@staticmethod
549-
def create_system_info(monitoring_data: Optional[MonitoringData] = None) -> str:
447+
async def create_system_info(monitoring_data: Optional[MonitoringData] = None) -> str:
550448
"""Create system information footer with current status"""
551449
# Use UTC and consistent formatting for both current time and last update
552450
from datetime import datetime, timezone
553-
from data_loader import get_last_update_time
554451

555452
colors = ThemeColors.get_colors()
556453

@@ -564,8 +461,10 @@ def create_system_info(monitoring_data: Optional[MonitoringData] = None) -> str:
564461
if monitoring_data:
565462
# Prefer a nicely formatted last update using the data loader helper
566463
try:
567-
last_update = get_last_update_time(monitoring_data) or current_time
568-
except Exception:
464+
timestamp = datetime.fromisoformat(monitoring_data.timestamp.replace('Z', '+00:00'))
465+
last_update = timestamp.strftime("%Y-%m-%d %H:%M:%S UTC")
466+
# TODO - Show time since last update in minutes/seconds for better understanding
467+
except Exception as e:
569468
# Fallback to raw timestamp or current time
570469
last_update = monitoring_data.timestamp or current_time
571470
system_status = "ONLINE"

0 commit comments

Comments
 (0)