File size: 4,446 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
"""
api/database.py — Endpoints quản lý database face (danh sách & xóa theo tên)

GET    /api/faces         — Danh sách khuôn mặt (RESTful)
GET    /api/db            — Legacy alias
DELETE /api/db/<filename> — Legacy: xóa theo tên file (backward compat)
GET    /api/unknown-faces — Danh sách khuôn mặt chưa nhận diện
"""
import logging
import os

from flask import Blueprint, jsonify

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.database")

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


@bp.get("/faces")
def list_faces():
    """
    GET /api/faces
    Trả về danh sách khuôn mặt đã đăng ký từ Supabase + MongoDB.
    """
    try:
        persons = pg_storage.list_persons_with_faces()
        faces = [
            {
                "filename":       f"{p['full_name']}.jpg",
                "name":           p["full_name"],
                "person_id":      p["person_id"],
                "status":         p.get("status", "active"),
                "total_appearances": p.get("total_appearances", 0),
                "last_seen_at":   p.get("last_seen_at"),
                "url":            f"/api/face-crops/{p['face_crop_mongo_id']}" if p.get("face_crop_mongo_id") else None,
            }
            for p in persons
        ]
        return jsonify({
            "total": len(faces),
            "faces": faces,
        }), 200
    except Exception as e:
        logger.error(f"[list_faces] Error: {e}", exc_info=True)
        return jsonify({"error": {
            "code": "FETCH_FAILED",
            "message": f"Lỗi khi lấy danh sách khuôn mặt: {e}",
            "status": 500,
        }}), 500


@bp.get("/db")
def list_faces_legacy():
    """
    Legacy alias GET /api/db (backward compatibility với frontend).
    Response format giữ nguyên để frontend không cần sửa.
    """
    try:
        persons = pg_storage.list_persons_with_faces()
        faces = [
            {
                "filename": f"{p['full_name']}.jpg",
                "name":     p["full_name"],
                "url":      f"/api/face-crops/{p['face_crop_mongo_id']}" if p.get("face_crop_mongo_id") else "",
            }
            for p in persons
        ]
        return jsonify({
            "total": len(faces),
            "faces": faces,
        }), 200
    except Exception as e:
        logger.error(f"[list_faces_legacy] Error: {e}", exc_info=True)
        return jsonify({"detail": f"Lỗi khi lấy danh sách khuôn mặt: {e}"}), 500


@bp.delete("/db/<filename>")
def delete_face_legacy(filename: str):
    """
    Legacy DELETE /api/db/<filename> — Xóa theo tên file (backward compat với frontend).
    """
    name = os.path.splitext(filename)[0]
    try:
        success, mongo_ids = pg_storage.delete_person_by_name(name)
        if not success:
            return jsonify({"detail": "Không tìm thấy người này trong database."}), 404

        for mid in mongo_ids:
            mongo_storage.delete_face_crop(mid)

        # Xóa khỏi RAM cache (theo tên)
        to_remove = [
            pid for pid, n in zip(face_analyzer.known_person_ids, face_analyzer.known_names)
            if n == name
        ]
        for pid in to_remove:
            face_analyzer.remove_from_cache(pid)

        return jsonify({
            "success":     True,
            "message":     f"Đã xóa thành công {name} khỏi database.",
            "total_faces": face_analyzer.total,
        }), 200
    except Exception as e:
        logger.error(f"[delete_face_legacy] Error: {e}", exc_info=True)
        return jsonify({"detail": f"Lỗi khi xóa khuôn mặt: {e}"}), 500


@bp.get("/unknown-faces")
def list_unknown_faces():
    """
    GET /api/unknown-faces
    Trả về danh sách khuôn mặt chưa nhận diện từ MongoDB.
    """
    try:
        unknowns = mongo_storage.list_unknown_faces(limit=50)
        return jsonify({
            "total":         len(unknowns),
            "unknown_faces": unknowns,
        }), 200
    except Exception as e:
        logger.error(f"[list_unknown_faces] Error: {e}", exc_info=True)
        return jsonify({"error": {
            "code": "FETCH_FAILED",
            "message": f"Lỗi khi lấy danh sách unknown faces: {e}",
            "status": 500,
        }}), 500