update adding refimg to new or existing person via right-click on a face to use new data structures

This commit is contained in:
2025-10-07 23:43:07 +11:00
parent 071164d381
commit 541cbec0de
3 changed files with 65 additions and 25 deletions

4
TODO
View File

@@ -1,5 +1,9 @@
### ###
# #
# fix all face, right-click options:
# partial fix for face to new and existing person - the person and refimg
# data are correct, but the face_file_link is not connected (do this in person) - and if we do run AI jobs, they will overwrite it anyway (watch out for race condition)
#
#1 get override data into view #1 get override data into view
# also all the add ref img/add override, etc are non-functional - FIX the override* stuff first to get table/naming consistency as that is half the problem # also all the add ref img/add override, etc are non-functional - FIX the override* stuff first to get table/naming consistency as that is half the problem
# NMO data -> there is an NMO object (just NMO names/types - |json), then there is per face level data - this should be a reference from Face and Schema/marshmallow # NMO data -> there is an NMO object (just NMO names/types - |json), then there is per face level data - this should be a reference from Face and Schema/marshmallow

View File

@@ -298,6 +298,23 @@ function OverrideForceMatch( person_id, key )
} ) } )
} }
// function that handles the POSTed data that comes back when we add a
// reference image to a new or existing person (right-click on a face)
// used in success callbacks from CreatePersonAndRefimg() and AddRefimgTo()
function handleAddRefimgData(key, data)
{
document.viewing.file_details.faces[item[key].which_face].refimg=data.refimg
document.viewing.file_details.faces[item[key].which_face].refimg_lnk={}
// if we used this img, for now set distance to 0 - it is an exact match!
document.viewing.file_details.faces[item[key].which_face].refimg_lnk.face_distance=0.0
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
// when we right-click a face and make a new person, this code creates and
// associates the face
function CreatePersonAndRefimg( key ) function CreatePersonAndRefimg( key )
{ {
d='&face_id='+item[key].id d='&face_id='+item[key].id
@@ -306,29 +323,17 @@ function CreatePersonAndRefimg( key )
+'&surname='+$('#surname').val() +'&surname='+$('#surname').val()
+'&refimg_data='+item[key].refimg_data +'&refimg_data='+item[key].refimg_data
$.ajax({ type: 'POST', data: d, url: '/match_with_create_person', $.ajax({ type: 'POST', data: d, url: '/match_with_create_person',
success: function(data) { success: function(data) { handleAddRefimgData(key, data ) },
document.viewing.file_details.faces[item[key].which_face].refimg.person.tag=data.who
document.viewing.file_details.faces[item[key].which_face].facefile_lnk.face_distance=data.distance
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
}) })
} }
// when we right-click a face and connect to an existing person, this connects
// the refimg and associates the face
function AddRefimgTo( person_id, key, search ) function AddRefimgTo( person_id, key, search )
{ {
d='&face_id='+item[key].id+'&person_id='+person_id+'&refimg_data='+item[key].refimg_data+'&search='+search d='&face_id='+item[key].id+'&person_id='+person_id+'&refimg_data='+item[key].refimg_data+'&search='+search
$.ajax({ type: 'POST', data: d, url: '/add_refimg_to_person', $.ajax({ type: 'POST', data: d, url: '/add_refimg_to_person',
success: function(data) { success: function(data) { handleAddRefimgData(key, data ) },
document.viewing.file_details.faces[item[key].which_face].refimg.person.tag=data.who
document.viewing.file_details.faces[item[key].which_face].facefile_lnk.face_distance=data.distance
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
}) })
} }

View File

@@ -3,8 +3,9 @@ from flask_wtf import FlaskForm
from flask import request, render_template, redirect, url_for, make_response, jsonify from flask import request, render_template, redirect, url_for, make_response, jsonify
from main import db, app, ma from main import db, app, ma
from settings import Settings, AIModel from settings import Settings, AIModel
from sqlalchemy import Sequence, func from sqlalchemy import Sequence, func, select
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import joinedload
from flask_login import login_required, current_user from flask_login import login_required, current_user
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from shared import GenFace, GenThumb, PA from shared import GenFace, GenThumb, PA
@@ -114,7 +115,7 @@ def AddRefimgToPerson( filename, person ):
SetFELog( f"<b>Failed to add Refimg:</b>&nbsp;{e.orig}", "danger" ) SetFELog( f"<b>Failed to add Refimg:</b>&nbsp;{e.orig}", "danger" )
except Exception as e: except Exception as e:
SetFELog( f"<b>Failed to modify Refimg:</b>&nbsp;{e}", "danger" ) SetFELog( f"<b>Failed to modify Refimg:</b>&nbsp;{e}", "danger" )
return return refimg
################################################################################ ################################################################################
# TempRefimgFile: helper function that takes data POST'd (from dialog box to # TempRefimgFile: helper function that takes data POST'd (from dialog box to
@@ -182,9 +183,12 @@ def match_with_create_person():
p = Person( tag=request.form["tag"], surname=request.form["surname"], firstname=request.form["firstname"] ) p = Person( tag=request.form["tag"], surname=request.form["surname"], firstname=request.form["firstname"] )
# add this fname (of temp refimg) to person # add this fname (of temp refimg) to person
fname=TempRefimgFile( request.form['refimg_data'], p.tag ) fname=TempRefimgFile( request.form['refimg_data'], p.tag )
AddRefimgToPerson( fname, p ) r=AddRefimgToPerson( fname, p )
SetFELog( f"Created person: {p.tag}" ) SetFELog( f"Created person: {p.tag}" )
return make_response( jsonify( who=p.tag, distance='0.0' ) ) refimg_schema=RefimgSchema(many=False)
r_data=refimg_schema.dump(r)
return make_response( jsonify( refimg=r_data, who=p.tag, distance='0.0' ) )
################################################################################ ################################################################################
# /person/<id> -> GET/POST(save or delete) -> shows/edits/delets a single person # /person/<id> -> GET/POST(save or delete) -> shows/edits/delets a single person
@@ -267,7 +271,7 @@ def add_refimg():
except Exception as e: except Exception as e:
SetFELog( f"<b>Failed to load reference image:</b>&nbsp;{e}", "danger" ) SetFELog( f"<b>Failed to load reference image:</b>&nbsp;{e}", "danger" )
AddRefimgToPerson( fname, person ) r=AddRefimgToPerson( fname, person )
return redirect( url_for( 'person', id=person.id) ) return redirect( url_for( 'person', id=person.id) )
################################################################################ ################################################################################
@@ -289,6 +293,28 @@ def find_persons(who):
return make_response( resp ) return make_response( resp )
class FaceRefimgLinkSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = FaceRefimgLink
load_instance = True
class PersonSchema(ma.SQLAlchemyAutoSchema):
class Meta: model=Person
load_instance = True
class RefimgSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Refimg
exclude = ('face',)
load_instance = True
person = ma.Nested(PersonSchema)
class FaceSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model=Face
exclude = ('face',)
load_instance = True
refimg = ma.Nested(RefimgSchema,allow_none=True)
refimg_lnk = ma.Nested(FaceRefimgLinkSchema,allow_none=True)
################################################################################ ################################################################################
# /add_refimg_to_person/ -> POST # /add_refimg_to_person/ -> POST
@@ -296,12 +322,14 @@ def find_persons(who):
@app.route("/add_refimg_to_person", methods=["POST"]) @app.route("/add_refimg_to_person", methods=["POST"])
@login_required @login_required
def add_refimg_to_person(): def add_refimg_to_person():
f = Face.query.get( request.form['face_id'] ) stmt = select(Face).options( joinedload(Face.refimg_lnk) ).where(Face.id == request.form['face_id'])
p = Person.query.get( request.form['person_id'] ) f=db.session.execute(stmt).scalars().first()
stmt = select(Person).options( joinedload(Person.refimg) ).where(Person.id == request.form['person_id'])
p=db.session.execute(stmt).scalars().first()
# add this fname (of temp refimg) to person # add this fname (of temp refimg) to person
fname=TempRefimgFile( request.form['refimg_data'], p.tag ) fname=TempRefimgFile( request.form['refimg_data'], p.tag )
AddRefimgToPerson( fname, p ) r=AddRefimgToPerson( fname, p )
if request.form['search'] == "true": if request.form['search'] == "true":
jex=[] jex=[]
@@ -316,7 +344,10 @@ def add_refimg_to_person():
jex.append( JobExtra( name=f"path_type", value=str(ptype.id) ) ) jex.append( JobExtra( name=f"path_type", value=str(ptype.id) ) )
job=NewJob( name="run_ai_on_path", num_files=0, wait_for=None, jex=jex, desc="Look for face(s) in storage path(s)" ) job=NewJob( name="run_ai_on_path", num_files=0, wait_for=None, jex=jex, desc="Look for face(s) in storage path(s)" )
return make_response( jsonify( who=p.tag, distance='0.0' ) ) refimg_schema=RefimgSchema(many=False)
r_data=refimg_schema.dump(r)
return make_response( jsonify( refimg=r_data, who=p.tag, distance='0.0' ) )
################################################################################ ################################################################################
# /add_force_match_override -> POST # /add_force_match_override -> POST