diff --git a/admin-frontend/src/pages/Identify.tsx b/admin-frontend/src/pages/Identify.tsx
index 699f93c..0625566 100644
--- a/admin-frontend/src/pages/Identify.tsx
+++ b/admin-frontend/src/pages/Identify.tsx
@@ -692,7 +692,19 @@ export default function Identify() {
const handleIdentify = async () => {
- if (!currentFace) return
+ console.log('handleIdentify called', { currentFace, personId, firstName, lastName, canIdentify })
+
+ if (!currentFace) {
+ console.warn('handleIdentify: No current face')
+ return
+ }
+
+ // Validate that we have either a person ID or both first and last name
+ if (!personId && (!firstName.trim() || !lastName.trim())) {
+ alert('Please select an existing person or enter first name and last name.')
+ return
+ }
+
setBusy(true)
const trimmedFirstName = firstName.trim()
const trimmedLastName = lastName.trim()
@@ -719,7 +731,10 @@ export default function Identify() {
payload.date_of_birth = trimmedDob
}
}
+
+ console.log('Identifying face:', currentFace.id, 'with payload:', payload)
await facesApi.identify(currentFace.id, payload)
+
// Optimistic: remove identified faces from list
const identifiedSet = new Set([currentFace.id, ...additional])
const remaining = faces.filter((f) => !identifiedSet.has(f.id))
@@ -741,6 +756,10 @@ export default function Identify() {
}
// Don't clear form - let the useEffect handle restoring/clearing when face changes
+ } catch (error: any) {
+ console.error('Error identifying face:', error)
+ const errorMessage = error.response?.data?.detail || error.message || 'Failed to identify face. Please try again.'
+ alert(errorMessage)
} finally {
setBusy(false)
}
@@ -1378,13 +1397,15 @@ export default function Identify() {
disabled={!!personId} />
-
-
-
+
+
diff --git a/backend/api/faces.py b/backend/api/faces.py
index d9dfff3..64f9f4f 100644
--- a/backend/api/faces.py
+++ b/backend/api/faces.py
@@ -2,6 +2,8 @@
from __future__ import annotations
+from datetime import datetime
+
from fastapi import APIRouter, Depends, HTTPException, Query, status
from fastapi.responses import FileResponse, Response
from rq import Queue
@@ -306,12 +308,14 @@ def identify_face(
status_code=status.HTTP_400_BAD_REQUEST,
detail="first_name and last_name are required to create a person",
)
+ # Explicitly set created_date to ensure it's a valid datetime object
person = Person(
first_name=first_name,
last_name=last_name,
middle_name=middle_name,
maiden_name=maiden_name,
date_of_birth=request.date_of_birth,
+ created_date=datetime.utcnow(),
)
db.add(person)
db.flush() # get person.id
diff --git a/backend/api/pending_identifications.py b/backend/api/pending_identifications.py
index 110ab59..889b627 100644
--- a/backend/api/pending_identifications.py
+++ b/backend/api/pending_identifications.py
@@ -330,12 +330,14 @@ def approve_deny_pending_identifications(
# Create person if doesn't exist
created_person = False
if not person:
+ # Explicitly set created_date to ensure it's a valid datetime object
person = Person(
first_name=row.first_name,
last_name=row.last_name,
middle_name=row.middle_name,
maiden_name=row.maiden_name,
date_of_birth=row.date_of_birth,
+ created_date=datetime.utcnow(),
)
main_db.add(person)
main_db.flush() # get person.id
diff --git a/backend/api/people.py b/backend/api/people.py
index 27aadc4..ad6d774 100644
--- a/backend/api/people.py
+++ b/backend/api/people.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from datetime import datetime
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
@@ -118,12 +119,14 @@ def create_person(request: PersonCreateRequest, db: Session = Depends(get_db)) -
last_name = request.last_name.strip()
middle_name = request.middle_name.strip() if request.middle_name else None
maiden_name = request.maiden_name.strip() if request.maiden_name else None
+ # Explicitly set created_date to ensure it's a valid datetime object
person = Person(
first_name=first_name,
last_name=last_name,
middle_name=middle_name,
maiden_name=maiden_name,
date_of_birth=request.date_of_birth,
+ created_date=datetime.utcnow(),
)
db.add(person)
try:
diff --git a/backend/db/models.py b/backend/db/models.py
index 45fe127..6b85d01 100644
--- a/backend/db/models.py
+++ b/backend/db/models.py
@@ -10,6 +10,7 @@ from sqlalchemy import (
Column,
Date,
DateTime,
+ String,
ForeignKey,
Index,
Integer,
@@ -37,19 +38,37 @@ class PrismaCompatibleDateTime(TypeDecorator):
Prisma's SQLite driver has issues with microseconds in datetime strings.
This type ensures datetimes are stored in ISO format without microseconds:
'YYYY-MM-DD HH:MM:SS' instead of 'YYYY-MM-DD HH:MM:SS.ffffff'
+
+ Uses String as the underlying type for SQLite to have full control over the format.
"""
- impl = DateTime
+ impl = String
cache_ok = True
def process_bind_param(self, value, dialect):
- """Convert Python datetime to SQL string format."""
+ """Convert Python datetime to SQL string format without microseconds."""
if value is None:
return None
if isinstance(value, datetime):
# Strip microseconds and format as ISO string without microseconds
# This ensures Prisma can read it correctly
- value = value.replace(microsecond=0)
- return value.strftime('%Y-%m-%d %H:%M:%S')
+ return value.replace(microsecond=0).strftime('%Y-%m-%d %H:%M:%S')
+ # If it's already a string, ensure it doesn't have microseconds
+ if isinstance(value, str):
+ try:
+ # Parse and reformat to remove microseconds
+ if '.' in value:
+ # Has microseconds or timezone info - strip them
+ dt = datetime.strptime(value.split('.')[0], '%Y-%m-%d %H:%M:%S')
+ elif 'T' in value:
+ # ISO format with T
+ dt = datetime.fromisoformat(value.replace('Z', '+00:00').split('.')[0])
+ else:
+ # Already in correct format
+ return value
+ return dt.strftime('%Y-%m-%d %H:%M:%S')
+ except (ValueError, TypeError):
+ # If parsing fails, return as-is
+ return value
return value
def process_result_value(self, value, dialect):
diff --git a/backend/services/video_service.py b/backend/services/video_service.py
index b73423b..0009626 100644
--- a/backend/services/video_service.py
+++ b/backend/services/video_service.py
@@ -224,12 +224,14 @@ def identify_person_in_video(
if existing_person:
person = existing_person
else:
+ # Explicitly set created_date to ensure it's a valid datetime object
person = Person(
first_name=first_name,
last_name=last_name,
middle_name=middle_name,
maiden_name=maiden_name,
date_of_birth=date_of_birth,
+ created_date=datetime.utcnow(),
)
db.add(person)
db.flush() # Get person.id