File size: 4,915 Bytes
75557db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""
api/persons.py — Endpoints quản lý thông tin Person

GET    /api/persons          — Danh sách tất cả persons có embedding
PUT    /api/persons/<id>     — Cập nhật thông tin cá nhân
DELETE /api/persons/<id>     — Xóa person (cascade: visits, embeddings, logs, MongoDB)
DELETE /api/db/<filename>    — Legacy alias xóa theo tên file (backward compat)
"""
import logging

from flask import Blueprint, jsonify, request

from face_model.analyzer import face_analyzer
from storage.mongo_storage import mongo_storage
from storage.pg_storage import pg_storage

logger = logging.getLogger("api.persons")

bp = Blueprint("persons", __name__, url_prefix="/api")


@bp.get("/persons")
def list_persons():
    """
    GET /api/persons
    Trả về danh sách tất cả persons có face_embedding active.
    """
    try:
        persons = pg_storage.list_persons_with_faces()
        for p in persons:
            crop_id = p.get("face_crop_mongo_id", "")
            p["face_url"] = f"/api/face-crops/{crop_id}" if crop_id else None
        return jsonify({
            "total": len(persons),
            "persons": persons,
        }), 200
    except Exception as e:
        logger.error(f"[list_persons] Error: {e}", exc_info=True)
        return jsonify({"error": {
            "code": "FETCH_FAILED",
            "message": f"Không thể lấy danh sách persons: {e}",
            "status": 500,
        }}), 500


@bp.put("/persons/<person_id>")
def update_person(person_id: str):
    """
    PUT /api/persons/<person_id>
    Body JSON: { full_name, date_of_birth?, phone_number?, address? }
    """
    body = request.get_json(silent=True)
    if not body or "full_name" not in body:
        return jsonify({"error": {
            "code": "VALIDATION_ERROR",
            "message": "Thiếu trường bắt buộc: full_name.",
            "status": 400,
        }}), 400

    full_name    = body["full_name"].strip()
    date_of_birth = body.get("date_of_birth")
    phone_number  = body.get("phone_number")
    address       = body.get("address")

    if not full_name:
        return jsonify({"error": {
            "code": "VALIDATION_ERROR",
            "message": "full_name không được để trống.",
            "status": 400,
        }}), 400

    try:
        updated = pg_storage.update_person_info(
            person_id=person_id,
            full_name=full_name,
            date_of_birth=date_of_birth,
            phone_number=phone_number,
            address=address,
        )
        if not updated:
            return jsonify({"error": {
                "code": "NOT_FOUND",
                "message": f"Không tìm thấy person với ID: {person_id}",
                "status": 404,
            }}), 404

        # Cập nhật tên trong RAM cache
        face_analyzer.update_name_in_cache(person_id, full_name)

        return jsonify({
            "success":   True,
            "person_id": person_id,
            "full_name": full_name,
            "message":   f"Đã cập nhật thông tin cho {full_name}.",
        }), 200

    except Exception as e:
        logger.error(f"[update_person] Error: {e}", exc_info=True)
        return jsonify({"error": {
            "code": "UPDATE_FAILED",
            "message": f"Không thể cập nhật thông tin: {e}",
            "status": 500,
        }}), 500


@bp.delete("/persons/<person_id>")
def delete_person(person_id: str):
    """
    DELETE /api/persons/<person_id>
    Xóa person và toàn bộ dữ liệu liên quan (cascade).
    """
    try:
        success, mongo_ids = pg_storage.delete_person(person_id)
        if not success:
            return jsonify({"error": {
                "code": "NOT_FOUND",
                "message": f"Không tìm thấy person với ID: {person_id}",
                "status": 404,
            }}), 404

        # Xóa ảnh crop trên MongoDB
        for mid in mongo_ids:
            mongo_storage.delete_face_crop(mid)

        # Xóa khỏi RAM cache
        face_analyzer.remove_from_cache(person_id)

        return jsonify({
            "success":   True,
            "person_id": person_id,
            "message":   "Đã xóa person thành công.",
            "total_faces": face_analyzer.total,
        }), 200

    except Exception as e:
        logger.error(f"[delete_person] Error: {e}", exc_info=True)
        return jsonify({"error": {
            "code": "DELETE_FAILED",
            "message": f"Không thể xóa person: {e}",
            "status": 500,
        }}), 500


# ── Legacy endpoints ───────────────────────────────────────────────────────

@bp.put("/person/<person_id>")
def update_person_legacy(person_id: str):
    """Legacy alias → PUT /api/persons/<person_id>"""
    return update_person(person_id)