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.
Files changed (196) hide show
  1. matrice_analytics/__init__.py +28 -0
  2. matrice_analytics/boundary_drawing_internal/README.md +305 -0
  3. matrice_analytics/boundary_drawing_internal/__init__.py +45 -0
  4. matrice_analytics/boundary_drawing_internal/boundary_drawing_internal.py +1207 -0
  5. matrice_analytics/boundary_drawing_internal/boundary_drawing_tool.py +429 -0
  6. matrice_analytics/boundary_drawing_internal/boundary_tool_template.html +1036 -0
  7. matrice_analytics/boundary_drawing_internal/data/.gitignore +12 -0
  8. matrice_analytics/boundary_drawing_internal/example_usage.py +206 -0
  9. matrice_analytics/boundary_drawing_internal/usage/README.md +110 -0
  10. matrice_analytics/boundary_drawing_internal/usage/boundary_drawer_launcher.py +102 -0
  11. matrice_analytics/boundary_drawing_internal/usage/simple_boundary_launcher.py +107 -0
  12. matrice_analytics/post_processing/README.md +455 -0
  13. matrice_analytics/post_processing/__init__.py +732 -0
  14. matrice_analytics/post_processing/advanced_tracker/README.md +650 -0
  15. matrice_analytics/post_processing/advanced_tracker/__init__.py +17 -0
  16. matrice_analytics/post_processing/advanced_tracker/base.py +99 -0
  17. matrice_analytics/post_processing/advanced_tracker/config.py +77 -0
  18. matrice_analytics/post_processing/advanced_tracker/kalman_filter.py +370 -0
  19. matrice_analytics/post_processing/advanced_tracker/matching.py +195 -0
  20. matrice_analytics/post_processing/advanced_tracker/strack.py +230 -0
  21. matrice_analytics/post_processing/advanced_tracker/tracker.py +367 -0
  22. matrice_analytics/post_processing/config.py +146 -0
  23. matrice_analytics/post_processing/core/__init__.py +63 -0
  24. matrice_analytics/post_processing/core/base.py +704 -0
  25. matrice_analytics/post_processing/core/config.py +3291 -0
  26. matrice_analytics/post_processing/core/config_utils.py +925 -0
  27. matrice_analytics/post_processing/face_reg/__init__.py +43 -0
  28. matrice_analytics/post_processing/face_reg/compare_similarity.py +556 -0
  29. matrice_analytics/post_processing/face_reg/embedding_manager.py +950 -0
  30. matrice_analytics/post_processing/face_reg/face_recognition.py +2234 -0
  31. matrice_analytics/post_processing/face_reg/face_recognition_client.py +606 -0
  32. matrice_analytics/post_processing/face_reg/people_activity_logging.py +321 -0
  33. matrice_analytics/post_processing/ocr/__init__.py +0 -0
  34. matrice_analytics/post_processing/ocr/easyocr_extractor.py +250 -0
  35. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/__init__.py +9 -0
  36. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/__init__.py +4 -0
  37. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/cli.py +33 -0
  38. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/dataset_stats.py +139 -0
  39. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/export.py +398 -0
  40. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/train.py +447 -0
  41. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/utils.py +129 -0
  42. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/valid.py +93 -0
  43. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/validate_dataset.py +240 -0
  44. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_augmentation.py +176 -0
  45. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/cli/visualize_predictions.py +96 -0
  46. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/__init__.py +3 -0
  47. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/process.py +246 -0
  48. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/types.py +60 -0
  49. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/core/utils.py +87 -0
  50. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/__init__.py +3 -0
  51. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/config.py +82 -0
  52. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/hub.py +141 -0
  53. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/inference/plate_recognizer.py +323 -0
  54. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/py.typed +0 -0
  55. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/__init__.py +0 -0
  56. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/__init__.py +0 -0
  57. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/augmentation.py +101 -0
  58. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/data/dataset.py +97 -0
  59. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/__init__.py +0 -0
  60. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/config.py +114 -0
  61. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/layers.py +553 -0
  62. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/loss.py +55 -0
  63. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/metric.py +86 -0
  64. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_builders.py +95 -0
  65. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/model/model_schema.py +395 -0
  66. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/__init__.py +0 -0
  67. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/backend_utils.py +38 -0
  68. matrice_analytics/post_processing/ocr/fast_plate_ocr_py38/train/utilities/utils.py +214 -0
  69. matrice_analytics/post_processing/ocr/postprocessing.py +270 -0
  70. matrice_analytics/post_processing/ocr/preprocessing.py +52 -0
  71. matrice_analytics/post_processing/post_processor.py +1175 -0
  72. matrice_analytics/post_processing/test_cases/__init__.py +1 -0
  73. matrice_analytics/post_processing/test_cases/run_tests.py +143 -0
  74. matrice_analytics/post_processing/test_cases/test_advanced_customer_service.py +841 -0
  75. matrice_analytics/post_processing/test_cases/test_basic_counting_tracking.py +523 -0
  76. matrice_analytics/post_processing/test_cases/test_comprehensive.py +531 -0
  77. matrice_analytics/post_processing/test_cases/test_config.py +852 -0
  78. matrice_analytics/post_processing/test_cases/test_customer_service.py +585 -0
  79. matrice_analytics/post_processing/test_cases/test_data_generators.py +583 -0
  80. matrice_analytics/post_processing/test_cases/test_people_counting.py +510 -0
  81. matrice_analytics/post_processing/test_cases/test_processor.py +524 -0
  82. matrice_analytics/post_processing/test_cases/test_usecases.py +165 -0
  83. matrice_analytics/post_processing/test_cases/test_utilities.py +356 -0
  84. matrice_analytics/post_processing/test_cases/test_utils.py +743 -0
  85. matrice_analytics/post_processing/usecases/Histopathological_Cancer_Detection_img.py +604 -0
  86. matrice_analytics/post_processing/usecases/__init__.py +267 -0
  87. matrice_analytics/post_processing/usecases/abandoned_object_detection.py +797 -0
  88. matrice_analytics/post_processing/usecases/advanced_customer_service.py +1601 -0
  89. matrice_analytics/post_processing/usecases/age_detection.py +842 -0
  90. matrice_analytics/post_processing/usecases/age_gender_detection.py +1085 -0
  91. matrice_analytics/post_processing/usecases/anti_spoofing_detection.py +656 -0
  92. matrice_analytics/post_processing/usecases/assembly_line_detection.py +841 -0
  93. matrice_analytics/post_processing/usecases/banana_defect_detection.py +624 -0
  94. matrice_analytics/post_processing/usecases/basic_counting_tracking.py +667 -0
  95. matrice_analytics/post_processing/usecases/blood_cancer_detection_img.py +881 -0
  96. matrice_analytics/post_processing/usecases/car_damage_detection.py +834 -0
  97. matrice_analytics/post_processing/usecases/car_part_segmentation.py +946 -0
  98. matrice_analytics/post_processing/usecases/car_service.py +1601 -0
  99. matrice_analytics/post_processing/usecases/cardiomegaly_classification.py +864 -0
  100. matrice_analytics/post_processing/usecases/cell_microscopy_segmentation.py +897 -0
  101. matrice_analytics/post_processing/usecases/chicken_pose_detection.py +648 -0
  102. matrice_analytics/post_processing/usecases/child_monitoring.py +814 -0
  103. matrice_analytics/post_processing/usecases/color/clip.py +660 -0
  104. matrice_analytics/post_processing/usecases/color/clip_processor/merges.txt +48895 -0
  105. matrice_analytics/post_processing/usecases/color/clip_processor/preprocessor_config.json +28 -0
  106. matrice_analytics/post_processing/usecases/color/clip_processor/special_tokens_map.json +30 -0
  107. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer.json +245079 -0
  108. matrice_analytics/post_processing/usecases/color/clip_processor/tokenizer_config.json +32 -0
  109. matrice_analytics/post_processing/usecases/color/clip_processor/vocab.json +1 -0
  110. matrice_analytics/post_processing/usecases/color/color_map_utils.py +70 -0
  111. matrice_analytics/post_processing/usecases/color/color_mapper.py +468 -0
  112. matrice_analytics/post_processing/usecases/color_detection.py +1936 -0
  113. matrice_analytics/post_processing/usecases/color_map_utils.py +70 -0
  114. matrice_analytics/post_processing/usecases/concrete_crack_detection.py +827 -0
  115. matrice_analytics/post_processing/usecases/crop_weed_detection.py +781 -0
  116. matrice_analytics/post_processing/usecases/customer_service.py +1008 -0
  117. matrice_analytics/post_processing/usecases/defect_detection_products.py +936 -0
  118. matrice_analytics/post_processing/usecases/distracted_driver_detection.py +822 -0
  119. matrice_analytics/post_processing/usecases/drone_traffic_monitoring.py +585 -0
  120. matrice_analytics/post_processing/usecases/drowsy_driver_detection.py +829 -0
  121. matrice_analytics/post_processing/usecases/dwell_detection.py +829 -0
  122. matrice_analytics/post_processing/usecases/emergency_vehicle_detection.py +827 -0
  123. matrice_analytics/post_processing/usecases/face_emotion.py +813 -0
  124. matrice_analytics/post_processing/usecases/face_recognition.py +827 -0
  125. matrice_analytics/post_processing/usecases/fashion_detection.py +835 -0
  126. matrice_analytics/post_processing/usecases/field_mapping.py +902 -0
  127. matrice_analytics/post_processing/usecases/fire_detection.py +1146 -0
  128. matrice_analytics/post_processing/usecases/flare_analysis.py +836 -0
  129. matrice_analytics/post_processing/usecases/flower_segmentation.py +1006 -0
  130. matrice_analytics/post_processing/usecases/gas_leak_detection.py +837 -0
  131. matrice_analytics/post_processing/usecases/gender_detection.py +832 -0
  132. matrice_analytics/post_processing/usecases/human_activity_recognition.py +871 -0
  133. matrice_analytics/post_processing/usecases/intrusion_detection.py +1672 -0
  134. matrice_analytics/post_processing/usecases/leaf.py +821 -0
  135. matrice_analytics/post_processing/usecases/leaf_disease.py +840 -0
  136. matrice_analytics/post_processing/usecases/leak_detection.py +837 -0
  137. matrice_analytics/post_processing/usecases/license_plate_detection.py +1188 -0
  138. matrice_analytics/post_processing/usecases/license_plate_monitoring.py +1781 -0
  139. matrice_analytics/post_processing/usecases/litter_monitoring.py +717 -0
  140. matrice_analytics/post_processing/usecases/mask_detection.py +869 -0
  141. matrice_analytics/post_processing/usecases/natural_disaster.py +907 -0
  142. matrice_analytics/post_processing/usecases/parking.py +787 -0
  143. matrice_analytics/post_processing/usecases/parking_space_detection.py +822 -0
  144. matrice_analytics/post_processing/usecases/pcb_defect_detection.py +888 -0
  145. matrice_analytics/post_processing/usecases/pedestrian_detection.py +808 -0
  146. matrice_analytics/post_processing/usecases/people_counting.py +706 -0
  147. matrice_analytics/post_processing/usecases/people_counting_bckp.py +1683 -0
  148. matrice_analytics/post_processing/usecases/people_tracking.py +1842 -0
  149. matrice_analytics/post_processing/usecases/pipeline_detection.py +605 -0
  150. matrice_analytics/post_processing/usecases/plaque_segmentation_img.py +874 -0
  151. matrice_analytics/post_processing/usecases/pothole_segmentation.py +915 -0
  152. matrice_analytics/post_processing/usecases/ppe_compliance.py +645 -0
  153. matrice_analytics/post_processing/usecases/price_tag_detection.py +822 -0
  154. matrice_analytics/post_processing/usecases/proximity_detection.py +1901 -0
  155. matrice_analytics/post_processing/usecases/road_lane_detection.py +623 -0
  156. matrice_analytics/post_processing/usecases/road_traffic_density.py +832 -0
  157. matrice_analytics/post_processing/usecases/road_view_segmentation.py +915 -0
  158. matrice_analytics/post_processing/usecases/shelf_inventory_detection.py +583 -0
  159. matrice_analytics/post_processing/usecases/shoplifting_detection.py +822 -0
  160. matrice_analytics/post_processing/usecases/shopping_cart_analysis.py +899 -0
  161. matrice_analytics/post_processing/usecases/skin_cancer_classification_img.py +864 -0
  162. matrice_analytics/post_processing/usecases/smoker_detection.py +833 -0
  163. matrice_analytics/post_processing/usecases/solar_panel.py +810 -0
  164. matrice_analytics/post_processing/usecases/suspicious_activity_detection.py +1030 -0
  165. matrice_analytics/post_processing/usecases/template_usecase.py +380 -0
  166. matrice_analytics/post_processing/usecases/theft_detection.py +648 -0
  167. matrice_analytics/post_processing/usecases/traffic_sign_monitoring.py +724 -0
  168. matrice_analytics/post_processing/usecases/underground_pipeline_defect_detection.py +775 -0
  169. matrice_analytics/post_processing/usecases/underwater_pollution_detection.py +842 -0
  170. matrice_analytics/post_processing/usecases/vehicle_monitoring.py +1029 -0
  171. matrice_analytics/post_processing/usecases/warehouse_object_segmentation.py +899 -0
  172. matrice_analytics/post_processing/usecases/waterbody_segmentation.py +923 -0
  173. matrice_analytics/post_processing/usecases/weapon_detection.py +771 -0
  174. matrice_analytics/post_processing/usecases/weld_defect_detection.py +615 -0
  175. matrice_analytics/post_processing/usecases/wildlife_monitoring.py +898 -0
  176. matrice_analytics/post_processing/usecases/windmill_maintenance.py +834 -0
  177. matrice_analytics/post_processing/usecases/wound_segmentation.py +856 -0
  178. matrice_analytics/post_processing/utils/__init__.py +150 -0
  179. matrice_analytics/post_processing/utils/advanced_counting_utils.py +400 -0
  180. matrice_analytics/post_processing/utils/advanced_helper_utils.py +317 -0
  181. matrice_analytics/post_processing/utils/advanced_tracking_utils.py +461 -0
  182. matrice_analytics/post_processing/utils/alerting_utils.py +213 -0
  183. matrice_analytics/post_processing/utils/category_mapping_utils.py +94 -0
  184. matrice_analytics/post_processing/utils/color_utils.py +592 -0
  185. matrice_analytics/post_processing/utils/counting_utils.py +182 -0
  186. matrice_analytics/post_processing/utils/filter_utils.py +261 -0
  187. matrice_analytics/post_processing/utils/format_utils.py +293 -0
  188. matrice_analytics/post_processing/utils/geometry_utils.py +300 -0
  189. matrice_analytics/post_processing/utils/smoothing_utils.py +358 -0
  190. matrice_analytics/post_processing/utils/tracking_utils.py +234 -0
  191. matrice_analytics/py.typed +0 -0
  192. matrice_analytics-0.1.60.dist-info/METADATA +481 -0
  193. matrice_analytics-0.1.60.dist-info/RECORD +196 -0
  194. matrice_analytics-0.1.60.dist-info/WHEEL +5 -0
  195. matrice_analytics-0.1.60.dist-info/licenses/LICENSE.txt +21 -0
  196. matrice_analytics-0.1.60.dist-info/top_level.txt +1 -0
