diff --git a/TODO b/TODO
index e1b2630..cc08613 100644
--- a/TODO
+++ b/TODO
@@ -1,11 +1,11 @@
## GENERAL
- * create a new table file_face_refimg_link:
- file_id, face_enc, ref_img (can be null)
+ * allow rotate of image (permanently on FS, so its right everywhere)
+
+ * improve photo browser -> view file, rather than just allowing browser to show image
+
* 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
- * 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:
+ - make use of new FACE structures/links ... (code for this is in pa_job_manager, just not being called/used as yet) && something like:
FindUnknownFacesInFile()
MatchRefImgWithUnknownFace()
Then create wrapper funcs:
diff --git a/ai.py b/ai.py
index 29b3fe3..d551b9b 100644
--- a/ai.py
+++ b/ai.py
@@ -10,6 +10,9 @@ from person import Person, PersonRefimgLink
from refimg import Refimg
from flask_login import login_required, current_user
+from job import Job, JobExtra, Joblog, NewJob
+
+
# pylint: disable=no-member
################################################################################
@@ -28,3 +31,22 @@ def aistats():
last_fname = e.name
entry['people'].append( { 'tag': p.tag } )
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 Job #{job.id} to Look for face(s) in selected file(s)")
+ return render_template("base.html")
diff --git a/pa_job_manager.py b/pa_job_manager.py
index dcd1349..b601662 100644
--- a/pa_job_manager.py
+++ b/pa_job_manager.py
@@ -137,9 +137,13 @@ class Entry(Base):
in_dir = relationship ("Dir", secondary="entry_dir_link", uselist=False )
def FullPathOnFS(self):
- s=self.in_dir.in_path.path_prefix + '/'
- if len(self.in_dir.rel_path) > 0:
- s += self.in_dir.rel_path + '/'
+ if self.in_dir:
+ s=self.in_dir.in_path.path_prefix + '/'
+ 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
return s
@@ -431,6 +435,8 @@ def RunJob(job):
JobRestoreFiles(job)
elif job.name == "processai":
JobProcessAI(job)
+ elif job.name == "run_ai_on":
+ JobRunAIOn(job)
else:
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...
@@ -940,6 +946,44 @@ def JobProcessAI(job):
FinishJob(job, "Finished Processesing AI")
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):
# 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):
if DEBUG==1:
+ print( f"???? e={e}" )
print( f"DEBUG: ProcessFilesInDir: {e.FullPathOnFS()}")
if e.type.name != 'Directory':
file_func(job, e)
diff --git a/templates/files.html b/templates/files.html
index 78c78c3..2802bc4 100644
--- a/templates/files.html
+++ b/templates/files.html
@@ -222,7 +222,7 @@
{% endif %}
{# if this dir is the toplevel of the cwd, show the folder icon #}
{% if dirname| TopLevelFolderOf(cwd) %}
-
+ {{obj.name}}
@@ -270,6 +270,13 @@ function GetSelnAsData()
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)
{
to_del = GetSelnAsData()
@@ -457,6 +464,34 @@ function NoSel() {
$('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; });
$(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({
selector: '.figure',
build: function($triggerElement, e){
@@ -474,6 +509,7 @@ $.contextMenu({
{% for p in people %}
"ai-{{p.tag}}": {"name": "{{p.tag}}"},
{% endfor %}
+ "ai-all": {"name": "all"},
}
}
}
@@ -493,7 +529,7 @@ $.contextMenu({
if( key == "move" ) { MoveDBox() }
if( key == "del" ) { DelDBox('Delete') }
if( key == "undel" ) { DelDBox('Restore') }
- if( key.startsWith("ai")) { console.log( key +'was chosen')}
+ if( key.startsWith("ai")) { RunAIOnSeln(key) }
},
items: item_list
};