Skip to content

Commit a6b10b3

Browse files
authored
Merge branch 'main' into feature/dtd-doc-updates
2 parents ada07e7 + 6686520 commit a6b10b3

20 files changed

+5849
-29
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[pytest]
2+
# Test discovery
3+
testpaths = tests
4+
python_files = test_*.py
5+
python_classes = Test*
6+
python_functions = test_*
7+
8+
# Output configuration
9+
addopts = -v --tb=short --strict-markers
10+
11+
# Async configuration
12+
asyncio_mode = auto
13+
14+
# Markers
15+
markers =
16+
unit: Unit tests (fast, isolated)
17+
18+
# Logging
19+
log_cli = true
20+
log_cli_level = INFO
21+
22+
# Coverage (optional, requires pytest-cov)
23+
# addopts = --cov=src --cov-report=term-missing --cov-report=html

metro-ai-suite/smart-traffic-intersection-agent/setup.sh

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,36 +235,36 @@ print_all_service_host_endpoints() {
235235
*dlstreamer-pipeline-server*)
236236
SERVICE_NAME="DLStreamer Pipeline Server"
237237
PORT=$(docker port "$CONTAINER_NAME" 8080 | cut -d: -f2)
238-
echo -e "${BLUE}Access $SERVICE_NAME at -> http://$HOST_IP:$PORT${NC}/pipelines"
238+
echo -e "${BLUE}Access $SERVICE_NAME -> http://$HOST_IP:$PORT${NC}/pipelines"
239239
;;
240240
*grafana*)
241241
SERVICE_NAME="Grafana Dashboard"
242242
PORT=$(docker port "$CONTAINER_NAME" 3000 | cut -d: -f2)
243-
echo -e "${BLUE}Access $SERVICE_NAME at -> http://$HOST_IP:$PORT${NC}"
243+
echo -e "${BLUE}Access $SERVICE_NAME -> http://$HOST_IP:$PORT${NC}"
244244
;;
245245
*node-red*)
246246
SERVICE_NAME="Node-RED"
247247
PORT=$(docker port "$CONTAINER_NAME" 1880 | cut -d: -f2)
248-
echo -e "${BLUE}Access $SERVICE_NAME at -> http://$HOST_IP:$PORT${NC}"
248+
echo -e "${BLUE}Access $SERVICE_NAME -> http://$HOST_IP:$PORT${NC}"
249249
;;
250250
*web*)
251251
SERVICE_NAME="Scenescape Web UI"
252252
PORT=$(docker port "$CONTAINER_NAME" 443 | cut -d: -f2)
253-
echo -e "${BLUE}Access $SERVICE_NAME at -> https://$HOST_IP:$PORT${NC}"
253+
echo -e "${BLUE}Access $SERVICE_NAME -> https://$HOST_IP:$PORT${NC}"
254254
;;
255255
*traffic-agent*)
256256
BACKEND_SERVICE_NAME="Traffic Intersection Agent API Docs"
257257
PORT=$(docker port "$CONTAINER_NAME" 8081 | cut -d: -f2)
258-
echo -e "${CYAN}$BACKEND_SERVICE_NAME -> http://$HOST_IP:$PORT/docs${NC}"
258+
echo -e "${BLUE}Access $BACKEND_SERVICE_NAME -> http://$HOST_IP:$PORT/docs${NC}"
259259

260260
UI_SERVICE_NAME="Traffic Intersection Agent UI"
261261
PORT=$(docker port "$CONTAINER_NAME" 7860 | cut -d: -f2)
262-
echo -e "${CYAN}$UI_SERVICE_NAME -> http://$HOST_IP:$PORT${NC}"
262+
echo -e "${BLUE}Access $UI_SERVICE_NAME -> http://$HOST_IP:$PORT${NC}"
263263
;;
264264
*vlm*)
265265
SERVICE_NAME="VLM OpenVINO Serving API"
266266
PORT=$(docker port "$CONTAINER_NAME" 8000 | cut -d: -f2)
267-
echo -e "${CYAN}$SERVICE_NAME -> http://$HOST_IP:$PORT/docs${NC}"
267+
echo -e "${BLUE}Access $SERVICE_NAME -> http://$HOST_IP:$PORT/docs${NC}"
268268
;;
269269
*)
270270
SERVICE_NAME="Unknown Service"

metro-ai-suite/smart-traffic-intersection-agent/src/services/data_aggregator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ def get_service_status(self) -> Dict[str, Any]:
371371
"active_analyzed_cameras": len(self.vlm_analyzed_camera_images),
372372
"has_weather_data": self.vlm_analyzed_weather_data is not None,
373373
"has_vlm_analysis": self.current_vlm_analysis is not None,
374-
"last_analysis_time": self.last_analysis_time.isoformat() if self.last_analysis_time else None,
374+
"last_analysis_time": datetime.fromtimestamp(self.last_analysis_time, tz=timezone.utc).isoformat() if self.last_analysis_time else None,
375375
# "vlm_analyzed_history_count": len(self.traffic_history),
376376
# "analysis_tasks_running": self.analysis_task is not None and not self.analysis_task.done()
377377
}