@@ -0,0 +1,585 @@
1
+ """
2
+ Comprehensive tests for the Customer Service use case.
3
+
4
+ This module tests detailed functionality for customer service analytics including:
5
+ - Queue management
6
+ - Service time analysis
7
+ - Staff efficiency metrics
8
+ - Customer flow analysis
9
+ - Alert generation
10
+ """
11
+
12
+ import unittest
13
+ import json
14
+ import time
15
+ from unittest.mock import patch, MagicMock
16
+
17
+ from .test_utilities import BasePostProcessingTest
18
+ from .test_data_generators import (
19
+ create_detection_results, create_tracking_results,
20
+ create_customer_service_config, create_zone_polygons,
21
+ create_performance_test_data, create_edge_case_data
22
+ )
23
+
24
+ from src.matrice_analytics.post_processing import (
25
+ PostProcessor, ProcessingStatus, CustomerServiceConfig,
26
+ TrackingConfig, AlertConfig
27
+ )
28
+
29
+
30
+ class TestCustomerServiceUseCase(BasePostProcessingTest):
31
+ """Test cases for customer service use case."""
32
+
33
+ def test_basic_customer_service_processing(self):
34
+ """Test basic customer service processing functionality."""
35
+ # Create test data with relevant categories
36
+ test_data = create_detection_results(
37
+ num_detections=10,
38
+ categories=["person", "staff", "customer"],
39
+ confidence_range=(0.6, 0.9)
40
+ )
41
+
42
+ # Create configuration
43
+ config = create_customer_service_config(
44
+ confidence_threshold=0.5,
45
+ service_proximity_threshold=50.0
46
+ )
47
+
48
+ # Process the data
49
+ result = self.processor.process(test_data, config)
50
+
51
+ # Validate basic result structure
52
+ self.assert_processing_result_valid(result, expected_usecase="customer_service")
53
+ # Handle both SUCCESS and WARNING status as valid
54
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
55
+
56
+ # Check for expected metrics (using actual metric names from implementation)
57
+ expected_metrics = ["customer_to_staff_ratio", "service_coverage", "interaction_rate"]
58
+ for metric in expected_metrics:
59
+ self.assertIn(metric, result.metrics)
60
+
61
+ # Check that insights were generated
62
+ self.assert_insights_generated(result, min_insights=1)
63
+
64
+ def test_customer_service_with_areas(self):
65
+ """Test customer service with defined areas."""
66
+ # Create test data
67
+ test_data = create_detection_results(
68
+ num_detections=8,
69
+ categories=["staff", "customer", "person"]
70
+ )
71
+
72
+ # Create configuration with predefined areas
73
+ config = create_customer_service_config(
74
+ confidence_threshold=0.6,
75
+ service_proximity_threshold=100.0
76
+ )
77
+
78
+ result = self.processor.process(test_data, config)
79
+
80
+ # Validate result
81
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
82
+
83
+ # Check for area-related metrics (using actual metric names)
84
+ self.assertIn("area_utilization", result.metrics)
85
+ area_utilization = result.metrics["area_utilization"]
86
+ self.assertIsInstance(area_utilization, dict)
87
+
88
+ # Should have customer and service area analysis
89
+ if "customer_areas" in area_utilization:
90
+ self.assertIsInstance(area_utilization["customer_areas"], (int, float))
91
+ if "service_areas" in area_utilization:
92
+ self.assertIsInstance(area_utilization["service_areas"], (int, float))
93
+
94
+ def test_customer_service_with_tracking(self):
95
+ """Test customer service with tracking enabled."""
96
+ # Create tracking data with staff and customers
97
+ tracking_data = create_tracking_results(
98
+ num_tracks=10,
99
+ categories=["staff", "customer", "person"]
100
+ )
101
+
102
+ result = self.processor.process_simple(
103
+ tracking_data,
104
+ usecase="customer_service",
105
+ confidence_threshold=0.6,
106
+ enable_tracking=True,
107
+ staff_categories=["staff"],
108
+ customer_categories=["customer", "person"],
109
+ max_service_time=1200.0
110
+ )
111
+
112
+ # Validate result
113
+ self.assert_processing_result_valid(result)
114
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
115
+
116
+ # Should have customer service metrics
117
+ self.assertIn("customer_to_staff_ratio", result.metrics)
118
+ self.assertIn("service_coverage", result.metrics)
119
+
120
+ def test_customer_service_proximity_analysis(self):
121
+ """Test proximity-based service analysis."""
122
+ # Create test data with specific positions for proximity analysis
123
+ test_data = create_detection_results(
124
+ num_detections=8,
125
+ categories=["staff", "customer"]
126
+ )
127
+
128
+ # Manually adjust positions for proximity testing
129
+ for i, detection in enumerate(test_data):
130
+ if i < 4: # First half as staff
131
+ detection["category"] = "staff"
132
+ # Position staff in service area
133
+ detection["bbox"] = [500 + i * 20, 100, 550 + i * 20, 200]
134
+ else: # Second half as customers
135
+ detection["category"] = "customer"
136
+ # Position customers near staff (within proximity)
137
+ detection["bbox"] = [480 + (i-4) * 30, 150, 520 + (i-4) * 30, 230]
138
+
139
+ result = self.processor.process_simple(
140
+ test_data,
141
+ usecase="customer_service",
142
+ confidence_threshold=0.5,
143
+ service_proximity_threshold=100.0,
144
+ staff_categories=["staff"],
145
+ customer_categories=["customer"]
146
+ )
147
+
148
+ # Should analyze proximity patterns
149
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
150
+
151
+ # Should have service-related metrics
152
+ self.assertIn("customer_to_staff_ratio", result.metrics)
153
+ self.assertIn("service_coverage", result.metrics)
154
+
155
+ def test_customer_service_staff_utilization(self):
156
+ """Test staff utilization analysis."""
157
+ # Create test data with staff and customers
158
+ test_data = create_detection_results(
159
+ num_detections=15,
160
+ categories=["staff"] * 5 + ["customer"] * 10
161
+ )
162
+
163
+ config = create_customer_service_config(
164
+ confidence_threshold=0.6,
165
+ service_proximity_threshold=100.0
166
+ )
167
+
168
+ result = self.processor.process(test_data, config)
169
+
170
+ # Validate result (handle WARNING status)
171
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
172
+
173
+ # Check for staff utilization metrics
174
+ self.assertIn("staff_utilization", result.metrics)
175
+ self.assertIn("customer_to_staff_ratio", result.metrics)
176
+
177
+ # Check utilization values are reasonable
178
+ staff_utilization = result.metrics["staff_utilization"]
179
+ self.assertIsInstance(staff_utilization, (int, float))
180
+ self.assertGreaterEqual(staff_utilization, 0.0)
181
+
182
+ def test_customer_service_queue_analysis(self):
183
+ """Test queue management analysis."""
184
+ # Create test data representing a queue scenario
185
+ test_data = []
186
+
187
+ # Add service counter staff
188
+ test_data.append({
189
+ "bbox": [500, 100, 550, 200],
190
+ "confidence": 0.9,
191
+ "category": "staff",
192
+ "category_id": 0
193
+ })
194
+
195
+ # Add customers in a queue formation
196
+ queue_positions = [
197
+ [450, 150], [400, 150], [350, 150], [300, 150], [250, 150]
198
+ ]
199
+
200
+ for i, (x, y) in enumerate(queue_positions):
201
+ test_data.append({
202
+ "bbox": [x, y, x+40, y+80],
203
+ "confidence": 0.8,
204
+ "category": "customer",
205
+ "category_id": 1
206
+ })
207
+
208
+ result = self.processor.process_simple(
209
+ test_data,
210
+ usecase="customer_service",
211
+ confidence_threshold=0.5,
212
+ service_proximity_threshold=150.0,
213
+ staff_categories=["staff"],
214
+ customer_categories=["customer"],
215
+ buffer_time=2.0
216
+ )
217
+
218
+ # Should analyze queue patterns
219
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
220
+
221
+ # Should have customer service metrics
222
+ self.assertIn("customer_to_staff_ratio", result.metrics)
223
+
224
+ def test_customer_service_service_time_analysis(self):
225
+ """Test service time analysis with tracking data."""
226
+ # Create tracking data showing service interactions over time
227
+ tracking_data = []
228
+
229
+ # Staff track (stationary)
230
+ staff_track = {
231
+ "track_id": 1,
232
+ "category": "staff",
233
+ "points": [],
234
+ "start_frame": 0,
235
+ "end_frame": 100,
236
+ "is_active": True,
237
+ "total_frames": 100
238
+ }
239
+
240
+ # Staff stays in one place
241
+ for frame in range(100):
242
+ staff_track["points"].append({
243
+ "frame_id": frame,
244
+ "track_id": 1,
245
+ "bbox": [500, 100, 550, 200],
246
+ "confidence": 0.9,
247
+ "category": "staff",
248
+ "timestamp": time.time() + frame
249
+ })
250
+
251
+ tracking_data.append(staff_track)
252
+
253
+ # Customer track (approaches staff, gets service, leaves)
254
+ customer_track = {
255
+ "track_id": 2,
256
+ "category": "customer",
257
+ "points": [],
258
+ "start_frame": 10,
259
+ "end_frame": 60,
260
+ "is_active": False,
261
+ "total_frames": 50
262
+ }
263
+
264
+ # Customer movement: approach -> wait -> service -> leave
265
+ for frame in range(50):
266
+ x = 300 + frame * 4 # Moving towards staff
267
+ if frame > 30: # After service, moving away
268
+ x = 530 - (frame - 30) * 4
269
+
270
+ customer_track["points"].append({
271
+ "frame_id": frame + 10,
272
+ "track_id": 2,
273
+ "bbox": [x, 150, x+40, 230],
274
+ "confidence": 0.8,
275
+ "category": "customer",
276
+ "timestamp": time.time() + frame + 10
277
+ })
278
+
279
+ tracking_data.append(customer_track)
280
+
281
+ result = self.processor.process_simple(
282
+ tracking_data,
283
+ usecase="customer_service",
284
+ confidence_threshold=0.5,
285
+ enable_tracking=True,
286
+ service_proximity_threshold=100.0,
287
+ max_service_time=1800.0,
288
+ staff_categories=["staff"],
289
+ customer_categories=["customer"]
290
+ )
291
+
292
+ # Should analyze service times
293
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
294
+
295
+ # Should have service metrics
296
+ self.assertIn("customer_to_staff_ratio", result.metrics)
297
+
298
+ def test_customer_service_config_validation(self):
299
+ """Test customer service configuration validation."""
300
+ # Valid configuration
301
+ valid_config = CustomerServiceConfig(
302
+ category="sales",
303
+ usecase="customer_service",
304
+ confidence_threshold=0.6,
305
+ service_proximity_threshold=100.0,
306
+ staff_categories=["staff", "employee"],
307
+ customer_categories=["customer", "person"],
308
+ customer_areas={"waiting": [[0, 0], [100, 0], [100, 100], [0, 100]]},
309
+ staff_areas={"counter": [[200, 0], [300, 0], [300, 100], [200, 100]]},
310
+ service_areas={"desk": [[150, 150], [250, 150], [250, 250], [150, 250]]}
311
+ )
312
+
313
+ errors = valid_config.validate()
314
+ self.assertEqual(len(errors), 0)
315
+
316
+ # Invalid configurations
317
+ invalid_configs = [
318
+ # Invalid service proximity threshold
319
+ CustomerServiceConfig(
320
+ category="sales",
321
+ usecase="customer_service",
322
+ service_proximity_threshold=-50.0
323
+ ),
324
+ # Invalid max service time
325
+ CustomerServiceConfig(
326
+ category="sales",
327
+ usecase="customer_service",
328
+ max_service_time=-100.0
329
+ ),
330
+ # Empty staff categories
331
+ CustomerServiceConfig(
332
+ category="sales",
333
+ usecase="customer_service",
334
+ staff_categories=[]
335
+ ),
336
+ # Empty customer categories
337
+ CustomerServiceConfig(
338
+ category="sales",
339
+ usecase="customer_service",
340
+ customer_categories=[]
341
+ ),
342
+ # Invalid area polygon
343
+ CustomerServiceConfig(
344
+ category="sales",
345
+ usecase="customer_service",
346
+ customer_areas={"invalid": [[0, 0], [100, 100]]} # Only 2 points
347
+ )
348
+ ]
349
+
350
+ for config in invalid_configs:
351
+ errors = config.validate()
352
+ self.assertGreater(len(errors), 0)
353
+
354
+ def test_customer_service_tracking_config_validation(self):
355
+ """Test tracking configuration validation."""
356
+ # Valid tracking configuration
357
+ valid_tracking_config = TrackingConfig(
358
+ tracking_method="kalman",
359
+ max_age=30,
360
+ min_hits=3,
361
+ iou_threshold=0.3,
362
+ target_classes=["staff", "customer"]
363
+ )
364
+
365
+ errors = valid_tracking_config.validate()
366
+ self.assertEqual(len(errors), 0)
367
+
368
+ # Invalid tracking configurations
369
+ invalid_tracking_configs = [
370
+ # Invalid tracking method
371
+ TrackingConfig(tracking_method="invalid_method"),
372
+ # Invalid max_age
373
+ TrackingConfig(max_age=-5),
374
+ # Invalid min_hits
375
+ TrackingConfig(min_hits=0),
376
+ # Invalid IoU threshold
377
+ TrackingConfig(iou_threshold=1.5)
378
+ ]
379
+
380
+ for config in invalid_tracking_configs:
381
+ errors = config.validate()
382
+ self.assertGreater(len(errors), 0)
383
+
384
+ def test_customer_service_alerts(self):
385
+ """Test customer service alert generation."""
386
+ # Create test data that should trigger alerts
387
+ test_data = create_detection_results(
388
+ num_detections=20,
389
+ categories=["person", "customer"], # High customer count, low staff
390
+ confidence_range=(0.7, 0.9)
391
+ )
392
+
393
+ # Create configuration with alert settings
394
+ config = create_customer_service_config(
395
+ confidence_threshold=0.6,
396
+ service_proximity_threshold=30.0
397
+ )
398
+
399
+ # Process the data
400
+ result = self.processor.process(test_data, config)
401
+
402
+ # Validate result
403
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
404
+
405
+ # Check for optimization opportunities instead of alerts
406
+ self.assertIn("optimization_opportunities", result.metrics)
407
+ opportunities = result.metrics["optimization_opportunities"]
408
+ self.assertIsInstance(opportunities, list)
409
+
410
+ # Should have suggestions for high customer to staff ratio
411
+ self.assertGreater(len(opportunities), 0)
412
+
413
+ def test_customer_service_empty_data(self):
414
+ """Test customer service with empty input data."""
415
+ result = self.processor.process_simple(
416
+ [],
417
+ usecase="customer_service",
418
+ confidence_threshold=0.6
419
+ )
420
+
421
+ # Should handle empty data gracefully (may return WARNING for empty data)
422
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
423
+
424
+ # Check for appropriate summary message
425
+ self.assertTrue(
426
+ "no" in result.summary.lower() or
427
+ "empty" in result.summary.lower() or
428
+ "zero" in result.summary.lower()
429
+ )
430
+
431
+ # Should have basic metrics structure
432
+ self.assertIsInstance(result.metrics, dict)
433
+
434
+ def test_customer_service_performance(self):
435
+ """Test customer service performance with large datasets."""
436
+ # Create large dataset with mixed categories
437
+ large_data = create_detection_results(
438
+ num_detections=500,
439
+ categories=["staff", "customer", "person", "employee"]
440
+ )
441
+
442
+ start_time = time.time()
443
+ result = self.processor.process_simple(
444
+ large_data,
445
+ usecase="customer_service",
446
+ confidence_threshold=0.6,
447
+ service_proximity_threshold=100.0
448
+ )
449
+ processing_time = time.time() - start_time
450
+
451
+ # Should complete successfully (may return WARNING for large datasets)
452
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
453
+
454
+ # Should complete within reasonable time (3 seconds for complex analysis)
455
+ self.assertLess(processing_time, 3.0)
456
+
457
+ # Should have reasonable performance metrics (processing_time may be 0 for fast processing)
458
+ self.assertGreaterEqual(result.processing_time, 0)
459
+
460
+ def test_customer_service_insights_generation(self):
461
+ """Test that meaningful insights are generated."""
462
+ # Create test data with staff-customer interactions
463
+ test_data = create_detection_results(
464
+ num_detections=25,
465
+ categories=["staff"] * 5 + ["customer"] * 20
466
+ )
467
+
468
+ config = create_customer_service_config(
469
+ confidence_threshold=0.6,
470
+ service_proximity_threshold=100.0
471
+ )
472
+
473
+ result = self.processor.process(test_data, config)
474
+
475
+ # Should generate multiple insights
476
+ self.assert_insights_generated(result, min_insights=2)
477
+
478
+ # Check insight quality
479
+ insights = result.insights
480
+
481
+ # Should mention staff and customers
482
+ staff_mentioned = any("staff" in insight.lower() for insight in insights)
483
+ customer_mentioned = any("customer" in insight.lower() for insight in insights)
484
+
485
+ self.assertTrue(staff_mentioned or customer_mentioned)
486
+
487
+ # Should mention service aspects
488
+ service_mentioned = any("service" in insight.lower() for insight in insights)
489
+ self.assertTrue(service_mentioned)
490
+
491
+ def test_customer_service_metrics_completeness(self):
492
+ """Test that all expected metrics are generated."""
493
+ # Create test data
494
+ test_data = create_detection_results(
495
+ num_detections=15,
496
+ categories=["staff", "customer", "person"]
497
+ )
498
+
499
+ # Create comprehensive configuration
500
+ config = create_customer_service_config(
501
+ confidence_threshold=0.6,
502
+ enable_tracking=True,
503
+ service_proximity_threshold=100.0
504
+ )
505
+
506
+ result = self.processor.process(test_data, config)
507
+
508
+ # Handle WARNING status as valid for this test
509
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
510
+
511
+ # Check for core metrics (using actual metric names from implementation)
512
+ expected_core_metrics = [
513
+ "customer_to_staff_ratio",
514
+ "service_coverage",
515
+ "interaction_rate",
516
+ "staff_utilization",
517
+ "area_utilization",
518
+ "service_quality_score",
519
+ "attention_score",
520
+ "overall_performance"
521
+ ]
522
+
523
+ for metric in expected_core_metrics:
524
+ self.assertIn(metric, result.metrics)
525
+
526
+ # Check for optimization opportunities
527
+ self.assertIn("optimization_opportunities", result.metrics)
528
+ self.assertIsInstance(result.metrics["optimization_opportunities"], list)
529
+
530
+
531
+ class TestCustomerServiceIntegration(BasePostProcessingTest):
532
+ """Integration tests for customer service with other components."""
533
+
534
+ def test_customer_service_config_serialization(self):
535
+ """Test configuration serialization and deserialization."""
536
+ # Create configuration
537
+ original_config = create_customer_service_config(
538
+ confidence_threshold=0.7,
539
+ service_proximity_threshold=120.0
540
+ )
541
+
542
+ # Save and load configuration
543
+ config_file = self.create_temp_config_file(original_config)
544
+ loaded_config = self.processor.load_config(config_file)
545
+
546
+ # Compare key attributes
547
+ self.assertEqual(original_config.usecase, loaded_config.usecase)
548
+ self.assertEqual(original_config.category, loaded_config.category)
549
+ self.assertEqual(original_config.confidence_threshold, loaded_config.confidence_threshold)
550
+ self.assertEqual(original_config.service_proximity_threshold, loaded_config.service_proximity_threshold)
551
+ self.assertEqual(original_config.staff_categories, loaded_config.staff_categories)
552
+ self.assertEqual(original_config.customer_categories, loaded_config.customer_categories)
553
+
554
+ def test_customer_service_error_recovery(self):
555
+ """Test error recovery in customer service."""
556
+ # Create partially invalid data
557
+ mixed_quality_data = [
558
+ # Valid staff detection
559
+ {"bbox": [10, 10, 50, 100], "confidence": 0.9, "category": "staff"},
560
+ # Valid customer detection
561
+ {"bbox": [100, 100, 140, 200], "confidence": 0.8, "category": "customer"},
562
+ # Invalid detection
563
+ {"bbox": [200, 200, 190, 190], "confidence": 0.9, "category": "person"}, # Invalid bbox
564
+ # Another valid detection
565
+ {"bbox": [300, 300, 340, 400], "confidence": 0.7, "category": "customer"}
566
+ ]
567
+
568
+ result = self.processor.process_simple(
569
+ mixed_quality_data,
570
+ usecase="customer_service",
571
+ confidence_threshold=0.5,
572
+ staff_categories=["staff"],
573
+ customer_categories=["customer", "person"]
574
+ )
575
+
576
+ # Should handle partial failures gracefully
577
+ self.assertIn(result.status, [ProcessingStatus.SUCCESS, ProcessingStatus.WARNING])
578
+
579
+ # Should still provide some results
580
+ self.assertIsNotNone(result.data)
581
+ self.assertIsInstance(result.metrics, dict)
582
+
583
+
584
+ if __name__ == "__main__":
585
+ unittest.main(verbosity=2)