ivideon: Add some more schemed for crowd service
This commit is contained in:
parent
be420adc49
commit
d5ecc2d577
6 changed files with 589 additions and 218 deletions
|
|
@ -0,0 +1,90 @@
|
|||
@startuml Crowd Analyzer Flow
|
||||
|
||||
title Поток анализа очередей в Crowd Analyzer
|
||||
|
||||
actor "Redis Queue" as redis
|
||||
participant "analyze_crowd()" as entry
|
||||
participant "_analyze_crowd()" as main
|
||||
participant "frames.pull()" as frames_mod
|
||||
participant "_run_detectors()" as detectors
|
||||
participant "TevianHeadsDetector" as tevian
|
||||
participant "_build_zones_info()" as zones_builder
|
||||
participant "_get_triggered_zones()" as trigger_checker
|
||||
participant "Storage" as storage
|
||||
participant "central.send()" as central
|
||||
participant "zones_db" as db
|
||||
|
||||
redis -> entry: task(uin, camera_id, zones)
|
||||
activate entry
|
||||
|
||||
entry -> entry: acquire TASKS_LICENCES
|
||||
entry -> main: _analyze_crowd(uin, camera, zones, server_id)
|
||||
activate main
|
||||
|
||||
main -> main: _filter_frequent_tasks()
|
||||
alt tasks too frequent
|
||||
main -> entry: return (skip)
|
||||
else proceed
|
||||
main -> frames_mod: pull(uin, camera)
|
||||
activate frames_mod
|
||||
frames_mod -> main: Frame object
|
||||
deactivate frames_mod
|
||||
|
||||
opt rotation_angle provided
|
||||
main -> main: frame.rotate(rotation_angle)
|
||||
end
|
||||
|
||||
main -> detectors: _run_detectors(uin, camera, frame, zones)
|
||||
activate detectors
|
||||
|
||||
detectors -> detectors: check tevian enabled & used
|
||||
detectors -> tevian: request(uin, camera, frame, zones)
|
||||
activate tevian
|
||||
tevian -> detectors: detection results
|
||||
deactivate tevian
|
||||
|
||||
detectors -> main: detected_values, timings, errors
|
||||
deactivate detectors
|
||||
|
||||
main -> zones_builder: _build_zones_info(zones, detected_values)
|
||||
activate zones_builder
|
||||
zones_builder -> main: zones_info (with AI results)
|
||||
deactivate zones_builder
|
||||
|
||||
main -> trigger_checker: _get_triggered_zones(zones_info, timestamp)
|
||||
activate trigger_checker
|
||||
|
||||
loop for each zone
|
||||
trigger_checker -> trigger_checker: check trigger conditions
|
||||
trigger_checker -> trigger_checker: check schedule
|
||||
trigger_checker -> trigger_checker: check grace period
|
||||
end
|
||||
|
||||
trigger_checker -> main: triggered_zones
|
||||
deactivate trigger_checker
|
||||
|
||||
main -> main: draw zones on frame
|
||||
main -> storage: upload_fileobj(image_with_zones)
|
||||
activate storage
|
||||
storage -> main: zones_url
|
||||
deactivate storage
|
||||
|
||||
main -> central: send('new_measurement', data)
|
||||
activate central
|
||||
central -> main: measurement sent
|
||||
deactivate central
|
||||
|
||||
main -> db: update({'_id': f'{uin}/{camera}'}, detected_at)
|
||||
activate db
|
||||
db -> main: updated
|
||||
deactivate db
|
||||
end
|
||||
|
||||
main -> entry: analysis complete
|
||||
deactivate main
|
||||
|
||||
entry -> entry: release TASKS_LICENCES
|
||||
entry -> redis: task finished
|
||||
deactivate entry
|
||||
|
||||
@enduml
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
@startuml Crowd Analyzer Architecture
|
||||
|
||||
title Архитектура Crowd Analyzer
|
||||
|
||||
package "External Systems" {
|
||||
[Redis Queue] as redis
|
||||
[Storage Service] as storage
|
||||
[Tevian API] as tevian_api
|
||||
[Camera Server] as camera_server
|
||||
}
|
||||
|
||||
package "Crowd Analyzer" {
|
||||
[analyze_crowd()] as entry_point
|
||||
[_analyze_crowd()] as main_logic
|
||||
[_run_detectors()] as detector_runner
|
||||
[_get_triggered_zones()] as trigger_logic
|
||||
[_build_zones_info()] as zones_builder
|
||||
}
|
||||
|
||||
package "Detectors" {
|
||||
[TevianHeadsDetector] as tevian_detector
|
||||
}
|
||||
|
||||
package "Data Access" {
|
||||
[frames.pull()] as frame_puller
|
||||
[central.send()] as central_sender
|
||||
[services_db] as services_db
|
||||
[zones_db] as zones_db
|
||||
}
|
||||
|
||||
package "Storage" {
|
||||
[MongoDB Local] as mongo_local
|
||||
[MongoDB Crowd] as mongo_crowd
|
||||
}
|
||||
|
||||
' External connections
|
||||
redis --> entry_point : tasks
|
||||
camera_server <-- frame_puller : get frames
|
||||
tevian_api <-- tevian_detector : AI requests
|
||||
storage <-- main_logic : upload images
|
||||
|
||||
' Internal flow
|
||||
entry_point --> main_logic
|
||||
main_logic --> detector_runner
|
||||
main_logic --> zones_builder
|
||||
main_logic --> trigger_logic
|
||||
detector_runner --> tevian_detector
|
||||
|
||||
' Data access
|
||||
frame_puller --> camera_server
|
||||
central_sender --> redis : results
|
||||
services_db --> mongo_local
|
||||
zones_db --> mongo_local
|
||||
|
||||
' Key relationships
|
||||
main_logic --> frame_puller : get frames
|
||||
main_logic --> central_sender : send results
|
||||
main_logic --> zones_db : update status
|
||||
detector_runner --> services_db : check config
|
||||
tevian_detector --> tevian_api : detect heads
|
||||
|
||||
note right of entry_point
|
||||
Entry point:
|
||||
- Semaphore control
|
||||
- Error handling
|
||||
- Metrics tracking
|
||||
end note
|
||||
|
||||
note right of main_logic
|
||||
Main logic:
|
||||
- Frame processing
|
||||
- Zone analysis
|
||||
- Result storage
|
||||
- Notification sending
|
||||
end note
|
||||
|
||||
note right of detector_runner
|
||||
Detector runner:
|
||||
- AI service calls
|
||||
- Error handling
|
||||
- Performance tracking
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
||||
Диаграмма состояний зоны:
|
||||
|
||||
@startuml Zone State Diagram
|
||||
|
||||
title Состояния зоны в процессе анализа
|
||||
|
||||
[*] --> Inactive : zone created
|
||||
|
||||
state Inactive {
|
||||
Inactive : length_by_ai = 0
|
||||
Inactive : no triggers
|
||||
}
|
||||
|
||||
state Active {
|
||||
Active : length_by_ai > 0
|
||||
Active : analyzing people count
|
||||
}
|
||||
|
||||
state Triggered {
|
||||
Triggered : trigger condition met
|
||||
Triggered : schedule active
|
||||
Triggered : not in grace period
|
||||
}
|
||||
|
||||
state GracePeriod {
|
||||
GracePeriod : trigger condition met
|
||||
GracePeriod : but in grace period
|
||||
GracePeriod : suppressing notifications
|
||||
}
|
||||
|
||||
Inactive --> Active : people detected
|
||||
Active --> Inactive : no people detected
|
||||
Active --> Triggered : trigger_at threshold reached\nAND schedule active\nAND not in grace period
|
||||
Active --> GracePeriod : trigger_at threshold reached\nBUT in grace period
|
||||
Triggered --> Active : trigger condition not met
|
||||
GracePeriod --> Triggered : grace period expired\nAND trigger still met
|
||||
GracePeriod --> Active : trigger condition not met
|
||||
|
||||
note right of Triggered
|
||||
Actions:
|
||||
- Zone highlighted on image
|
||||
- Notification sent
|
||||
- Grace period started
|
||||
end note
|
||||
|
||||
note right of GracePeriod
|
||||
Grace period prevents
|
||||
spam notifications for
|
||||
zones that constantly
|
||||
trigger
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
@startuml CrowdReport Class Relationships
|
||||
|
||||
!define ENTITY class
|
||||
!define INTERFACE interface
|
||||
|
||||
package "API Concept Layer" {
|
||||
INTERFACE CrowdReportInterface {
|
||||
+id: str
|
||||
+owner_id: str
|
||||
+type: str
|
||||
+name: str
|
||||
+status: str
|
||||
+created_at: timestamp
|
||||
+updated_at: timestamp
|
||||
+progress: int
|
||||
+options: dict
|
||||
+create(user, type, options, name)
|
||||
}
|
||||
|
||||
ENTITY APIObject {
|
||||
+api_method()
|
||||
+error_codes()
|
||||
}
|
||||
|
||||
package "Errors" {
|
||||
ENTITY errors.BadRequest
|
||||
ENTITY errors.FeatureNotSupported
|
||||
ENTITY errors.BadParameter
|
||||
ENTITY errors.MalformedSchedule
|
||||
}
|
||||
}
|
||||
|
||||
package "Crowd Frontend Implementation" {
|
||||
ENTITY CrowdReport {
|
||||
+ID_REGEX: str
|
||||
+COLLECTION: Collection
|
||||
+MAX_REPORT_INTERVAL: int = 90
|
||||
+MAX_WORK_TIME_REPORT_INTERVAL: int = 31
|
||||
+MAX_REPORT_NAME_LEN: int = 60
|
||||
+create(user, type, options, name)
|
||||
+delete()
|
||||
}
|
||||
|
||||
ENTITY InitializerMixin {
|
||||
+_initialize(data)
|
||||
+COLLECTION
|
||||
+underscored_name()
|
||||
}
|
||||
|
||||
ENTITY ReportCustomizer {
|
||||
+RESTRICTION_COLLECTION
|
||||
+_type: str
|
||||
+_required_fields: set
|
||||
+_allowed_fields: set
|
||||
+_registry: dict
|
||||
+for_type(report_type)
|
||||
+validate(options)
|
||||
+setup(query, options)
|
||||
+__init_subclass__()
|
||||
}
|
||||
|
||||
ENTITY CrowdOverallStatsReportCustomizer {
|
||||
+_type = 'crowd_overall_stats_report'
|
||||
+_required_fields
|
||||
+_allowed_fields
|
||||
+setup(query, options)
|
||||
}
|
||||
|
||||
ENTITY CrowdQueuesStatsReportCustomizer {
|
||||
+_type = 'crowd_queue_stats_report'
|
||||
+_required_fields
|
||||
+_allowed_fields
|
||||
+setup(query, options)
|
||||
}
|
||||
|
||||
ENTITY CrowdWorkTimeReportCustomizer {
|
||||
+_type = 'crowd_work_time_report'
|
||||
+_required_fields
|
||||
+_allowed_fields
|
||||
+setup(query, options)
|
||||
}
|
||||
|
||||
ENTITY QueryEnricher {
|
||||
+query: dict
|
||||
+start_time: int
|
||||
+end_time: int
|
||||
+options: dict
|
||||
+enrich()
|
||||
-_populate_time_condition()
|
||||
-_populate_sources()
|
||||
-_populate_step()
|
||||
-_populate_max_queue_size()
|
||||
-_populate_duration_from()
|
||||
}
|
||||
}
|
||||
|
||||
package "Utilities & Helpers" {
|
||||
ENTITY api_helpers {
|
||||
+InitializerMixin
|
||||
+get_user_timezone(user)
|
||||
+schedule_parser(schedule)
|
||||
+get_default_schedule()
|
||||
+zone_settings_changed()
|
||||
}
|
||||
|
||||
ENTITY validators {
|
||||
+validate_list_of_strings()
|
||||
+validate_schedule()
|
||||
+validate_time_range()
|
||||
+validate_limit()
|
||||
+validate_schedule_values()
|
||||
}
|
||||
|
||||
ENTITY helpers {
|
||||
+get_user_zones(user_id, zone_ids)
|
||||
+_get_user_zones()
|
||||
}
|
||||
}
|
||||
|
||||
package "Database & External" {
|
||||
ENTITY db {
|
||||
+reports()
|
||||
+user_reports
|
||||
+insert_with_random_id()
|
||||
}
|
||||
|
||||
ENTITY time_tools {
|
||||
+DAY
|
||||
+to_seconds()
|
||||
}
|
||||
|
||||
ENTITY pendulum {
|
||||
+from_timestamp()
|
||||
}
|
||||
}
|
||||
|
||||
' Inheritance relationships
|
||||
CrowdReportInterface <|-- CrowdReport
|
||||
APIObject <|-- CrowdReportInterface
|
||||
InitializerMixin <|-- CrowdReport
|
||||
ReportCustomizer <|-- CrowdOverallStatsReportCustomizer
|
||||
ReportCustomizer <|-- CrowdQueuesStatsReportCustomizer
|
||||
ReportCustomizer <|-- CrowdWorkTimeReportCustomizer
|
||||
|
||||
' Composition/Usage relationships
|
||||
CrowdReport ..> QueryEnricher : creates
|
||||
CrowdReport ..> ReportCustomizer : uses
|
||||
CrowdReport ..> db : persists to
|
||||
CrowdReport ..> time_tools : uses
|
||||
CrowdReport ..> pendulum : uses
|
||||
CrowdReport ..> api_helpers : uses get_user_timezone
|
||||
CrowdReport ..> helpers : uses get_user_zones
|
||||
|
||||
QueryEnricher ..> validators : uses validate_list_of_strings
|
||||
QueryEnricher ..> helpers : uses get_user_zones
|
||||
|
||||
ReportCustomizer ..> validators : uses validate_schedule
|
||||
|
||||
CrowdReport --> errors.BadRequest : throws
|
||||
CrowdReport --> errors.FeatureNotSupported : throws
|
||||
ReportCustomizer --> errors.BadRequest : throws
|
||||
ReportCustomizer --> errors.FeatureNotSupported : throws
|
||||
ReportCustomizer --> errors.BadParameter : throws
|
||||
validators --> errors.BadRequest : throws
|
||||
validators --> errors.MalformedSchedule : throws
|
||||
|
||||
' Registry pattern
|
||||
ReportCustomizer : <<registry>>
|
||||
CrowdOverallStatsReportCustomizer ..> ReportCustomizer : auto-registers
|
||||
CrowdQueuesStatsReportCustomizer ..> ReportCustomizer : auto-registers
|
||||
CrowdWorkTimeReportCustomizer ..> ReportCustomizer : auto-registers
|
||||
|
||||
note right of ReportCustomizer
|
||||
Uses __init_subclass__ for
|
||||
automatic subclass registration
|
||||
in _registry dictionary
|
||||
end note
|
||||
|
||||
note right of CrowdReport
|
||||
Main report factory class.
|
||||
Validates inputs, creates queries,
|
||||
and persists report tasks to MongoDB.
|
||||
Maximum intervals: 90 days (regular),
|
||||
31 days (work time reports)
|
||||
end note
|
||||
|
||||
note right of QueryEnricher
|
||||
Builds MongoDB queries from
|
||||
user parameters, handles time
|
||||
ranges, zones, and filtering
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
@startuml Entity Structures
|
||||
|
||||
!define ENTITY class
|
||||
!define COLLECTION database
|
||||
|
||||
title Структура сущностей для Crowd Reports Sources
|
||||
|
||||
package "Cameras" {
|
||||
ENTITY Camera_Entity {
|
||||
+id: "server_id:camera_index"
|
||||
+server_id: string
|
||||
+camera_index: string
|
||||
+name: string
|
||||
+owner_id: string
|
||||
}
|
||||
|
||||
COLLECTION servers_collection {
|
||||
_id: ObjectId (server_id)
|
||||
owner_id: string
|
||||
name: string
|
||||
cameras: {
|
||||
"0": {name: "Камера 1", active: true},
|
||||
"1": {name: "Камера 2", active: true}
|
||||
}
|
||||
cam_services: {
|
||||
"0": {crowd: {active: true}},
|
||||
"1": {crowd: {active: true}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
package "Folders" {
|
||||
ENTITY Folder_Entity {
|
||||
+id: ObjectId (folder_id)
|
||||
+name: string
|
||||
+owner_id: string
|
||||
+parents: List[string]
|
||||
+objects: List[object_info]
|
||||
}
|
||||
|
||||
COLLECTION folders_collection {
|
||||
_id: ObjectId (folder_id)
|
||||
owner_id: string
|
||||
name: string
|
||||
parents: []
|
||||
objects: [
|
||||
{object_type: "camera", object_id: "server:0"},
|
||||
{object_type: "camera", object_id: "server:1"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
package "Detection Zones" {
|
||||
ENTITY Zone_Entity {
|
||||
+id: string (zone_id)
|
||||
+camera_id: "server_id:camera_index"
|
||||
+owner_id: string
|
||||
+name: string
|
||||
+polygon: List[coordinates]
|
||||
}
|
||||
|
||||
COLLECTION zones_collection {
|
||||
_id: string (zone_id)
|
||||
owner_id: string
|
||||
camera_id: "server_id:camera_index"
|
||||
name: string
|
||||
polygon: [...]
|
||||
deleted: false
|
||||
}
|
||||
}
|
||||
|
||||
' Relationships
|
||||
Camera_Entity --> servers_collection : stored in
|
||||
Folder_Entity --> folders_collection : stored in
|
||||
Zone_Entity --> zones_collection : stored in
|
||||
|
||||
servers_collection --> zones_collection : "camera_id links"
|
||||
folders_collection --> servers_collection : "contains camera references"
|
||||
|
||||
note right of Camera_Entity
|
||||
ID составляется из:
|
||||
server_id + ":" + camera_index
|
||||
|
||||
Пример:
|
||||
"507f1f77bcf86cd799439011:0"
|
||||
end note
|
||||
|
||||
note right of Folder_Entity
|
||||
objects[] может содержать:
|
||||
- cameras
|
||||
- другие folders (вложенность)
|
||||
- другие типы объектов
|
||||
end note
|
||||
|
||||
note right of Zone_Entity
|
||||
Каждая зона привязана
|
||||
к конкретной камере через
|
||||
camera_id поле
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
|
@ -1,35 +1,3 @@
|
|||
@startuml _get_all_user_cameras Sequence Diagram
|
||||
|
||||
title Последовательность выполнения _get_all_user_cameras
|
||||
|
||||
participant "Caller" as caller
|
||||
participant "_get_all_user_cameras" as main_func
|
||||
participant "_get_servers" as get_servers
|
||||
participant "MongoDB" as mongo
|
||||
participant "ivideon.servers" as servers_collection
|
||||
|
||||
note over main_func
|
||||
Входные параметры:
|
||||
- user_id: int
|
||||
- requested_cameras: list[str]
|
||||
(формат: ["server1:0", "server1:1"])
|
||||
- service_name: str (например: "crowd")
|
||||
end note
|
||||
|
||||
caller -> main_func: _get_all_user_cameras(user_id, requested_cameras, service_name)
|
||||
activate main_func
|
||||
|
||||
main_func -> main_func: cameras = {}
|
||||
|
||||
main_func -> get_servers: _get_servers(requested_cameras)
|
||||
activate get_servers
|
||||
|
||||
note over get_servers
|
||||
Извлекает server_ids из camera_ids:
|
||||
["server1:0", "server1:1"]
|
||||
→ ["server1", "server1"]
|
||||
→ ["server1"]
|
||||
end note
|
||||
@startuml _get_all_user_cameras Activity Diagram
|
||||
|
||||
title Алгоритм работы _get_all_user_cameras
|
||||
|
|
@ -139,188 +107,3 @@ note left
|
|||
end note
|
||||
|
||||
@enduml
|
||||
ggVG
|
||||
get_servers -> get_servers: requested_server_ids = [camera_id.split(':')[0] \\nfor camera_id in requested_camera_ids]
|
||||
|
||||
get_servers -> get_servers: query = {\n 'deleted': {'$ne': True},\n '_id': {'$in': requested_server_ids}\n}
|
||||
|
||||
get_servers -> get_servers: projection = {\n '_id': 1, 'owner_id': 1, 'name': 1,\n 'cameras': 1, 'cam_services': 1,\n 'info': 1, 'timezone': 1\n}
|
||||
|
||||
get_servers -> mongo: db.ivideon().servers.find(query, projection)
|
||||
activate mongo
|
||||
mongo -> servers_collection: find documents
|
||||
activate servers_collection
|
||||
servers_collection -> mongo: return server documents
|
||||
deactivate servers_collection
|
||||
mongo -> get_servers: list[server_documents]
|
||||
deactivate mongo
|
||||
|
||||
get_servers -> main_func: return servers_list
|
||||
deactivate get_servers
|
||||
|
||||
loop for each server in servers_list
|
||||
main_func -> main_func: server@startuml _get_all_user_cameras Activity Diagram
|
||||
|
||||
title Алгоритм работы _get_all_user_cameras
|
||||
|
||||
start
|
||||
|
||||
note right
|
||||
**Входные параметры:**
|
||||
• user_id: int
|
||||
• requested_cameras: list[str]
|
||||
(формат: ["server1:0", "server1:1"])
|
||||
• service_name: str (например: "crowd")
|
||||
end note
|
||||
|
||||
:Инициализация cameras = {};
|
||||
|
||||
:Извлечь server_ids из requested_cameras|
|
||||
note right
|
||||
["server1:0", "server1:1"]
|
||||
→ ["server1"]
|
||||
end note
|
||||
|
||||
:Построить MongoDB запрос:
|
||||
query = {
|
||||
'deleted': {'$ne': True},
|
||||
'_id': {'$in': server_ids}
|
||||
}|
|
||||
|
||||
:Задать проекцию полей:
|
||||
projection = {
|
||||
'_id': 1, 'owner_id': 1, 'name': 1,
|
||||
'cameras': 1, 'cam_services': 1,
|
||||
'info': 1, 'timezone': 1
|
||||
}|
|
||||
|
||||
:Выполнить запрос к MongoDB:
|
||||
servers = db.ivideon().servers.find(query, projection)|
|
||||
|
||||
partition "Обработка серверов" {
|
||||
:Взять следующий server;
|
||||
|
||||
while (Есть серверы для обработки?) is (да)
|
||||
:server_id = server['_id'];
|
||||
:is_shared = server['owner_id'] != user_id;
|
||||
:server_build_type = server.get('info', {}).get('build_type', '');
|
||||
:is_server_embedded = server_build_type.endswith('camera');
|
||||
:cam_services = server.get('cam_services', {});
|
||||
|
||||
partition "Обработка камер сервера" {
|
||||
:Взять следующую камеру (camera_idx, camera_data);
|
||||
|
||||
while (Есть камеры на сервере?) is (да)
|
||||
:service_info = cam_services.get(camera_idx, {})
|
||||
.get(service_name, {});
|
||||
|
||||
if (service_info.get('active', False) == True?) then (да)
|
||||
:camera_id = f'{server_id}:{camera_idx}';
|
||||
|
||||
if (is_server_embedded?) then (да)
|
||||
:camera_name = server['name'];
|
||||
else (нет)
|
||||
:camera_name = camera_data.get('name');
|
||||
endif
|
||||
|
||||
:cameras[camera_id] = {
|
||||
'id': camera_id,
|
||||
'owner_id': server['owner_id'],
|
||||
'server': server_id,
|
||||
'name': camera_name,
|
||||
'is_shared': is_shared,
|
||||
'timezone': server.get('timezone') or
|
||||
server.get('timezone_default'),
|
||||
'is_embedded': is_server_embedded
|
||||
};
|
||||
|
||||
else (нет)
|
||||
note right: Камера пропускается - сервис неактивен
|
||||
endif
|
||||
|
||||
:Взять следующую камеру (camera_idx, camera_data);
|
||||
endwhile (нет)
|
||||
}
|
||||
|
||||
:Взять следующий server;
|
||||
endwhile (нет)
|
||||
}
|
||||
|
||||
:return cameras;
|
||||
|
||||
stop
|
||||
|
||||
note left
|
||||
**Результат:** dict[camera_id, camera_info]
|
||||
|
||||
**Пример:**
|
||||
{
|
||||
"507f...439011:0": {
|
||||
"id": "507f...439011:0",
|
||||
"owner_id": "user123",
|
||||
"server": "507f...439011",
|
||||
"name": "Камера входа",
|
||||
"is_shared": false,
|
||||
"timezone": "Europe/Moscow",
|
||||
"is_embedded": false
|
||||
}
|
||||
}
|
||||
end note
|
||||
|
||||
@enduml
|
||||
ggVG_id = server['_id']
|
||||
main_func -> main_func: is_shared = server['owner_id'] != user_id
|
||||
main_func -> main_func: server_build_type = server.get('info', {}).get('build_type', '')
|
||||
main_func -> main_func: is_server_embedded = server_build_type.endswith('camera')
|
||||
main_func -> main_func: cam_services = server.get('cam_services', {})
|
||||
|
||||
loop for camera_idx, camera_data in server.cameras.items()
|
||||
main_func -> main_func: service_info = cam_services.get(camera_idx, {})\\n .get(service_name, {})
|
||||
|
||||
alt service_info.get('active', False) == True
|
||||
main_func -> main_func: camera_id = f'{server_id}:{camera_idx}'
|
||||
|
||||
alt is_server_embedded == True
|
||||
main_func -> main_func: camera_name = server['name']
|
||||
else
|
||||
main_func -> main_func: camera_name = camera_data.get('name')
|
||||
end
|
||||
|
||||
main_func -> main_func: cameras[camera_id] = {\n 'id': camera_id,\n 'owner_id': server['owner_id'],\n 'server': server_id,\n 'name': camera_name,\n 'is_shared': is_shared,\n 'timezone': server.timezone,\n 'is_embedded': is_server_embedded\n}
|
||||
|
||||
note right
|
||||
Создается полная информация
|
||||
о камере для возврата
|
||||
end note
|
||||
|
||||
else
|
||||
note right
|
||||
Камера пропускается:
|
||||
сервис неактивен
|
||||
end note
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
main_func -> caller: return cameras dict
|
||||
deactivate main_func
|
||||
|
||||
note over caller
|
||||
Результат: dict[camera_id, camera_info]
|
||||
где camera_id = "server_id:camera_index"
|
||||
|
||||
Пример:
|
||||
{
|
||||
"507f...439011:0": {
|
||||
"id": "507f...439011:0",
|
||||
"owner_id": "user123",
|
||||
"server": "507f...439011",
|
||||
"name": "Камера входа",
|
||||
"is_shared": false,
|
||||
"timezone": "Europe/Moscow",
|
||||
"is_embedded": false
|
||||
}
|
||||
}
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
|
|
|||
66
ivideon/puml/Crowd/cl_reports_1.puml
Normal file
66
ivideon/puml/Crowd/cl_reports_1.puml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
@startuml Crowd Reports System
|
||||
|
||||
title Система отчетов Crowd
|
||||
|
||||
actor User
|
||||
participant "CrowdReport API" as api
|
||||
database "reports.user_reports" as queue_db
|
||||
participant "Report Builder Worker" as worker
|
||||
participant "OverallStatsReport" as overall
|
||||
participant "QueueStatsReport" as queues
|
||||
participant "WorkTimeReport" as worktime
|
||||
database "crowd.measurements" as measurements_db
|
||||
database "crowd.detected_queues" as queues_db
|
||||
participant "Excel Generator" as excel
|
||||
participant "Storage (S3)" as storage
|
||||
|
||||
== Создание отчета ==
|
||||
User -> api: POST /crowd_reports?op=CREATE
|
||||
api -> api: QueryEnricher._populate_sources()
|
||||
note right: zones.id = ['zone1', 'zone2']
|
||||
api -> queue_db: insert task (status='in_queue')
|
||||
api -> User: report created
|
||||
|
||||
== Обработка очереди ==
|
||||
loop continuous
|
||||
worker -> queue_db: find_one_and_update(status='in_queue')
|
||||
alt task found
|
||||
queue_db -> worker: report task
|
||||
worker -> worker: setup_context(report)
|
||||
|
||||
alt overall_stats_report
|
||||
worker -> overall: make_report()
|
||||
overall -> measurements_db: aggregate({'zones.id': {'$in': [...]}})
|
||||
measurements_db -> overall: measurements data
|
||||
else queue_stats_report
|
||||
worker -> queues: make_report()
|
||||
queues -> queues_db: find({'zone_id': {'$in': [...]}})
|
||||
queues_db -> queues: queues data
|
||||
else work_time_report
|
||||
worker -> worktime: make_report()
|
||||
worktime -> measurements_db: find + schedule analysis
|
||||
measurements_db -> worktime: filtered data
|
||||
end
|
||||
|
||||
worker -> excel: generate XLSX
|
||||
excel -> worker: Excel file
|
||||
worker -> storage: save_to_s3()
|
||||
worker -> queue_db: update(status='done')
|
||||
else no tasks
|
||||
worker -> worker: sleep(SLEEP_INTERVAL)
|
||||
end
|
||||
end
|
||||
|
||||
== Получение результата ==
|
||||
User -> api: GET /crowd_reports/{id}
|
||||
api -> queue_db: find report
|
||||
alt status='done'
|
||||
queue_db -> api: report with download_url
|
||||
api -> User: report ready
|
||||
else status='in_progress'
|
||||
api -> User: report in progress
|
||||
else status='failed'
|
||||
api -> User: report failed
|
||||
end
|
||||
|
||||
@enduml
|
||||
Loading…
Reference in a new issue