From 8141aaa323927e28b71b07307371c601ab7f6fa5 Mon Sep 17 00:00:00 2001 From: t0xa Date: Mon, 19 Jan 2026 16:05:02 +0300 Subject: [PATCH] ivideon: add face_report generated diagram --- ivideon/puml/faces/report_creation.puml | 96 +++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 ivideon/puml/faces/report_creation.puml diff --git a/ivideon/puml/faces/report_creation.puml b/ivideon/puml/faces/report_creation.puml new file mode 100644 index 0000000..e7ef9ad --- /dev/null +++ b/ivideon/puml/faces/report_creation.puml @@ -0,0 +1,96 @@ +@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