metro-ai-suite/smart-traffic-intersection-agent/src/services/vlm_service.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,9 @@ def _build_vlm_request(self, prompt: str, camera_images: List[CameraImage]) -> D
323323
}
324324

325325
logger.debug("VLM request built",
326-
request)
326+
model=request.get("model"),
327+
max_tokens=request.get("max_completion_tokens"),
328+
content_items=len(content))
327329

328330
return request
329331

@@ -555,18 +557,10 @@ def _create_fallback_analysis(self,
555557
if weather_data:
556558
weather_impact = True
557559

558-
# Handle both WeatherType enum and string (defensive programming)
559-
weather_type = weather_data.weather_type
560-
if isinstance(weather_type, str):
561-
weather_type = WeatherType(weather_type)
562-
563-
CRITICAL_WEATHER = {WeatherType.FIRES, WeatherType.STORM, WeatherType.FLOOD}
564-
alert_level = AlertLevel.CRITICAL if weather_type in CRITICAL_WEATHER else AlertLevel.WARNING
565-
566560
alert = VLMAlert(
567561
alert_type=AlertType.WEATHER_RELATED,
568-
level=alert_level,
569-
description=self.weather_service.get_current_weather_description(weather_type),
562+
level=AlertLevel.INFO,
563+
description=self.weather_service.get_current_weather_description(),
570564
weather_related=weather_impact
571565
)
572566
alerts.append(alert)

metro-ai-suite/smart-traffic-intersection-agent/src/services/weather_service.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,22 @@ def _sync_fetch():
202202

203203
logger.debug("Forecast API response successful", status_code=forecast_resp.status_code)
204204

205-
# Step 4: Process the first forecast period (most current hour) and format according to specified structure
206-
first_period = forecast_data["properties"]["periods"][0]
207-
logger.info("First forecast period data", period=first_period)
205+
# Step 4: Find the current hour's forecast period
206+
periods = forecast_data["properties"]["periods"]
207+
now = datetime.now(timezone.utc)
208+
current_period = periods[0] # Default to first period
209+
210+
for period in periods:
211+
start_time = datetime.fromisoformat(period["startTime"].replace('Z', '+00:00'))
212+
end_time = datetime.fromisoformat(period["endTime"].replace('Z', '+00:00'))
213+
if start_time <= now <= end_time:
214+
current_period = period
215+
break
216+
217+
logger.info("Current forecast period data", period=current_period)
208218

209219
# Process weather data into WeatherData object
210-
weather_data = self._process_weather_data(first_period)
220+
weather_data = self._process_weather_data(current_period)
211221

212222
logger.debug("Weather data fetched successfully",
213223
conditions=weather_data.detailed_forecast,

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88
from typing import Optional
99
from datetime import datetime, timezone
1010

11-
from models import (
12-
MonitoringData, IntersectionData, RegionCount,
13-
VLMAnalysis, WeatherData, CameraData, TrafficContext
14-
)
11+
try:
12+
from ui.models import (
13+
MonitoringData, IntersectionData, RegionCount,
14+
VLMAnalysis, WeatherData, CameraData, TrafficContext
15+
)
16+
except (ModuleNotFoundError, ImportError):
17+
from models import (
18+
MonitoringData, IntersectionData, RegionCount,
19+
VLMAnalysis, WeatherData, CameraData, TrafficContext
20+
)
1521

1622
logger = logging.getLogger(__name__)
1723

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88
from PIL import Image
99
from typing import Any, Optional, List, Tuple
1010

11-
from models import MonitoringData
12-
from config import Config
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
1320

1421

1522
class ThemeColors:
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Copyright (C) 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Smart Traffic Intersection Agent Test Suite."""
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Copyright (C) 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Shared test fixtures and configuration for pytest."""
4+
5+
import os
6+
import sys
7+
import pytest
8+
9+
# Add src directory to Python path for imports
10+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
11+
12+
13+
@pytest.fixture
14+
def mock_env_vars(monkeypatch):
15+
"""Set up mock environment variables for testing."""
16+
monkeypatch.setenv("INTERSECTION_NAME", "Test-Intersection")
17+
monkeypatch.setenv("INTERSECTION_LATITUDE", "37.7749")
18+
monkeypatch.setenv("INTERSECTION_LONGITUDE", "-122.4194")
19+
monkeypatch.setenv("MQTT_HOST", "localhost")
20+
monkeypatch.setenv("MQTT_PORT", "1883")
21+
monkeypatch.setenv("WEATHER_MOCK", "true")
22+
monkeypatch.setenv("HIGH_DENSITY_THRESHOLD", "10")
23+
monkeypatch.setenv("MODERATE_DENSITY_THRESHOLD", "5")
24+
25+
26+
@pytest.fixture
27+
def sample_traffic_data():
28+
"""Sample traffic data for testing."""
29+
return {
30+
"intersection_id": "test-intersection-001",
31+
"timestamp": "2026-01-22T10:00:00Z",
32+
"directions": {
33+
"north": {"vehicle_count": 5, "density": "NORMAL"},
34+
"south": {"vehicle_count": 8, "density": "MODERATE"},
35+
"east": {"vehicle_count": 12, "density": "HIGH"},
36+
"west": {"vehicle_count": 3, "density": "NORMAL"}
37+
},
38+
"total_vehicles": 28
39+
}
40+
41+
42+
@pytest.fixture
43+
def sample_weather_data():
44+
"""Sample weather data for testing."""
45+
return {
46+
"temperature": 55,
47+
"short_forecast": "Sunny",
48+
"conditions": "Cloudy conditions with 5 mph winds from the NW. 10% chance of precipitation.",
49+
"is_precipitation": False,
50+
"precipitation": False,
51+
"wind_speed": "5 mph",
52+
"wind_direction": "NW"
53+
}
54+
55+
56+
@pytest.fixture
57+
def sample_vlm_response():
58+
"""Sample VLM analysis response for testing."""
59+
return {
60+
"analysis": "Traffic flow is moderate with higher density on the east approach.",
61+
"alerts": [
62+
{
63+
"alert_type": "congestion",
64+
"level": "warning",
65+
"description": "East approach experiencing high traffic density",
66+
"weather_related": False
67+
}
68+
],
69+
"recommendations": [
70+
{
71+
"recommendation": "Consider extending green light duration for east-west traffic"
72+
}
73+
]
74+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Running Unit Tests for Smart Traffic Intersection Agent
2+
3+
This guide will help you run the unit tests for the Smart Traffic Intersection Agent project using the pytest framework.
4+
5+
---
6+
7+
## Table of Contents
8+
9+
- [Prerequisites](#prerequisites)
10+
- [Running Tests in a Virtual Environment](#running-tests-in-a-virtual-environment)
11+
- [Test Files](#test-files)
12+
13+
---
14+
15+
## Prerequisites
16+
17+
Before running the tests, ensure you have the following installed:
18+
19+
- Python 3.10+
20+
- `pip` (Python package installer)
21+
- `uv` (Python package and project manager) - Optional but recommended
22+
23+
You can install `uv` using the following command:
24+
25+
```bash
26+
curl -LsSf https://astral.sh/uv/install.sh | sh
27+
```
28+
29+
---
30+
31+
## Running Tests in a Virtual Environment
32+
33+
Follow these steps to run the tests:
34+
35+
1. **Clone the Repository**
36+
37+
Clone the repository to your local machine:
38+
39+
```bash
40+
git clone https://github.com/open-edge-platform/edge-ai-suites.git
41+
cd edge-ai-suites/metro-ai-suite/smart-traffic-intersection-agent
42+
```
43+
44+
2. **Create and Activate a Virtual Environment**
45+
46+
Navigate to the project directory and create a virtual environment:
47+
48+
```bash
49+
python -m venv .venv
50+
source .venv/bin/activate
51+
```
52+
53+
3. **Install Dependencies**
54+
55+
Install application and dev dependencies:
56+
57+
```bash
58+
# Using uv (recommended)
59+
cd src
60+
uv pip install -e ".[dev]"
61+
62+
# Or using pip
63+
cd src
64+
pip install -e ".[dev]"
65+
```
66+
67+
4. **Navigate to the Project Root**
68+
69+
Change to the project root directory (where `pytest.ini` is located):
70+
71+
```bash
72+
cd /path/to/edge-ai-suites/metro-ai-suite/smart-traffic-intersection-agent
73+
```
74+
75+
5. **Run the Tests**
76+
77+
**Important:** Make sure the virtual environment is activated before running tests. You should see `(.venv)` in your terminal prompt.
78+
79+
```bash
80+
# Verify you're in the virtual environment (should show .venv path)
81+
which python
82+
# Expected: /path/to/smart-traffic-intersection-agent/.venv/bin/python
83+
84+
# If not activated, activate it first:
85+
source .venv/bin/activate
86+
```
87+
88+
Use the below command to run all unit tests:
89+
90+
```bash
91+
pytest tests/unit -v
92+
```
93+
94+
To run a specific test file:
95+
96+
```bash
97+
pytest tests/unit/test_data_aggregator.py -v
98+
```
99+
100+
To run tests with coverage report:
101+
102+
```bash
103+
pytest tests/unit --cov=src --cov-report=term-missing
104+
```
105+
106+
6. **Deactivate Virtual Environment**
107+
108+
When you are done with testing:
109+
110+
```bash
111+
deactivate
112+
```
113+
114+
---
115+
116+
## Test Files
117+
118+
The following unit test files are available:
119+
120+
| Test File | Description |
121+
|-----------|-------------|
122+
| `test_api_routes.py` | Tests for FastAPI route endpoints |
123+
| `test_config_service.py` | Tests for configuration service |
124+
| `test_data_aggregator.py` | Tests for data aggregation service |
125+
| `test_main.py` | Tests for main application entry point |
126+
| `test_mqtt_service.py` | Tests for MQTT communication service |
127+
| `test_run.py` | Tests for application runner |
128+
| `test_ui_components.py` | Tests for UI components |
129+
| `test_vlm_service.py` | Tests for VLM (Vision Language Model) service |
130+
| `test_weather_service.py` | Tests for weather data service |

0 commit comments

Comments
 (0)