96 lines
3.9 KiB
Text
96 lines
3.9 KiB
Text
@startuml
|
||
title Создание отчёта о рабочем времени (face_work_time_json)
|
||
|
||
actor Client
|
||
participant "face/api" as API
|
||
database "MongoDB\nuser_reports" as ReportsDB
|
||
participant "report_checker\n(Worker)" as Checker
|
||
participant "reporter.py" as Reporter
|
||
participant "WorkTimeJsonReport" as WorkTime
|
||
participant "data_loader" as DataLoader
|
||
database "MongoDB\nface_events" as EventsDB
|
||
database "MongoDB\nfaces" as FacesDB
|
||
participant "tools.py" as Tools
|
||
database "Redis" as Redis
|
||
|
||
== 1. Создание задачи на отчёт ==
|
||
Client -> API: POST /reports\n(type=face_work_time_json)
|
||
API -> API: FaceReport.create()\nvalidate options, build query
|
||
API -> ReportsDB: insert(status='in_queue')
|
||
API --> Client: {success: true, id: report_id}
|
||
|
||
== 2. Обработка отчёта воркером ==
|
||
loop каждые SLEEP_INTERVAL секунд
|
||
Checker -> ReportsDB: find_one_and_update\n(status='in_queue' → 'in_progress')
|
||
end
|
||
|
||
Checker -> Checker: ProcessPoolExecutor\ncreate_report_sync(report)
|
||
note right: Новый процесс\nс asyncio.run()
|
||
|
||
Checker -> Reporter: create_report(report)
|
||
Reporter -> Reporter: @with_context\nустановить контекст пользователя
|
||
Reporter -> Reporter: Выбрать класс из REGISTRY\n(face_work_time_json → WorkTimeJsonReport)
|
||
Reporter -> WorkTime: make_report()
|
||
|
||
== 3. Загрузка данных ==
|
||
WorkTime -> DataLoader: get_events(query)
|
||
DataLoader -> EventsDB: find(query)\n[best_shot_time, face_id, camera_id]
|
||
EventsDB --> DataLoader: events[]
|
||
DataLoader --> WorkTime: events
|
||
|
||
WorkTime -> DataLoader: get_faces(galleries, events,\ncameras_in, cameras_out, tz)
|
||
|
||
loop для каждого события
|
||
DataLoader -> DataLoader: result[face_id] = GroupedFace(face_id)
|
||
DataLoader -> DataLoader: group.handle(best_shot_time, direction)
|
||
end
|
||
|
||
DataLoader -> DataLoader: _get_faces_without_events()\n(лица из галерей без событий)
|
||
DataLoader -> FacesDB: find(_id NOT IN face_ids,\ngallery IN galleries)
|
||
FacesDB --> DataLoader: faces_without_events
|
||
|
||
DataLoader -> DataLoader: face_ids = events + faces_without_events
|
||
|
||
== 4. Обогащение данных фотографиями ==
|
||
DataLoader -> DataLoader: _get_faces_data(face_ids)
|
||
DataLoader -> FacesDB: find(_id IN face_ids)\n[person, photos, description, gallery_id]
|
||
FacesDB --> DataLoader: faces_data[]
|
||
|
||
loop для каждого face
|
||
DataLoader -> DataLoader: _get_photo(face_data)
|
||
alt photos пустой или None
|
||
DataLoader --> DataLoader: return None
|
||
note right #pink: **ПРИЧИНА 1**\nphotos отсутствует
|
||
else photos есть
|
||
DataLoader -> DataLoader: найти default фото\nили первое из списка
|
||
DataLoader -> Tools: get_thumb_url(photo)
|
||
alt есть thumbnails.thumbnail_200.url
|
||
Tools --> DataLoader: url
|
||
else есть thumbnails.thumbnail_200.obj_ref
|
||
Tools -> Tools: storage.generate_presigned_url()
|
||
Tools --> DataLoader: presigned_url
|
||
else нет thumbnail_200
|
||
Tools --> DataLoader: None
|
||
note right #pink: **ПРИЧИНА 2**\nнет thumbnail_200
|
||
end
|
||
end
|
||
DataLoader -> DataLoader: face.photo = photo_url
|
||
end
|
||
|
||
DataLoader --> WorkTime: List[GroupedFace]
|
||
|
||
== 5. Генерация и сохранение ==
|
||
WorkTime -> WorkTime: _generate(query, faces, tz, schedule)
|
||
note right: Формирует JSON:\n[name, photo, face_id, ...]
|
||
|
||
WorkTime -> Redis: setex(key, json, TTL=1 day)
|
||
WorkTime -> ReportsDB: update(status='done',\n_redis_key=key)
|
||
|
||
== 6. Получение результата ==
|
||
Client -> API: GET /reports/{id}/json
|
||
API -> ReportsDB: find(_id=id)
|
||
API -> Redis: get(_redis_key)
|
||
Redis --> API: json_data
|
||
API --> Client: {summary: {...}, details: {...}}
|
||
|
||
@enduml
|