started hooking up right-click menu for Dir and Files all the way through to calling the new ScanFileForPerson() - which is still incomplete but does use the new Faces DB linkages and functions
This commit is contained in:
10
TODO
10
TODO
@@ -1,11 +1,11 @@
|
|||||||
## GENERAL
|
## GENERAL
|
||||||
|
|
||||||
* create a new table file_face_refimg_link:
|
* allow rotate of image (permanently on FS, so its right everywhere)
|
||||||
file_id, face_enc, ref_img (can be null)
|
|
||||||
|
* improve photo browser -> view file, rather than just allowing browser to show image
|
||||||
|
|
||||||
* need AI code to:
|
* need AI code to:
|
||||||
scan for unknown faces, instead of storing array of all faces in FILE->FACES use table above one row per FILE & FACE with refimg_link as null to start
|
- make use of new FACE structures/links ... (code for this is in pa_job_manager, just not being called/used as yet) && something like:
|
||||||
* when we do ai matching, we find all refimg is null (for a specific file) and match that
|
|
||||||
* need pa_job_mgr AI jobs to have low-level functions:
|
|
||||||
FindUnknownFacesInFile()
|
FindUnknownFacesInFile()
|
||||||
MatchRefImgWithUnknownFace()
|
MatchRefImgWithUnknownFace()
|
||||||
Then create wrapper funcs:
|
Then create wrapper funcs:
|
||||||
|
|||||||
22
ai.py
22
ai.py
@@ -10,6 +10,9 @@ from person import Person, PersonRefimgLink
|
|||||||
from refimg import Refimg
|
from refimg import Refimg
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
|
from job import Job, JobExtra, Joblog, NewJob
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -28,3 +31,22 @@ def aistats():
|
|||||||
last_fname = e.name
|
last_fname = e.name
|
||||||
entry['people'].append( { 'tag': p.tag } )
|
entry['people'].append( { 'tag': p.tag } )
|
||||||
return render_template("aistats.html", page_title='Placeholder', entries=entries)
|
return render_template("aistats.html", page_title='Placeholder', entries=entries)
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /run_ai_on -> CAM: needs more thought (what actual params, e.g list of file -
|
||||||
|
# tick, but which face or faces? are we forcing a re-finding of unknown faces
|
||||||
|
# or just looking for matches? (maybe in the long run there are different
|
||||||
|
# routes, not params - stuff we will work out as we go)
|
||||||
|
################################################################################
|
||||||
|
@app.route("/run_ai_on", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def run_ai_on():
|
||||||
|
jex=[]
|
||||||
|
for el in request.form:
|
||||||
|
jex.append( JobExtra( name=f"{el}", value=request.form[el] ) )
|
||||||
|
print( f"would create new job with extras={jex}" )
|
||||||
|
job=NewJob( "run_ai_on", 0, None, jex )
|
||||||
|
st.SetAlert("success")
|
||||||
|
st.SetMessage( f"Created <a href=/job/{job.id}>Job #{job.id}</a> to Look for face(s) in selected file(s)")
|
||||||
|
return render_template("base.html")
|
||||||
|
|||||||
@@ -137,9 +137,13 @@ class Entry(Base):
|
|||||||
in_dir = relationship ("Dir", secondary="entry_dir_link", uselist=False )
|
in_dir = relationship ("Dir", secondary="entry_dir_link", uselist=False )
|
||||||
|
|
||||||
def FullPathOnFS(self):
|
def FullPathOnFS(self):
|
||||||
s=self.in_dir.in_path.path_prefix + '/'
|
if self.in_dir:
|
||||||
if len(self.in_dir.rel_path) > 0:
|
s=self.in_dir.in_path.path_prefix + '/'
|
||||||
s += self.in_dir.rel_path + '/'
|
if len(self.in_dir.rel_path) > 0:
|
||||||
|
s += self.in_dir.rel_path + '/'
|
||||||
|
# this occurs when we have a dir that is the root of a path
|
||||||
|
else:
|
||||||
|
s=self.dir_details.in_path.path_prefix+'/'
|
||||||
s += self.name
|
s += self.name
|
||||||
return s
|
return s
|
||||||
|
|
||||||
@@ -431,6 +435,8 @@ def RunJob(job):
|
|||||||
JobRestoreFiles(job)
|
JobRestoreFiles(job)
|
||||||
elif job.name == "processai":
|
elif job.name == "processai":
|
||||||
JobProcessAI(job)
|
JobProcessAI(job)
|
||||||
|
elif job.name == "run_ai_on":
|
||||||
|
JobRunAIOn(job)
|
||||||
else:
|
else:
|
||||||
print("ERROR: Requested to process unknown job type: {}".format(job.name))
|
print("ERROR: Requested to process unknown job type: {}".format(job.name))
|
||||||
# okay, we finished a job, so check for any jobs that are dependant on this and run them...
|
# okay, we finished a job, so check for any jobs that are dependant on this and run them...
|
||||||
@@ -940,6 +946,44 @@ def JobProcessAI(job):
|
|||||||
FinishJob(job, "Finished Processesing AI")
|
FinishJob(job, "Finished Processesing AI")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def WrapperForScanFileForPerson(job, entry):
|
||||||
|
which_person=[jex.value for jex in job.extra if jex.name == "person"][0]
|
||||||
|
if which_person == "all":
|
||||||
|
ppl=session.query(Person).all()
|
||||||
|
else:
|
||||||
|
ppl=session.query(Person).filter(Person.tag==which_person).all()
|
||||||
|
for person in ppl:
|
||||||
|
print( f"(wrapper) call == ScanFileForPerson( {entry.id}, {person.id}, force=False )" )
|
||||||
|
return
|
||||||
|
|
||||||
|
def JobRunAIOn(job):
|
||||||
|
AddLogForJob(job, f"INFO: Starting looking For faces in files job...")
|
||||||
|
which_person=[jex.value for jex in job.extra if jex.name == "person"][0]
|
||||||
|
if which_person == "all":
|
||||||
|
ppl=session.query(Person).all()
|
||||||
|
else:
|
||||||
|
ppl=session.query(Person).filter(Person.tag==which_person).all()
|
||||||
|
print( "JobRunAIOn() called" )
|
||||||
|
for person in ppl:
|
||||||
|
print( f"person={person.tag}" )
|
||||||
|
|
||||||
|
for jex in job.extra:
|
||||||
|
if 'eid-' in jex.name:
|
||||||
|
entry=session.query(Entry).get(jex.value)
|
||||||
|
print( f'en={entry}' )
|
||||||
|
if entry.type.name == 'Directory':
|
||||||
|
ProcessFilesInDir( job, entry, WrapperForScanFileForPerson )
|
||||||
|
elif entry.type.name == 'Image':
|
||||||
|
which_file=session.query(Entry).join(File).filter(Entry.id==jex.value).first()
|
||||||
|
print( f"JobRunAIOn: file to process had id: {which_file.id}" )
|
||||||
|
for person in ppl:
|
||||||
|
print( f"call == ScanFileForPerson( {which_file.id}, {person.id}, force=False )" )
|
||||||
|
else:
|
||||||
|
AddLogForJob( job, f'Not processing Entry: {entry.name} - not an image' )
|
||||||
|
print(" HARD EXITING to keep testing " )
|
||||||
|
exit( -1 )
|
||||||
|
FinishJob(job, "Finished Processesing AI")
|
||||||
|
return
|
||||||
|
|
||||||
def GenHashAndThumb(job, e):
|
def GenHashAndThumb(job, e):
|
||||||
# commit every 100 files to see progress being made but not hammer the database
|
# commit every 100 files to see progress being made but not hammer the database
|
||||||
@@ -1056,6 +1100,7 @@ def compareAI(known_encoding, unknown_encoding):
|
|||||||
|
|
||||||
def ProcessFilesInDir(job, e, file_func):
|
def ProcessFilesInDir(job, e, file_func):
|
||||||
if DEBUG==1:
|
if DEBUG==1:
|
||||||
|
print( f"???? e={e}" )
|
||||||
print( f"DEBUG: ProcessFilesInDir: {e.FullPathOnFS()}")
|
print( f"DEBUG: ProcessFilesInDir: {e.FullPathOnFS()}")
|
||||||
if e.type.name != 'Directory':
|
if e.type.name != 'Directory':
|
||||||
file_func(job, e)
|
file_func(job, e)
|
||||||
|
|||||||
@@ -222,7 +222,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{# if this dir is the toplevel of the cwd, show the folder icon #}
|
{# if this dir is the toplevel of the cwd, show the folder icon #}
|
||||||
{% if dirname| TopLevelFolderOf(cwd) %}
|
{% if dirname| TopLevelFolderOf(cwd) %}
|
||||||
<figure class="px-1 dir" dir="{{dirname}}">
|
<figure class="px-1 dir" id={{obj.id}} dir="{{dirname}}">
|
||||||
<i style="font-size:{{size|int-22}};" class="fas fa-folder"></i>
|
<i style="font-size:{{size|int-22}};" class="fas fa-folder"></i>
|
||||||
<figcaption class="figure-caption text-center text-wrap text-break">{{obj.name}}</figcaption>
|
<figcaption class="figure-caption text-center text-wrap text-break">{{obj.name}}</figcaption>
|
||||||
</figure class="figure">
|
</figure class="figure">
|
||||||
@@ -270,6 +270,13 @@ function GetSelnAsData()
|
|||||||
return to_del
|
return to_del
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RunAIOnSeln(person)
|
||||||
|
{
|
||||||
|
post_data = GetSelnAsData()
|
||||||
|
post_data += '&person='+person.replace('ai-','')
|
||||||
|
$.ajax({ type: 'POST', data: post_data, url: '/run_ai_on', success: function(data){ window.location='/'; return false; } })
|
||||||
|
}
|
||||||
|
|
||||||
function DelDBox(del_or_undel)
|
function DelDBox(del_or_undel)
|
||||||
{
|
{
|
||||||
to_del = GetSelnAsData()
|
to_del = GetSelnAsData()
|
||||||
@@ -457,6 +464,34 @@ function NoSel() {
|
|||||||
$('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; });
|
$('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; });
|
||||||
$(document).on('click', function(e) { $('.highlight').removeClass('highlight') ; SetButtonState() });
|
$(document).on('click', function(e) { $('.highlight').removeClass('highlight') ; SetButtonState() });
|
||||||
|
|
||||||
|
|
||||||
|
// different context menu on directory
|
||||||
|
$.contextMenu({
|
||||||
|
selector: '.dir',
|
||||||
|
build: function($triggerElement, e){
|
||||||
|
if( NoSel() )
|
||||||
|
DoSel(e, e.currentTarget )
|
||||||
|
item_list = {
|
||||||
|
ai: {
|
||||||
|
name: "Scan file for faces",
|
||||||
|
items: {
|
||||||
|
{% for p in people %}
|
||||||
|
"ai-{{p.tag}}": {"name": "{{p.tag}}"},
|
||||||
|
{% endfor %}
|
||||||
|
"ai-all": {"name": "all"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
callback: function( key, options) {
|
||||||
|
if( key.startsWith("ai")) { RunAIOnSeln(key) }
|
||||||
|
},
|
||||||
|
items: item_list
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// different context menu on files
|
||||||
$.contextMenu({
|
$.contextMenu({
|
||||||
selector: '.figure',
|
selector: '.figure',
|
||||||
build: function($triggerElement, e){
|
build: function($triggerElement, e){
|
||||||
@@ -474,6 +509,7 @@ $.contextMenu({
|
|||||||
{% for p in people %}
|
{% for p in people %}
|
||||||
"ai-{{p.tag}}": {"name": "{{p.tag}}"},
|
"ai-{{p.tag}}": {"name": "{{p.tag}}"},
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
"ai-all": {"name": "all"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -493,7 +529,7 @@ $.contextMenu({
|
|||||||
if( key == "move" ) { MoveDBox() }
|
if( key == "move" ) { MoveDBox() }
|
||||||
if( key == "del" ) { DelDBox('Delete') }
|
if( key == "del" ) { DelDBox('Delete') }
|
||||||
if( key == "undel" ) { DelDBox('Restore') }
|
if( key == "undel" ) { DelDBox('Restore') }
|
||||||
if( key.startsWith("ai")) { console.log( key +'was chosen')}
|
if( key.startsWith("ai")) { RunAIOnSeln(key) }
|
||||||
},
|
},
|
||||||
items: item_list
|
items: item_list
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user