matrice-analytics 0.1.60__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matrice_analytics/__init__.py +28 -0
- matrice_analytics/boundary_drawing_internal/README.md +305 -0
- matrice_analytics/boundary_drawing_internal/__init__.py +45 -0
- matrice_analytics/boundary_drawing_internal/boundary_drawing_internal.py +1207 -0
- matrice_analytics/boundary_drawing_internal/boundary_drawing_tool.py +429 -0
- matrice_analytics/boundary_drawing_internal/boundary_tool_template.html +1036 -0
- matrice_analytics/boundary_drawing_internal/data/.gitignore +12 -0
- matrice_analytics/boundary_drawing_internal/example_usage.py +206 -0
- matrice_analytics/boundary_drawing_internal/usage/README.md +110 -0
- matrice_analytics/boundary_drawing_internal/usage/boundary_drawer_launcher.py +102 -0
- matrice_analytics/boundary_drawing_internal/usage/simple_boundary_launcher.py +107 -0
- matrice_analytics/post_processing/README.md +455 -0
- matrice_analytics/post_processing/__init__.py +732 -0
- matrice_analytics/post_processing/advanced_tracker/README.md +650 -0
- matrice_analytics/post_processing/advanced_tracker/__init__.py +17 -0
- matrice_analytics/post_processing/advanced_tracker/base.py +99 -0
- matrice_analytics/post_processing/advanced_tracker/config.py +77 -0
- matrice_analytics/post_processing/advanced_tracker/kalman_filter.py +370 -0
- matrice_analytics/post_processing/advanced_tracker/matching.py +195 -0
- matrice_analytics/post_processing/advanced_tracker/strack.py +230 -0
- matrice_analytics/post_processing/advanced_tracker/tracker.py +367 -0
- matrice_analytics/post_processing/config.py +146 -0
- matrice_analytics/post_processing/core/__init__.py +63 -0
- matrice_analytics/post_processing/core/base.py +704 -0
- matrice_analytics/post_processing/core/config.py +3291 -0
- matrice_analytics/post_processing/core/config_utils.py +925 -0
- matrice_analytics/post_processing/face_reg/__init__.py +43 -0
- matrice_analytics/post_processing/face_reg/compare_similarity.py +556 -0
- matrice_analytics/post_processing/face_reg/embedding_manager.py +950 -0
- matrice_analytics/post_processing/face_reg/face_recognition.py +2234 -0
- matrice_analytics/post_processing/face_reg/face_recognition_client.py +606 -0
- matrice_analytics/post_processing/face_reg/people_activity_logging.py +321 -0
- matrice_analytics/post_processing/ocr/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/easyocr_extractor.py +250 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
- matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
- matrice_analytics/post_processing/ocr/postprocessing.py +270 -0
- matrice_analytics/post_processing/ocr/preprocessing.py +52 -0
- matrice_analytics/post_processing/post_processor.py +1175 -0
- matrice_analytics/post_processing/test_cases/__init__.py +1 -0
- matrice_analytics/post_processing/test_cases/run_tests.py +143 -0
- matrice_analytics/post_processing/test_cases/test_advanced_customer_service.py +841 -0
- matrice_analytics/post_processing/test_cases/test_basic_counting_tracking.py +523 -0
- matrice_analytics/post_processing/test_cases/test_comprehensive.py +531 -0
- matrice_analytics/post_processing/test_cases/test_config.py +852 -0
- matrice_analytics/post_processing/test_cases/test_customer_service.py +585 -0
- matrice_analytics/post_processing/test_cases/test_data_generators.py +583 -0
- matrice_analytics/post_processing/test_cases/test_people_counting.py +510 -0
- matrice_analytics/post_processing/test_cases/test_processor.py +524 -0
- matrice_analytics/post_processing/test_cases/test_usecases.py +165 -0
- matrice_analytics/post_processing/test_cases/test_utilities.py +356 -0
- matrice_analytics/post_processing/test_cases/test_utils.py +743 -0
- matrice_analytics/post_processing/usecases/Histopathological_Cancer_Detection_img.py +604 -0
- matrice_analytics/post_processing/usecases/__init__.py +267 -0
- matrice_analytics/post_processing/usecases/abandoned_object_detection.py +797 -0
- matrice_analytics/post_processing/usecases/advanced_customer_service.py +1601 -0
- matrice_analytics/post_processing/usecases/age_detection.py +842 -0
- matrice_analytics/post_processing/usecases/age_gender_detection.py +1085 -0
- matrice_analytics/post_processing/usecases/anti_spoofing_detection.py +656 -0
- matrice_analytics/post_processing/usecases/assembly_line_detection.py +841 -0
- matrice_analytics/post_processing/usecases/banana_defect_detection.py +624 -0
- matrice_analytics/post_processing/usecases/basic_counting_tracking.py +667 -0
- matrice_analytics/post_processing/usecases/blood_cancer_detection_img.py +881 -0
- matrice_analytics/post_processing/usecases/car_damage_detection.py +834 -0
- matrice_analytics/post_processing/usecases/car_part_segmentation.py +946 -0
- matrice_analytics/post_processing/usecases/car_service.py +1601 -0
- matrice_analytics/post_processing/usecases/cardiomegaly_classification.py +864 -0
- matrice_analytics/post_processing/usecases/cell_microscopy_segmentation.py +897 -0
- matrice_analytics/post_processing/usecases/chicken_pose_detection.py +648 -0
- matrice_analytics/post_processing/usecases/child_monitoring.py +814 -0
- matrice_analytics/post_processing/usecases/color/clip.py +660 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/merges.txt +48895 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/preprocessor_config.json +28 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/special_tokens_map.json +30 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer.json +245079 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer_config.json +32 -0
- matrice_analytics/post_processing/usecases/color/clip_processor/vocab.json +1 -0
- matrice_analytics/post_processing/usecases/color/color_map_utils.py +70 -0
- matrice_analytics/post_processing/usecases/color/color_mapper.py +468 -0
- matrice_analytics/post_processing/usecases/color_detection.py +1936 -0
- matrice_analytics/post_processing/usecases/color_map_utils.py +70 -0
- matrice_analytics/post_processing/usecases/concrete_crack_detection.py +827 -0
- matrice_analytics/post_processing/usecases/crop_weed_detection.py +781 -0
- matrice_analytics/post_processing/usecases/customer_service.py +1008 -0
- matrice_analytics/post_processing/usecases/defect_detection_products.py +936 -0
- matrice_analytics/post_processing/usecases/distracted_driver_detection.py +822 -0
- matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +585 -0
- matrice_analytics/post_processing/usecases/drowsy_driver_detection.py +829 -0
- matrice_analytics/post_processing/usecases/dwell_detection.py +829 -0
- matrice_analytics/post_processing/usecases/emergency_vehicle_detection.py +827 -0
- matrice_analytics/post_processing/usecases/face_emotion.py +813 -0
- matrice_analytics/post_processing/usecases/face_recognition.py +827 -0
- matrice_analytics/post_processing/usecases/fashion_detection.py +835 -0
- matrice_analytics/post_processing/usecases/field_mapping.py +902 -0
- matrice_analytics/post_processing/usecases/fire_detection.py +1146 -0
- matrice_analytics/post_processing/usecases/flare_analysis.py +836 -0
- matrice_analytics/post_processing/usecases/flower_segmentation.py +1006 -0
- matrice_analytics/post_processing/usecases/gas_leak_detection.py +837 -0
- matrice_analytics/post_processing/usecases/gender_detection.py +832 -0
- matrice_analytics/post_processing/usecases/human_activity_recognition.py +871 -0
- matrice_analytics/post_processing/usecases/intrusion_detection.py +1672 -0
- matrice_analytics/post_processing/usecases/leaf.py +821 -0
- matrice_analytics/post_processing/usecases/leaf_disease.py +840 -0
- matrice_analytics/post_processing/usecases/leak_detection.py +837 -0
- matrice_analytics/post_processing/usecases/license_plate_detection.py +1188 -0
- matrice_analytics/post_processing/usecases/license_plate_monitoring.py +1781 -0
- matrice_analytics/post_processing/usecases/litter_monitoring.py +717 -0
- matrice_analytics/post_processing/usecases/mask_detection.py +869 -0
- matrice_analytics/post_processing/usecases/natural_disaster.py +907 -0
- matrice_analytics/post_processing/usecases/parking.py +787 -0
- matrice_analytics/post_processing/usecases/parking_space_detection.py +822 -0
- matrice_analytics/post_processing/usecases/pcb_defect_detection.py +888 -0
- matrice_analytics/post_processing/usecases/pedestrian_detection.py +808 -0
- matrice_analytics/post_processing/usecases/people_counting.py +706 -0
- matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
- matrice_analytics/post_processing/usecases/people_tracking.py +1842 -0
- matrice_analytics/post_processing/usecases/pipeline_detection.py +605 -0
- matrice_analytics/post_processing/usecases/plaque_segmentation_img.py +874 -0
- matrice_analytics/post_processing/usecases/pothole_segmentation.py +915 -0
- matrice_analytics/post_processing/usecases/ppe_compliance.py +645 -0
- matrice_analytics/post_processing/usecases/price_tag_detection.py +822 -0
- matrice_analytics/post_processing/usecases/proximity_detection.py +1901 -0
- matrice_analytics/post_processing/usecases/road_lane_detection.py +623 -0
- matrice_analytics/post_processing/usecases/road_traffic_density.py +832 -0
- matrice_analytics/post_processing/usecases/road_view_segmentation.py +915 -0
- matrice_analytics/post_processing/usecases/shelf_inventory_detection.py +583 -0
- matrice_analytics/post_processing/usecases/shoplifting_detection.py +822 -0
- matrice_analytics/post_processing/usecases/shopping_cart_analysis.py +899 -0
- matrice_analytics/post_processing/usecases/skin_cancer_classification_img.py +864 -0
- matrice_analytics/post_processing/usecases/smoker_detection.py +833 -0
- matrice_analytics/post_processing/usecases/solar_panel.py +810 -0
- matrice_analytics/post_processing/usecases/suspicious_activity_detection.py +1030 -0
- matrice_analytics/post_processing/usecases/template_usecase.py +380 -0
- matrice_analytics/post_processing/usecases/theft_detection.py +648 -0
- matrice_analytics/post_processing/usecases/traffic_sign_monitoring.py +724 -0
- matrice_analytics/post_processing/usecases/underground_pipeline_defect_detection.py +775 -0
- matrice_analytics/post_processing/usecases/underwater_pollution_detection.py +842 -0
- matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1029 -0
- matrice_analytics/post_processing/usecases/warehouse_object_segmentation.py +899 -0
- matrice_analytics/post_processing/usecases/waterbody_segmentation.py +923 -0
- matrice_analytics/post_processing/usecases/weapon_detection.py +771 -0
- matrice_analytics/post_processing/usecases/weld_defect_detection.py +615 -0
- matrice_analytics/post_processing/usecases/wildlife_monitoring.py +898 -0
- matrice_analytics/post_processing/usecases/windmill_maintenance.py +834 -0
- matrice_analytics/post_processing/usecases/wound_segmentation.py +856 -0
- matrice_analytics/post_processing/utils/__init__.py +150 -0
- matrice_analytics/post_processing/utils/advanced_counting_utils.py +400 -0
- matrice_analytics/post_processing/utils/advanced_helper_utils.py +317 -0
- matrice_analytics/post_processing/utils/advanced_tracking_utils.py +461 -0
- matrice_analytics/post_processing/utils/alerting_utils.py +213 -0
- matrice_analytics/post_processing/utils/category_mapping_utils.py +94 -0
- matrice_analytics/post_processing/utils/color_utils.py +592 -0
- matrice_analytics/post_processing/utils/counting_utils.py +182 -0
- matrice_analytics/post_processing/utils/filter_utils.py +261 -0
- matrice_analytics/post_processing/utils/format_utils.py +293 -0
- matrice_analytics/post_processing/utils/geometry_utils.py +300 -0
- matrice_analytics/post_processing/utils/smoothing_utils.py +358 -0
- matrice_analytics/post_processing/utils/tracking_utils.py +234 -0
- matrice_analytics/py.typed +0 -0
- matrice_analytics-0.1.60.dist-info/METADATA +481 -0
- matrice_analytics-0.1.60.dist-info/RECORD +196 -0
- matrice_analytics-0.1.60.dist-info/WHEEL +5 -0
- matrice_analytics-0.1.60.dist-info/licenses/LICENSE.txt +21 -0
- matrice_analytics-0.1.60.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,841 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test cases for AdvancedCustomerServiceUseCase.
|
|
3
|
+
|
|
4
|
+
This module provides comprehensive tests for the advanced customer service use case,
|
|
5
|
+
covering journey analysis, queue management, staff analytics, and business intelligence.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
import time
|
|
10
|
+
from unittest.mock import patch, MagicMock
|
|
11
|
+
from typing import Dict, List, Any
|
|
12
|
+
|
|
13
|
+
from ..usecases.advanced_customer_service import AdvancedCustomerServiceUseCase
|
|
14
|
+
from ..core.base import ProcessingContext, ProcessingStatus
|
|
15
|
+
from ..core.config import CustomerServiceConfig, TrackingConfig, AlertConfig
|
|
16
|
+
from .test_utilities import BasePostProcessingTest
|
|
17
|
+
from .test_data_generators import (
|
|
18
|
+
create_detection_results,
|
|
19
|
+
create_tracking_results,
|
|
20
|
+
create_customer_service_areas,
|
|
21
|
+
create_large_dataset
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TestAdvancedCustomerServiceUseCase(BasePostProcessingTest):
|
|
26
|
+
"""Test AdvancedCustomerServiceUseCase functionality."""
|
|
27
|
+
|
|
28
|
+
def setUp(self):
|
|
29
|
+
"""Set up test environment."""
|
|
30
|
+
super().setUp()
|
|
31
|
+
self.use_case = AdvancedCustomerServiceUseCase()
|
|
32
|
+
self.areas = create_customer_service_areas()
|
|
33
|
+
self.config = CustomerServiceConfig(
|
|
34
|
+
customer_areas=self.areas["customer_areas"],
|
|
35
|
+
staff_areas=self.areas["staff_areas"],
|
|
36
|
+
service_areas=self.areas["service_areas"],
|
|
37
|
+
staff_categories=["staff", "employee"],
|
|
38
|
+
customer_categories=["customer", "person"],
|
|
39
|
+
confidence_threshold=0.5
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def test_use_case_initialization(self):
|
|
43
|
+
"""Test use case initialization."""
|
|
44
|
+
assert self.use_case.name == "advanced_customer_service"
|
|
45
|
+
assert self.use_case.category == "sales"
|
|
46
|
+
assert hasattr(self.use_case, 'logger')
|
|
47
|
+
|
|
48
|
+
# Check advanced tracking structures
|
|
49
|
+
assert hasattr(self.use_case, 'customer_occupancy')
|
|
50
|
+
assert hasattr(self.use_case, 'staff_occupancy')
|
|
51
|
+
assert hasattr(self.use_case, 'service_occupancy')
|
|
52
|
+
assert hasattr(self.use_case, 'customer_journey')
|
|
53
|
+
assert hasattr(self.use_case, 'staff_availability')
|
|
54
|
+
assert hasattr(self.use_case, 'JOURNEY_STATES')
|
|
55
|
+
|
|
56
|
+
def test_get_config_schema(self):
|
|
57
|
+
"""Test configuration schema retrieval."""
|
|
58
|
+
schema = self.use_case.get_config_schema()
|
|
59
|
+
|
|
60
|
+
assert schema["type"] == "object"
|
|
61
|
+
assert "properties" in schema
|
|
62
|
+
assert "confidence_threshold" in schema["properties"]
|
|
63
|
+
assert "customer_areas" in schema["properties"]
|
|
64
|
+
assert "staff_areas" in schema["properties"]
|
|
65
|
+
assert "service_areas" in schema["properties"]
|
|
66
|
+
assert "staff_categories" in schema["properties"]
|
|
67
|
+
assert "customer_categories" in schema["properties"]
|
|
68
|
+
assert "service_proximity_threshold" in schema["properties"]
|
|
69
|
+
assert "enable_journey_analysis" in schema["properties"]
|
|
70
|
+
assert "enable_queue_analytics" in schema["properties"]
|
|
71
|
+
|
|
72
|
+
# Check default values
|
|
73
|
+
assert schema["properties"]["confidence_threshold"]["default"] == 0.5
|
|
74
|
+
assert schema["properties"]["service_proximity_threshold"]["default"] == 100.0
|
|
75
|
+
|
|
76
|
+
def test_create_default_config(self):
|
|
77
|
+
"""Test default config creation."""
|
|
78
|
+
config = self.use_case.create_default_config()
|
|
79
|
+
|
|
80
|
+
assert isinstance(config, CustomerServiceConfig)
|
|
81
|
+
assert config.category == "sales"
|
|
82
|
+
assert config.usecase == "advanced_customer_service"
|
|
83
|
+
assert config.confidence_threshold == 0.5
|
|
84
|
+
assert config.enable_tracking is True
|
|
85
|
+
assert config.staff_categories == ["staff", "employee"]
|
|
86
|
+
assert config.customer_categories == ["customer", "person"]
|
|
87
|
+
|
|
88
|
+
def test_create_default_config_with_overrides(self):
|
|
89
|
+
"""Test default config creation with overrides."""
|
|
90
|
+
customer_areas = {"lobby": [[0, 0], [100, 0], [100, 100], [0, 100]]}
|
|
91
|
+
config = self.use_case.create_default_config(
|
|
92
|
+
category="retail",
|
|
93
|
+
confidence_threshold=0.8,
|
|
94
|
+
customer_areas=customer_areas,
|
|
95
|
+
service_proximity_threshold=150.0
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
assert config.category == "retail"
|
|
99
|
+
assert config.confidence_threshold == 0.8
|
|
100
|
+
assert config.customer_areas == customer_areas
|
|
101
|
+
assert config.service_proximity_threshold == 150.0
|
|
102
|
+
|
|
103
|
+
def test_process_mixed_detection_data_success(self):
|
|
104
|
+
"""Test processing mixed staff and customer detection data."""
|
|
105
|
+
# Create mixed detection data
|
|
106
|
+
detection_data = []
|
|
107
|
+
|
|
108
|
+
# Add staff detections
|
|
109
|
+
staff_data = create_detection_results(
|
|
110
|
+
num_detections=3,
|
|
111
|
+
categories=["staff"],
|
|
112
|
+
confidence_range=(0.7, 0.9),
|
|
113
|
+
bbox_range=((10, 10, 50, 50), (60, 60, 100, 100))
|
|
114
|
+
)
|
|
115
|
+
detection_data.extend(staff_data)
|
|
116
|
+
|
|
117
|
+
# Add customer detections
|
|
118
|
+
customer_data = create_detection_results(
|
|
119
|
+
num_detections=8,
|
|
120
|
+
categories=["customer", "person"],
|
|
121
|
+
confidence_range=(0.6, 0.9),
|
|
122
|
+
bbox_range=((110, 110, 150, 150), (160, 160, 200, 200))
|
|
123
|
+
)
|
|
124
|
+
detection_data.extend(customer_data)
|
|
125
|
+
|
|
126
|
+
result = self.use_case.process(detection_data, self.config)
|
|
127
|
+
|
|
128
|
+
self.assert_processing_result_valid(result)
|
|
129
|
+
assert result.status == ProcessingStatus.SUCCESS
|
|
130
|
+
assert result.usecase == "advanced_customer_service"
|
|
131
|
+
|
|
132
|
+
# Check comprehensive analytics structure
|
|
133
|
+
assert "customer_queue_analytics" in result.data
|
|
134
|
+
assert "staff_management" in result.data
|
|
135
|
+
assert "service_area_analytics" in result.data
|
|
136
|
+
assert "customer_journey_analytics" in result.data
|
|
137
|
+
assert "business_intelligence" in result.data
|
|
138
|
+
|
|
139
|
+
# Verify staff and customer counts
|
|
140
|
+
staff_count = result.data["staff_management"]["total_staff_present"]
|
|
141
|
+
customer_count = result.data["customer_queue_analytics"]["total_customers"]
|
|
142
|
+
assert staff_count > 0
|
|
143
|
+
assert customer_count > 0
|
|
144
|
+
|
|
145
|
+
def test_process_tracking_data_with_journey_analysis(self):
|
|
146
|
+
"""Test processing tracking data with customer journey analysis."""
|
|
147
|
+
tracking_data = create_tracking_results(
|
|
148
|
+
num_tracks=5,
|
|
149
|
+
categories=["customer", "staff"],
|
|
150
|
+
frames=10
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
config = CustomerServiceConfig(
|
|
154
|
+
customer_areas=self.areas["customer_areas"],
|
|
155
|
+
staff_areas=self.areas["staff_areas"],
|
|
156
|
+
service_areas=self.areas["service_areas"],
|
|
157
|
+
staff_categories=["staff"],
|
|
158
|
+
customer_categories=["customer"],
|
|
159
|
+
enable_tracking=True
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
result = self.use_case.process(tracking_data, config)
|
|
163
|
+
|
|
164
|
+
self.assert_processing_result_valid(result)
|
|
165
|
+
|
|
166
|
+
# Check journey analysis
|
|
167
|
+
journey_analytics = result.data["customer_journey_analytics"]
|
|
168
|
+
assert "active_journeys" in journey_analytics
|
|
169
|
+
assert "completed_journeys" in journey_analytics
|
|
170
|
+
assert "journey_states" in journey_analytics
|
|
171
|
+
|
|
172
|
+
# Check for journey state tracking
|
|
173
|
+
if journey_analytics["active_journeys"] > 0:
|
|
174
|
+
assert "journey_states" in journey_analytics
|
|
175
|
+
states = journey_analytics["journey_states"]
|
|
176
|
+
assert isinstance(states, dict)
|
|
177
|
+
|
|
178
|
+
def test_process_with_queue_analytics(self):
|
|
179
|
+
"""Test processing with queue analytics enabled."""
|
|
180
|
+
detection_data = create_detection_results(
|
|
181
|
+
num_detections=15,
|
|
182
|
+
categories=["customer", "person"],
|
|
183
|
+
confidence_range=(0.6, 0.9)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
config = CustomerServiceConfig(
|
|
187
|
+
customer_areas=self.areas["customer_areas"],
|
|
188
|
+
staff_areas=self.areas["staff_areas"],
|
|
189
|
+
service_areas=self.areas["service_areas"],
|
|
190
|
+
customer_categories=["customer", "person"],
|
|
191
|
+
staff_categories=["staff"],
|
|
192
|
+
enable_queue_analytics=True
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
result = self.use_case.process(detection_data, config)
|
|
196
|
+
|
|
197
|
+
self.assert_processing_result_valid(result)
|
|
198
|
+
|
|
199
|
+
# Check queue analytics
|
|
200
|
+
queue_analytics = result.data["customer_queue_analytics"]
|
|
201
|
+
assert "total_customers" in queue_analytics
|
|
202
|
+
assert "customers_by_area" in queue_analytics
|
|
203
|
+
assert "average_wait_time" in queue_analytics
|
|
204
|
+
assert "queue_length_by_area" in queue_analytics
|
|
205
|
+
|
|
206
|
+
# Verify queue metrics
|
|
207
|
+
assert queue_analytics["total_customers"] >= 0
|
|
208
|
+
assert isinstance(queue_analytics["customers_by_area"], dict)
|
|
209
|
+
|
|
210
|
+
def test_process_with_staff_management_analytics(self):
|
|
211
|
+
"""Test processing with staff management analytics."""
|
|
212
|
+
# Create mixed data with staff and customers
|
|
213
|
+
detection_data = []
|
|
214
|
+
|
|
215
|
+
# Add staff detections in staff areas
|
|
216
|
+
staff_data = create_detection_results(
|
|
217
|
+
num_detections=4,
|
|
218
|
+
categories=["staff", "employee"],
|
|
219
|
+
confidence_range=(0.7, 0.9)
|
|
220
|
+
)
|
|
221
|
+
detection_data.extend(staff_data)
|
|
222
|
+
|
|
223
|
+
# Add customer detections
|
|
224
|
+
customer_data = create_detection_results(
|
|
225
|
+
num_detections=12,
|
|
226
|
+
categories=["customer"],
|
|
227
|
+
confidence_range=(0.6, 0.9)
|
|
228
|
+
)
|
|
229
|
+
detection_data.extend(customer_data)
|
|
230
|
+
|
|
231
|
+
result = self.use_case.process(detection_data, self.config)
|
|
232
|
+
|
|
233
|
+
self.assert_processing_result_valid(result)
|
|
234
|
+
|
|
235
|
+
# Check staff management analytics
|
|
236
|
+
staff_mgmt = result.data["staff_management"]
|
|
237
|
+
assert "total_staff_present" in staff_mgmt
|
|
238
|
+
assert "staff_by_area" in staff_mgmt
|
|
239
|
+
assert "staff_availability" in staff_mgmt
|
|
240
|
+
assert "staff_efficiency" in staff_mgmt
|
|
241
|
+
assert "active_services" in staff_mgmt
|
|
242
|
+
|
|
243
|
+
# Verify staff metrics
|
|
244
|
+
assert staff_mgmt["total_staff_present"] >= 0
|
|
245
|
+
assert isinstance(staff_mgmt["staff_by_area"], dict)
|
|
246
|
+
assert isinstance(staff_mgmt["staff_availability"], dict)
|
|
247
|
+
|
|
248
|
+
def test_process_with_service_area_analytics(self):
|
|
249
|
+
"""Test processing with service area analytics."""
|
|
250
|
+
detection_data = create_detection_results(
|
|
251
|
+
num_detections=20,
|
|
252
|
+
categories=["customer", "staff"],
|
|
253
|
+
confidence_range=(0.6, 0.9)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
result = self.use_case.process(detection_data, self.config)
|
|
257
|
+
|
|
258
|
+
self.assert_processing_result_valid(result)
|
|
259
|
+
|
|
260
|
+
# Check service area analytics
|
|
261
|
+
service_analytics = result.data["service_area_analytics"]
|
|
262
|
+
assert "total_service_interactions" in service_analytics
|
|
263
|
+
assert "service_areas_occupancy" in service_analytics
|
|
264
|
+
assert "average_service_time" in service_analytics
|
|
265
|
+
assert "service_efficiency" in service_analytics
|
|
266
|
+
|
|
267
|
+
# Verify service metrics
|
|
268
|
+
assert service_analytics["total_service_interactions"] >= 0
|
|
269
|
+
assert isinstance(service_analytics["service_areas_occupancy"], dict)
|
|
270
|
+
|
|
271
|
+
def test_process_with_business_intelligence(self):
|
|
272
|
+
"""Test processing with business intelligence metrics."""
|
|
273
|
+
detection_data = create_detection_results(
|
|
274
|
+
num_detections=30,
|
|
275
|
+
categories=["customer", "staff"],
|
|
276
|
+
confidence_range=(0.6, 0.9)
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
result = self.use_case.process(detection_data, self.config)
|
|
280
|
+
|
|
281
|
+
self.assert_processing_result_valid(result)
|
|
282
|
+
|
|
283
|
+
# Check business intelligence
|
|
284
|
+
bi = result.data["business_intelligence"]
|
|
285
|
+
assert "customer_to_staff_ratio" in bi
|
|
286
|
+
assert "service_capacity_utilization" in bi
|
|
287
|
+
assert "peak_occupancy_times" in bi
|
|
288
|
+
assert "customer_flow_patterns" in bi
|
|
289
|
+
assert "service_bottlenecks" in bi
|
|
290
|
+
|
|
291
|
+
# Verify BI metrics
|
|
292
|
+
assert isinstance(bi["customer_to_staff_ratio"], (int, float))
|
|
293
|
+
assert isinstance(bi["service_capacity_utilization"], (int, float))
|
|
294
|
+
assert isinstance(bi["peak_occupancy_times"], dict)
|
|
295
|
+
|
|
296
|
+
def test_process_with_alerts(self):
|
|
297
|
+
"""Test processing with alert generation."""
|
|
298
|
+
detection_data = create_detection_results(
|
|
299
|
+
num_detections=25,
|
|
300
|
+
categories=["customer", "staff"]
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
alert_config = AlertConfig(
|
|
304
|
+
occupancy_thresholds={"customer_area": 15, "service_area": 8},
|
|
305
|
+
dwell_time_threshold=300.0, # 5 minutes
|
|
306
|
+
service_time_threshold=600.0, # 10 minutes
|
|
307
|
+
alert_cooldown=60.0
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
config = CustomerServiceConfig(
|
|
311
|
+
customer_areas=self.areas["customer_areas"],
|
|
312
|
+
staff_areas=self.areas["staff_areas"],
|
|
313
|
+
service_areas=self.areas["service_areas"],
|
|
314
|
+
customer_categories=["customer"],
|
|
315
|
+
staff_categories=["staff"],
|
|
316
|
+
alert_config=alert_config
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
result = self.use_case.process(detection_data, config)
|
|
320
|
+
|
|
321
|
+
self.assert_processing_result_valid(result)
|
|
322
|
+
|
|
323
|
+
# Check for alerts if thresholds are exceeded
|
|
324
|
+
if "alerts" in result.data and len(result.data["alerts"]) > 0:
|
|
325
|
+
alert = result.data["alerts"][0]
|
|
326
|
+
assert "type" in alert
|
|
327
|
+
assert "message" in alert
|
|
328
|
+
assert "timestamp" in alert
|
|
329
|
+
assert alert["type"] in ["occupancy_threshold", "service_time", "dwell_time"]
|
|
330
|
+
|
|
331
|
+
def test_process_empty_data(self):
|
|
332
|
+
"""Test processing empty data."""
|
|
333
|
+
result = self.use_case.process([], self.config)
|
|
334
|
+
|
|
335
|
+
self.assert_processing_result_valid(result)
|
|
336
|
+
|
|
337
|
+
# Should have zero counts but valid structure
|
|
338
|
+
assert result.data["customer_queue_analytics"]["total_customers"] == 0
|
|
339
|
+
assert result.data["staff_management"]["total_staff_present"] == 0
|
|
340
|
+
assert result.data["service_area_analytics"]["total_service_interactions"] == 0
|
|
341
|
+
assert len(result.insights) > 0
|
|
342
|
+
assert "No activity detected" in result.summary or "No objects detected" in result.summary
|
|
343
|
+
|
|
344
|
+
def test_process_invalid_data_format(self):
|
|
345
|
+
"""Test processing invalid data format."""
|
|
346
|
+
invalid_data = "not_a_list_or_dict"
|
|
347
|
+
|
|
348
|
+
result = self.use_case.process(invalid_data, self.config)
|
|
349
|
+
|
|
350
|
+
assert result.status == ProcessingStatus.ERROR
|
|
351
|
+
assert result.error_message is not None
|
|
352
|
+
assert "Invalid data format" in result.error_message or "Failed to process" in result.error_message
|
|
353
|
+
|
|
354
|
+
def test_customer_journey_state_transitions(self):
|
|
355
|
+
"""Test customer journey state transitions."""
|
|
356
|
+
# Create tracking data that simulates customer movement
|
|
357
|
+
tracking_data = [
|
|
358
|
+
{
|
|
359
|
+
"track_id": 1,
|
|
360
|
+
"bbox": [10, 10, 50, 50],
|
|
361
|
+
"confidence": 0.8,
|
|
362
|
+
"category": "customer",
|
|
363
|
+
"frame_id": 1
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
"track_id": 1,
|
|
367
|
+
"bbox": [60, 60, 100, 100],
|
|
368
|
+
"confidence": 0.8,
|
|
369
|
+
"category": "customer",
|
|
370
|
+
"frame_id": 2
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"track_id": 1,
|
|
374
|
+
"bbox": [110, 110, 150, 150],
|
|
375
|
+
"confidence": 0.8,
|
|
376
|
+
"category": "customer",
|
|
377
|
+
"frame_id": 3
|
|
378
|
+
}
|
|
379
|
+
]
|
|
380
|
+
|
|
381
|
+
config = CustomerServiceConfig(
|
|
382
|
+
customer_areas=self.areas["customer_areas"],
|
|
383
|
+
staff_areas=self.areas["staff_areas"],
|
|
384
|
+
service_areas=self.areas["service_areas"],
|
|
385
|
+
customer_categories=["customer"],
|
|
386
|
+
staff_categories=["staff"],
|
|
387
|
+
enable_journey_analysis=True
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
result = self.use_case.process(tracking_data, config)
|
|
391
|
+
|
|
392
|
+
self.assert_processing_result_valid(result)
|
|
393
|
+
|
|
394
|
+
# Check journey analytics
|
|
395
|
+
journey_analytics = result.data["customer_journey_analytics"]
|
|
396
|
+
assert "active_journeys" in journey_analytics
|
|
397
|
+
|
|
398
|
+
# Should track the customer journey
|
|
399
|
+
if journey_analytics["active_journeys"] > 0:
|
|
400
|
+
assert "journey_states" in journey_analytics
|
|
401
|
+
|
|
402
|
+
def test_staff_efficiency_calculation(self):
|
|
403
|
+
"""Test staff efficiency calculation."""
|
|
404
|
+
# Create data with staff serving customers
|
|
405
|
+
detection_data = []
|
|
406
|
+
|
|
407
|
+
# Add staff detections
|
|
408
|
+
staff_data = [
|
|
409
|
+
{"bbox": [10, 10, 50, 50], "confidence": 0.9, "category": "staff", "track_id": 101},
|
|
410
|
+
{"bbox": [200, 200, 240, 240], "confidence": 0.9, "category": "staff", "track_id": 102}
|
|
411
|
+
]
|
|
412
|
+
detection_data.extend(staff_data)
|
|
413
|
+
|
|
414
|
+
# Add customer detections near staff
|
|
415
|
+
customer_data = [
|
|
416
|
+
{"bbox": [15, 15, 45, 45], "confidence": 0.8, "category": "customer", "track_id": 201},
|
|
417
|
+
{"bbox": [25, 25, 55, 55], "confidence": 0.8, "category": "customer", "track_id": 202},
|
|
418
|
+
{"bbox": [205, 205, 235, 235], "confidence": 0.8, "category": "customer", "track_id": 203}
|
|
419
|
+
]
|
|
420
|
+
detection_data.extend(customer_data)
|
|
421
|
+
|
|
422
|
+
config = CustomerServiceConfig(
|
|
423
|
+
customer_areas=self.areas["customer_areas"],
|
|
424
|
+
staff_areas=self.areas["staff_areas"],
|
|
425
|
+
service_areas=self.areas["service_areas"],
|
|
426
|
+
customer_categories=["customer"],
|
|
427
|
+
staff_categories=["staff"],
|
|
428
|
+
service_proximity_threshold=50.0 # Close proximity
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
result = self.use_case.process(detection_data, config)
|
|
432
|
+
|
|
433
|
+
self.assert_processing_result_valid(result)
|
|
434
|
+
|
|
435
|
+
# Check staff efficiency metrics
|
|
436
|
+
staff_mgmt = result.data["staff_management"]
|
|
437
|
+
assert "staff_efficiency" in staff_mgmt
|
|
438
|
+
|
|
439
|
+
# Should have efficiency data if staff are serving customers
|
|
440
|
+
efficiency = staff_mgmt["staff_efficiency"]
|
|
441
|
+
assert isinstance(efficiency, dict)
|
|
442
|
+
|
|
443
|
+
def test_service_proximity_detection(self):
|
|
444
|
+
"""Test service proximity detection between staff and customers."""
|
|
445
|
+
# Create data with staff and customers in close proximity
|
|
446
|
+
detection_data = [
|
|
447
|
+
# Staff member
|
|
448
|
+
{"bbox": [100, 100, 140, 140], "confidence": 0.9, "category": "staff", "track_id": 1},
|
|
449
|
+
# Customer very close to staff (should be detected as service interaction)
|
|
450
|
+
{"bbox": [110, 110, 150, 150], "confidence": 0.8, "category": "customer", "track_id": 2},
|
|
451
|
+
# Customer far from staff (should not be service interaction)
|
|
452
|
+
{"bbox": [300, 300, 340, 340], "confidence": 0.8, "category": "customer", "track_id": 3}
|
|
453
|
+
]
|
|
454
|
+
|
|
455
|
+
config = CustomerServiceConfig(
|
|
456
|
+
customer_areas=self.areas["customer_areas"],
|
|
457
|
+
staff_areas=self.areas["staff_areas"],
|
|
458
|
+
service_areas=self.areas["service_areas"],
|
|
459
|
+
customer_categories=["customer"],
|
|
460
|
+
staff_categories=["staff"],
|
|
461
|
+
service_proximity_threshold=80.0 # 80 pixel threshold
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
result = self.use_case.process(detection_data, config)
|
|
465
|
+
|
|
466
|
+
self.assert_processing_result_valid(result)
|
|
467
|
+
|
|
468
|
+
# Check service interactions
|
|
469
|
+
service_analytics = result.data["service_area_analytics"]
|
|
470
|
+
interactions = service_analytics["total_service_interactions"]
|
|
471
|
+
|
|
472
|
+
# Should detect at least one service interaction (staff + close customer)
|
|
473
|
+
assert interactions >= 0 # May be 0 if proximity calculation differs
|
|
474
|
+
|
|
475
|
+
def test_peak_occupancy_tracking(self):
|
|
476
|
+
"""Test peak occupancy tracking."""
|
|
477
|
+
detection_data = create_detection_results(
|
|
478
|
+
num_detections=40,
|
|
479
|
+
categories=["customer", "staff"],
|
|
480
|
+
confidence_range=(0.6, 0.9)
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
result = self.use_case.process(detection_data, self.config)
|
|
484
|
+
|
|
485
|
+
self.assert_processing_result_valid(result)
|
|
486
|
+
|
|
487
|
+
# Check peak occupancy in business intelligence
|
|
488
|
+
bi = result.data["business_intelligence"]
|
|
489
|
+
peak_times = bi["peak_occupancy_times"]
|
|
490
|
+
|
|
491
|
+
assert isinstance(peak_times, dict)
|
|
492
|
+
# Should track current timestamp as peak if significant activity
|
|
493
|
+
if result.data["customer_queue_analytics"]["total_customers"] > 0:
|
|
494
|
+
assert len(peak_times) >= 0
|
|
495
|
+
|
|
496
|
+
def test_customer_flow_patterns(self):
|
|
497
|
+
"""Test customer flow pattern analysis."""
|
|
498
|
+
# Create tracking data showing customer movement patterns
|
|
499
|
+
tracking_data = []
|
|
500
|
+
|
|
501
|
+
# Simulate customers moving through different areas
|
|
502
|
+
for track_id in range(1, 6):
|
|
503
|
+
for frame in range(1, 4):
|
|
504
|
+
x_offset = frame * 50
|
|
505
|
+
y_offset = track_id * 30
|
|
506
|
+
tracking_data.append({
|
|
507
|
+
"track_id": track_id,
|
|
508
|
+
"bbox": [x_offset, y_offset, x_offset + 40, y_offset + 40],
|
|
509
|
+
"confidence": 0.8,
|
|
510
|
+
"category": "customer",
|
|
511
|
+
"frame_id": frame
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
config = CustomerServiceConfig(
|
|
515
|
+
customer_areas=self.areas["customer_areas"],
|
|
516
|
+
staff_areas=self.areas["staff_areas"],
|
|
517
|
+
service_areas=self.areas["service_areas"],
|
|
518
|
+
customer_categories=["customer"],
|
|
519
|
+
staff_categories=["staff"],
|
|
520
|
+
enable_journey_analysis=True
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
result = self.use_case.process(tracking_data, config)
|
|
524
|
+
|
|
525
|
+
self.assert_processing_result_valid(result)
|
|
526
|
+
|
|
527
|
+
# Check customer flow patterns
|
|
528
|
+
bi = result.data["business_intelligence"]
|
|
529
|
+
flow_patterns = bi["customer_flow_patterns"]
|
|
530
|
+
|
|
531
|
+
assert isinstance(flow_patterns, dict)
|
|
532
|
+
# Should have some flow analysis if customers are moving
|
|
533
|
+
if result.data["customer_queue_analytics"]["total_customers"] > 0:
|
|
534
|
+
assert "movement_patterns" in flow_patterns or len(flow_patterns) >= 0
|
|
535
|
+
|
|
536
|
+
def test_process_with_context(self):
|
|
537
|
+
"""Test processing with context information."""
|
|
538
|
+
detection_data = create_detection_results(10, ["customer", "staff"])
|
|
539
|
+
context = ProcessingContext(
|
|
540
|
+
input_size=len(detection_data),
|
|
541
|
+
confidence_threshold=0.5,
|
|
542
|
+
enable_tracking=True,
|
|
543
|
+
enable_analytics=True,
|
|
544
|
+
metadata={"location": "retail_store", "camera_id": "cam_01"}
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
result = self.use_case.process(detection_data, self.config, context)
|
|
548
|
+
|
|
549
|
+
self.assert_processing_result_valid(result)
|
|
550
|
+
assert result.context == context
|
|
551
|
+
assert result.processing_time > 0
|
|
552
|
+
assert result.context.processing_time is not None
|
|
553
|
+
|
|
554
|
+
def test_process_performance_large_dataset(self):
|
|
555
|
+
"""Test processing performance with large dataset."""
|
|
556
|
+
large_data = create_large_dataset(500, ["customer", "staff"])
|
|
557
|
+
|
|
558
|
+
start_time = time.time()
|
|
559
|
+
result = self.use_case.process(large_data, self.config)
|
|
560
|
+
processing_time = time.time() - start_time
|
|
561
|
+
|
|
562
|
+
self.assert_processing_result_valid(result)
|
|
563
|
+
assert processing_time < 10.0 # Should process within 10 seconds
|
|
564
|
+
|
|
565
|
+
# Check that all analytics components are present
|
|
566
|
+
assert "customer_queue_analytics" in result.data
|
|
567
|
+
assert "staff_management" in result.data
|
|
568
|
+
assert "service_area_analytics" in result.data
|
|
569
|
+
assert "business_intelligence" in result.data
|
|
570
|
+
|
|
571
|
+
# Check performance metrics
|
|
572
|
+
assert "processing_time" in result.metrics
|
|
573
|
+
assert result.metrics["processing_time"] > 0
|
|
574
|
+
|
|
575
|
+
def test_insights_generation_comprehensive(self):
|
|
576
|
+
"""Test comprehensive insight generation."""
|
|
577
|
+
detection_data = create_detection_results(
|
|
578
|
+
num_detections=35,
|
|
579
|
+
categories=["customer", "staff"],
|
|
580
|
+
confidence_range=(0.6, 0.9)
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
result = self.use_case.process(detection_data, self.config)
|
|
584
|
+
|
|
585
|
+
self.assert_processing_result_valid(result)
|
|
586
|
+
assert len(result.insights) > 0
|
|
587
|
+
|
|
588
|
+
# Check for different types of insights
|
|
589
|
+
insight_text = " ".join(result.insights).lower()
|
|
590
|
+
|
|
591
|
+
# Should have customer-related insights
|
|
592
|
+
assert any(word in insight_text for word in ["customer", "customers", "service", "staff"])
|
|
593
|
+
|
|
594
|
+
# Should provide business intelligence insights
|
|
595
|
+
if result.data["customer_queue_analytics"]["total_customers"] > 0:
|
|
596
|
+
assert len(result.insights) >= 2 # Multiple insights for comprehensive analysis
|
|
597
|
+
|
|
598
|
+
def test_metrics_calculation_comprehensive(self):
|
|
599
|
+
"""Test comprehensive metrics calculation."""
|
|
600
|
+
detection_data = create_detection_results(25, ["customer", "staff"])
|
|
601
|
+
|
|
602
|
+
result = self.use_case.process(detection_data, self.config)
|
|
603
|
+
|
|
604
|
+
self.assert_processing_result_valid(result)
|
|
605
|
+
assert "metrics" in result.__dict__
|
|
606
|
+
|
|
607
|
+
# Check for comprehensive metrics
|
|
608
|
+
expected_metrics = [
|
|
609
|
+
"total_objects_processed",
|
|
610
|
+
"unique_categories",
|
|
611
|
+
"processing_time",
|
|
612
|
+
"customer_count",
|
|
613
|
+
"staff_count",
|
|
614
|
+
"service_interactions",
|
|
615
|
+
"analytics_computed"
|
|
616
|
+
]
|
|
617
|
+
|
|
618
|
+
for metric in expected_metrics:
|
|
619
|
+
if metric in result.metrics:
|
|
620
|
+
assert result.metrics[metric] >= 0
|
|
621
|
+
|
|
622
|
+
def test_error_handling_malformed_areas(self):
|
|
623
|
+
"""Test error handling with malformed area definitions."""
|
|
624
|
+
malformed_config = CustomerServiceConfig(
|
|
625
|
+
customer_areas={"invalid_area": [[0, 0], [100]]}, # Invalid polygon
|
|
626
|
+
staff_areas=self.areas["staff_areas"],
|
|
627
|
+
service_areas=self.areas["service_areas"],
|
|
628
|
+
customer_categories=["customer"],
|
|
629
|
+
staff_categories=["staff"]
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
detection_data = create_detection_results(5, ["customer"])
|
|
633
|
+
|
|
634
|
+
# Should handle gracefully or provide validation error
|
|
635
|
+
result = self.use_case.process(detection_data, malformed_config)
|
|
636
|
+
|
|
637
|
+
# Either processes successfully with warnings or fails with clear error
|
|
638
|
+
assert result.status in [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING, ProcessingStatus.ERROR]
|
|
639
|
+
if result.status == ProcessingStatus.WARNING:
|
|
640
|
+
assert len(result.warnings) > 0
|
|
641
|
+
elif result.status == ProcessingStatus.ERROR:
|
|
642
|
+
assert result.error_message is not None
|
|
643
|
+
|
|
644
|
+
def test_memory_stability_advanced(self):
|
|
645
|
+
"""Test memory stability with repeated advanced processing."""
|
|
646
|
+
detection_data = create_detection_results(100, ["customer", "staff"])
|
|
647
|
+
|
|
648
|
+
# Process multiple times to check for memory leaks
|
|
649
|
+
for i in range(5):
|
|
650
|
+
result = self.use_case.process(detection_data, self.config)
|
|
651
|
+
self.assert_processing_result_valid(result)
|
|
652
|
+
|
|
653
|
+
# Verify all advanced analytics are computed
|
|
654
|
+
assert "customer_queue_analytics" in result.data
|
|
655
|
+
assert "staff_management" in result.data
|
|
656
|
+
assert "service_area_analytics" in result.data
|
|
657
|
+
assert "business_intelligence" in result.data
|
|
658
|
+
|
|
659
|
+
# If we reach here without crashes, memory is likely stable
|
|
660
|
+
assert True
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
class TestAdvancedCustomerServiceIntegration(BasePostProcessingTest):
|
|
664
|
+
"""Integration tests for AdvancedCustomerServiceUseCase."""
|
|
665
|
+
|
|
666
|
+
def setUp(self):
|
|
667
|
+
"""Set up test environment."""
|
|
668
|
+
super().setUp()
|
|
669
|
+
self.use_case = AdvancedCustomerServiceUseCase()
|
|
670
|
+
self.areas = create_customer_service_areas()
|
|
671
|
+
|
|
672
|
+
def test_end_to_end_retail_store_scenario(self):
|
|
673
|
+
"""Test end-to-end retail store customer service scenario."""
|
|
674
|
+
# Simulate busy retail store with staff and customers
|
|
675
|
+
detection_data = []
|
|
676
|
+
|
|
677
|
+
# Add staff members in different areas
|
|
678
|
+
staff_data = [
|
|
679
|
+
{"bbox": [50, 50, 90, 90], "confidence": 0.9, "category": "staff", "track_id": 101},
|
|
680
|
+
{"bbox": [250, 250, 290, 290], "confidence": 0.9, "category": "staff", "track_id": 102},
|
|
681
|
+
{"bbox": [450, 450, 490, 490], "confidence": 0.9, "category": "staff", "track_id": 103}
|
|
682
|
+
]
|
|
683
|
+
detection_data.extend(staff_data)
|
|
684
|
+
|
|
685
|
+
# Add customers in various states
|
|
686
|
+
customer_data = create_detection_results(
|
|
687
|
+
num_detections=20,
|
|
688
|
+
categories=["customer"],
|
|
689
|
+
confidence_range=(0.6, 0.9)
|
|
690
|
+
)
|
|
691
|
+
detection_data.extend(customer_data)
|
|
692
|
+
|
|
693
|
+
config = CustomerServiceConfig(
|
|
694
|
+
category="retail",
|
|
695
|
+
customer_areas=self.areas["customer_areas"],
|
|
696
|
+
staff_areas=self.areas["staff_areas"],
|
|
697
|
+
service_areas=self.areas["service_areas"],
|
|
698
|
+
customer_categories=["customer"],
|
|
699
|
+
staff_categories=["staff"],
|
|
700
|
+
service_proximity_threshold=100.0,
|
|
701
|
+
enable_journey_analysis=True,
|
|
702
|
+
enable_queue_analytics=True,
|
|
703
|
+
alert_config=AlertConfig(
|
|
704
|
+
occupancy_thresholds={"customer_area": 15},
|
|
705
|
+
service_time_threshold=300.0
|
|
706
|
+
)
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
result = self.use_case.process(detection_data, config)
|
|
710
|
+
|
|
711
|
+
self.assert_processing_result_valid(result)
|
|
712
|
+
assert result.category == "retail"
|
|
713
|
+
|
|
714
|
+
# Verify comprehensive analytics
|
|
715
|
+
assert result.data["customer_queue_analytics"]["total_customers"] > 0
|
|
716
|
+
assert result.data["staff_management"]["total_staff_present"] == 3
|
|
717
|
+
|
|
718
|
+
# Check business intelligence metrics
|
|
719
|
+
bi = result.data["business_intelligence"]
|
|
720
|
+
assert bi["customer_to_staff_ratio"] > 0
|
|
721
|
+
assert isinstance(bi["service_capacity_utilization"], (int, float))
|
|
722
|
+
|
|
723
|
+
# Verify insights are business-relevant
|
|
724
|
+
insight_text = " ".join(result.insights).lower()
|
|
725
|
+
assert any(word in insight_text for word in ["customer", "staff", "service", "retail"])
|
|
726
|
+
|
|
727
|
+
def test_end_to_end_bank_branch_scenario(self):
|
|
728
|
+
"""Test end-to-end bank branch customer service scenario."""
|
|
729
|
+
# Simulate bank branch with tellers and customers
|
|
730
|
+
detection_data = []
|
|
731
|
+
|
|
732
|
+
# Add bank tellers (staff)
|
|
733
|
+
teller_data = [
|
|
734
|
+
{"bbox": [100, 100, 140, 140], "confidence": 0.95, "category": "staff", "track_id": 201},
|
|
735
|
+
{"bbox": [300, 100, 340, 140], "confidence": 0.95, "category": "staff", "track_id": 202}
|
|
736
|
+
]
|
|
737
|
+
detection_data.extend(teller_data)
|
|
738
|
+
|
|
739
|
+
# Add customers waiting and being served
|
|
740
|
+
customer_data = create_detection_results(
|
|
741
|
+
num_detections=12,
|
|
742
|
+
categories=["customer", "person"],
|
|
743
|
+
confidence_range=(0.7, 0.9)
|
|
744
|
+
)
|
|
745
|
+
detection_data.extend(customer_data)
|
|
746
|
+
|
|
747
|
+
config = CustomerServiceConfig(
|
|
748
|
+
category="banking",
|
|
749
|
+
customer_areas=self.areas["customer_areas"],
|
|
750
|
+
staff_areas=self.areas["staff_areas"],
|
|
751
|
+
service_areas=self.areas["service_areas"],
|
|
752
|
+
customer_categories=["customer", "person"],
|
|
753
|
+
staff_categories=["staff"],
|
|
754
|
+
service_proximity_threshold=80.0,
|
|
755
|
+
max_service_time=900.0, # 15 minutes max service
|
|
756
|
+
enable_journey_analysis=True,
|
|
757
|
+
enable_queue_analytics=True,
|
|
758
|
+
alert_config=AlertConfig(
|
|
759
|
+
occupancy_thresholds={"customer_area": 10},
|
|
760
|
+
service_time_threshold=600.0, # 10 minutes
|
|
761
|
+
dwell_time_threshold=1200.0 # 20 minutes max wait
|
|
762
|
+
)
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
result = self.use_case.process(detection_data, config)
|
|
766
|
+
|
|
767
|
+
self.assert_processing_result_valid(result)
|
|
768
|
+
assert result.category == "banking"
|
|
769
|
+
|
|
770
|
+
# Verify banking-specific analytics
|
|
771
|
+
queue_analytics = result.data["customer_queue_analytics"]
|
|
772
|
+
staff_mgmt = result.data["staff_management"]
|
|
773
|
+
|
|
774
|
+
assert queue_analytics["total_customers"] > 0
|
|
775
|
+
assert staff_mgmt["total_staff_present"] == 2
|
|
776
|
+
|
|
777
|
+
# Check for appropriate customer-to-staff ratio for banking
|
|
778
|
+
bi = result.data["business_intelligence"]
|
|
779
|
+
ratio = bi["customer_to_staff_ratio"]
|
|
780
|
+
assert ratio > 0
|
|
781
|
+
|
|
782
|
+
# Banking should have insights about wait times and service efficiency
|
|
783
|
+
insight_text = " ".join(result.insights).lower()
|
|
784
|
+
assert any(word in insight_text for word in ["customer", "service", "wait", "staff"])
|
|
785
|
+
|
|
786
|
+
def test_end_to_end_restaurant_scenario(self):
|
|
787
|
+
"""Test end-to-end restaurant customer service scenario."""
|
|
788
|
+
# Simulate restaurant with servers and diners
|
|
789
|
+
detection_data = []
|
|
790
|
+
|
|
791
|
+
# Add restaurant staff (servers, hosts)
|
|
792
|
+
server_data = [
|
|
793
|
+
{"bbox": [150, 150, 190, 190], "confidence": 0.9, "category": "staff", "track_id": 301},
|
|
794
|
+
{"bbox": [350, 350, 390, 390], "confidence": 0.9, "category": "staff", "track_id": 302},
|
|
795
|
+
{"bbox": [50, 350, 90, 390], "confidence": 0.9, "category": "staff", "track_id": 303}
|
|
796
|
+
]
|
|
797
|
+
detection_data.extend(server_data)
|
|
798
|
+
|
|
799
|
+
# Add customers/diners
|
|
800
|
+
customer_data = create_detection_results(
|
|
801
|
+
num_detections=25,
|
|
802
|
+
categories=["customer", "person"],
|
|
803
|
+
confidence_range=(0.6, 0.9)
|
|
804
|
+
)
|
|
805
|
+
detection_data.extend(customer_data)
|
|
806
|
+
|
|
807
|
+
config = CustomerServiceConfig(
|
|
808
|
+
category="restaurant",
|
|
809
|
+
customer_areas=self.areas["customer_areas"],
|
|
810
|
+
staff_areas=self.areas["staff_areas"],
|
|
811
|
+
service_areas=self.areas["service_areas"],
|
|
812
|
+
customer_categories=["customer", "person"],
|
|
813
|
+
staff_categories=["staff"],
|
|
814
|
+
service_proximity_threshold=120.0, # Larger area for restaurant service
|
|
815
|
+
max_service_time=3600.0, # 1 hour max dining time
|
|
816
|
+
buffer_time=5.0,
|
|
817
|
+
enable_journey_analysis=True,
|
|
818
|
+
enable_queue_analytics=True
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
result = self.use_case.process(detection_data, config)
|
|
822
|
+
|
|
823
|
+
self.assert_processing_result_valid(result)
|
|
824
|
+
assert result.category == "restaurant"
|
|
825
|
+
|
|
826
|
+
# Verify restaurant-specific metrics
|
|
827
|
+
assert result.data["customer_queue_analytics"]["total_customers"] > 0
|
|
828
|
+
assert result.data["staff_management"]["total_staff_present"] == 3
|
|
829
|
+
|
|
830
|
+
# Restaurant should have different service patterns
|
|
831
|
+
service_analytics = result.data["service_area_analytics"]
|
|
832
|
+
assert "total_service_interactions" in service_analytics
|
|
833
|
+
|
|
834
|
+
# Check for restaurant-relevant insights
|
|
835
|
+
insight_text = " ".join(result.insights).lower()
|
|
836
|
+
assert any(word in insight_text for word in ["customer", "staff", "service", "dining"])
|
|
837
|
+
|
|
838
|
+
# Business intelligence should reflect restaurant operations
|
|
839
|
+
bi = result.data["business_intelligence"]
|
|
840
|
+
assert bi["customer_to_staff_ratio"] > 0
|
|
841
|
+
assert isinstance(bi["service_capacity_utilization"], (int, float))
|