another large clean up of code, all POSTs are now using make_response() and returning json OR are for a form that flask handles with rendering direct html. Where there is a POST with json response, the jscript now calls CheckForJobs() to show it in the F/E. Removed several debugs. Fixed up undocumented BUG where import datetime was wrong, and prefix/suffix also to offer directories near the date of an image. Removed unused routes for clearing messages

This commit is contained in:
2023-01-12 16:47:43 +11:00
parent 8d9cf5279e
commit ef9f26189a
12 changed files with 45 additions and 58 deletions

6
TODO
View File

@@ -1,10 +1,4 @@
### GENERAL
* should be using jsonify to return real json to my API calls, e.g:
use make_response( jsonify (... ) )
-- [TODO] all POSTs should follow new status behaviour (unless its a form POST, as that immediately renders the form in flask and will show the Status)
all GETs stay as is for now (as they expect a html reply, not data, and then html is base.html+ and it handles the status)
-- [TODO] find persons needs an array returned - test this, also viewlist
* delete files should behave like /move_files (stay on same page) as well as the status messages above
* all routes should be consistent naming conventions (with or without _ )

4
ai.py
View File

@@ -1,6 +1,6 @@
from wtforms import SubmitField, StringField, HiddenField, validators, Form
from flask_wtf import FlaskForm
from flask import request, render_template, redirect
from flask import request, render_template, redirect, make_response
from main import db, app, ma
from sqlalchemy import Sequence
from sqlalchemy.exc import SQLAlchemyError
@@ -124,4 +124,4 @@ def get_face_from_image(face_id):
img_bytearray = img_bytearray.getvalue()
face_img = base64.b64encode(img_bytearray)
face_img = str(face_img)[2:-1]
return face_img
return make_response( face_img )

View File

@@ -15,7 +15,7 @@ import numpy
import cv2
import time
import re
from datetime import datetime
from datetime import datetime, timedelta
import pytz
from flask_login import login_required, current_user
from states import States, PA_UserState
@@ -366,16 +366,6 @@ def GetEntries( OPT ):
UpdatePref( pref, OPT )
return entries
################################################################################
# /clear_jm_msg -> with a dismissable error (ie. anything not success, that is
# not showing duplicates (so rare errors) - allow them to be dismissed
################################################################################
@app.route("/clear_jm_msg/<id>", methods=["POST"])
@login_required
def clear_jm_msg(id):
ClearJM_Message(id)
return redirect( url_for("main_page") )
@app.route("/ChangeFileOpts", methods=["POST"])
@login_required
def ChangeFileOpts():
@@ -500,7 +490,7 @@ def fix_dups():
rows = db.engine.execute( "select e1.id as id1, f1.hash, d1.rel_path as rel_path1, d1.eid as did1, e1.name as fname1, p1.id as path1, p1.type_id as path_type1, e2.id as id2, d2.rel_path as rel_path2, d2.eid as did2, e2.name as fname2, p2.id as path2, p2.type_id as path_type2 from entry e1, file f1, dir d1, entry_dir_link edl1, path_dir_link pdl1, path p1, entry e2, file f2, dir d2, entry_dir_link edl2, path_dir_link pdl2, path p2 where e1.id = f1.eid and e2.id = f2.eid and d1.eid = edl1.dir_eid and edl1.entry_id = e1.id and edl2.dir_eid = d2.eid and edl2.entry_id = e2.id and p1.type_id != (select id from path_type where name = 'Bin') and p1.id = pdl1.path_id and pdl1.dir_eid = d1.eid and p2.type_id != (select id from path_type where name = 'Bin') and p2.id = pdl2.path_id and pdl2.dir_eid = d2.eid and f1.hash = f2.hash and e1.id != e2.id and f1.size_mb = f2.size_mb order by path1, rel_path1, fname1");
if rows.returns_rows == False:
SetFELog(f"Err, no dups - should now clear the FE 'danger' message?", "warning")
SetFELog(f"Err, No more duplicates? Old link followed, or something is wrong!", "warning")
return redirect("/")
if 'pagesize' not in request.form:
@@ -653,7 +643,7 @@ def viewlist():
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.orig_ptype==OPT.orig_ptype,PA_UserState.view_eid==OPT.view_eid).first()
UpdatePref( pref, OPT )
return resp
return make_response( resp )
################################################################################
# /view/id -> grabs data from DB and views it (GET)
@@ -785,7 +775,7 @@ def _jinja2_filter_parentpath(path):
###############################################################################
# route to allow the Move Dialog Box to pass a date (YYYYMMDD) and returns a
# json? list of existing dir names that could be near it in time. Starting
# json list of existing dir names that could be near it in time. Starting
# simple, by using YYYYMM-1, YYYYMM, YYYYMM+1 dirs
###############################################################################
@app.route("/getexistingpaths/<dt>", methods=["POST"])
@@ -795,13 +785,15 @@ def GetExistingPathsAsDiv(dt):
dirs_arr=[]
for delta in range(-7, 8):
try:
new_dtime=datetime.datetime.strptime(dt, "%Y%m%d") + datetime.timedelta(days=delta)
new_dtime=datetime.strptime(dt, "%Y%m%d") + timedelta(days=delta)
except:
# this is not a date, so we cant work out possible dirs, just
# return an empty set
return "[]"
return make_response( '[]' )
new_dt=new_dtime.strftime('%Y%m%d')
# find dirs named with this date
dirs_arr+=Dir.query.distinct(Dir.rel_path).filter(Dir.rel_path.ilike('%'+new_dt+'%')).all();
# find dirs with non-dirs (files) with this date
dirs_arr+=Dir.query.distinct(Dir.rel_path).join(EntryDirLink).join(Entry).filter(Entry.type_id!=dir_ft.id).filter(Entry.name.ilike('%'+new_dt+'%')).all()
# remove duplicates from array
@@ -811,12 +803,17 @@ def GetExistingPathsAsDiv(dt):
ret='[ '
first_dir=1
for dir in dirs:
print(dir)
# this can occur if there is a file with this date name in the top-levle of the path, its legit, but only really happens in DEV
# regardless, it cant be used for a existpath button in the F/E, ignore it
if dir.rel_path == '':
continue
if not first_dir:
ret +=", "
bits=dir.rel_path.split('-')
ret+= '{ '
ret+= '"prefix":"' + bits[0] + '", '
ret+= '"prefix":"' + bits[0] + '-", '
if len(bits)>1:
ret+= '"suffix":"' + bits[1] + '"'
else:
@@ -824,4 +821,4 @@ def GetExistingPathsAsDiv(dt):
ret+= ' } '
first_dir=0
ret+= ' ]'
return ret
return make_response( ret )

View File

@@ -21,14 +21,15 @@ function GetSelnAsData()
return to_del
}
// use an ajax POST to force an AI scan on the selected images
// use an ajax POST to force an AI scan on the selected image(s)
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; } })
$.ajax({ type: 'POST', data: post_data, url: '/run_ai_on', success: function(data){ CheckForJobs() } })
}
// code to change the associated image/icon when the move to selection box (import or storage paths) is changed
function change_rp_sel()
{
icon_url = $('option:selected', '#rp_sel').attr('icon_url')
@@ -37,6 +38,8 @@ function change_rp_sel()
$('#move_path_type').val( $('option:selected', '#rp_sel').attr('path_type') )
}
// POST to see if there are any other existing directories named around this date
// (if so display them as options for a move)
function GetExistingDirsAsDiv( dt, divname )
{
$.ajax({
@@ -46,7 +49,7 @@ function GetExistingDirsAsDiv( dt, divname )
dirs = JSON.parse(data)
s=''
dirs.forEach( function(item, index) {
s+= '<button class="btn btn-outline-primary" type="button" onClick="$(\'#prefix\').val(\''+item.prefix+'\'); $(\'#suffix\').val(\''+item.suffix+'\');return false;">'+item.prefix+'/'+item.suffix+'</button>'
s+= '<button class="btn btn-outline-primary" type="button" onClick="$(\'#prefix\').val(\''+item.prefix+'\'); $(\'#suffix\').val(\''+item.suffix+'\');return false;">'+item.prefix+item.suffix+'</button>'
} )
if( s == '' )
$('#existing').html('')
@@ -59,12 +62,12 @@ function GetExistingDirsAsDiv( dt, divname )
// used to remove the highlighted item(s) -- effectively removing them from view/selection so continued 'Moves' can occur
function MoveSubmit()
{
// remove the images being moved
// remove the images being moved (so UI immediately 'sees' the move)
$("[name^=eid-]").each( function() { $('#'+$(this).attr('value')).remove() } )
// reorder the images via ecnt again, so highlighting can work
// reorder the images via ecnt again, so future highlighting can work
document.mf_id=0; $('.figure').each( function() { $(this).attr('ecnt', document.mf_id ); document.mf_id++ } )
$('#dbox').modal('hide')
$.ajax({ type: 'POST', data: $('#mv_fm').serialize(), url: '/move_files', success: function(data){ CheckForJobs(); return false; } })
$.ajax({ type: 'POST', data: $('#mv_fm').serialize(), url: '/move_files', success: function(data) { CheckForJobs() } })
}
// show the DBox for a move file, includes all thumbnails of selected files to move
@@ -134,13 +137,13 @@ function DelDBox(del_or_undel)
if( del_or_undel == "Delete" )
div+=`
'/delete_files',
success: function(data){ window.location='/'; return false; } })" class="btn btn-outline-danger col-2">Ok</button>
success: function(data){ window.location='/'; CheckForJobs() } })" class="btn btn-outline-danger col-2">Ok</button>
</div>
`
else
div+=`
'/restore_files',
success: function(data){ window.location='/'; return false; } })" class="btn btn-outline-success col-2">Ok</button>
success: function(data){ window.location='/'; CheckForJobs() } })" class="btn btn-outline-success col-2">Ok</button>
</div>
`
$('#dbox-content').html(div)

View File

@@ -287,6 +287,7 @@ function OverrideForceMatch( person_id, key )
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
} )
}
@@ -320,6 +321,7 @@ function AddRefimgTo( person_id, key, search )
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
})
}
@@ -374,6 +376,7 @@ function RemoveOverrideForceMatch(face_pos)
delete objs[current].faces[face_pos].override
$('#dbox').modal('hide')
DrawImg()
CheckForJobs()
return false
}
} )
@@ -388,6 +391,7 @@ function RemoveOverrideNoMatch(face_pos, type_id)
delete objs[current].faces[face_pos].override
$('#dbox').modal('hide')
DrawImg()
CheckForJobs()
return false
}
} )
@@ -407,6 +411,7 @@ function AddNoMatchOverride(type_id, face_id, face_pos, type_id)
$('#dbox').modal('hide')
$('#faces').prop('checked',true)
DrawImg()
CheckForJobs()
}
} )
}

15
job.py
View File

@@ -297,6 +297,7 @@ def joblog_search():
for ent in ent_cursor:
jobs_cursor=db.engine.execute( f"select l.log, j.id, j.name, j.state, l.log_date from joblog l, job j where l.job_id = j.id and l.log ilike '%%{ent[0]}%%' order by l.log_date")
print("HERE")
# turn DB output into json and return it to the f/e
ret='[ '
first_job=1
@@ -312,7 +313,7 @@ def joblog_search():
ret+= '}'
first_job=0
ret+= ' ]'
return ret
return make_response( ret )
###############################################################################
@@ -331,18 +332,6 @@ def CheckForJobs():
sts.append( { 'id': msg.id, 'message': u+msg.message, 'level': msg.level, 'job_id': msg.job_id, 'persistent': msg.persistent, 'cant_close': msg.cant_close } )
return make_response( jsonify( num_active_jobs=num, sts=sts ) )
###############################################################################
# / -> POST -> looks for pa_job_manager status to F/E jobs and sends json of
# them back to F/E (called form internal/js/jobs.js:CheckForJobs()
################################################################################
@app.route("/clearmsgforjob/<id>", methods=["POST"])
@login_required
def ClearMessageForJob(id):
PA_JobManager_Message.query.filter(PA_JobManager_Message.job_id==id).delete()
db.session.commit()
# no real need for this response, as if it succeeded/failed the F/E ignores it
return make_response( jsonify( status="success" ) )
###############################################################################
# / -> POST -> looks for pa_job_manager status to F/E jobs and sends json of
# them back to F/E (called form internal/js/jobs.js:CheckForJobs()

View File

@@ -249,11 +249,12 @@ def person(id):
@app.route("/add_refimg", methods=["POST"])
@login_required
def add_refimg():
try:
# now save into the DB
person = Person.query.get(request.form['person_id']);
if not person:
raise Exception("could not find person to add reference image too!")
try:
# save the actual uploaded image to reference_images/
f=request.files['refimg_file']
fname=secure_filename(f.filename)
@@ -285,7 +286,7 @@ def find_persons(who):
resp[p.id]['firstname']=p.firstname
resp[p.id]['surname']=p.surname
return resp
return make_response( resp )
################################################################################

View File

@@ -100,7 +100,6 @@ def settings():
from job import SetFELog
s = Settings.query.one()
if 'submit' in request.form:
SetFELog("Successfully Updated Settings" )
s.import_path = request.form['import_path']
s.storage_path = request.form['storage_path']
s.recycle_bin_path = request.form['recycle_bin_path']
@@ -118,6 +117,7 @@ def settings():
s.scheduled_bin_cleanup = request.form['scheduled_bin_cleanup']
s.bin_cleanup_file_age = request.form['bin_cleanup_file_age']
s.job_archive_age = request.form['job_archive_age']
SetFELog("Successfully Updated Settings" )
db.session.commit()
return redirect( url_for( 'settings' ) )
except SQLAlchemyError as e:

View File

@@ -31,7 +31,6 @@
function ResetPageSize()
{
console.log( $("#pagesize").val() )
$("#psform").submit()
return false;
}

View File

@@ -24,8 +24,6 @@
orig_face_{{f.id}}.orig_w = {{f.face_right}}*1.05 - {{f.face_left}}*.95
orig_face_{{f.id}}.orig_h = {{f.face_bottom}}*1.05 - {{f.face_top}}*.95
//console.log( orig_face_{{f.id}} )
// when the document is ready, then DrawRefimg
$(function() { DrawUnmatchedFace( fig_{{f.id}}, im_{{f.id}}, c_{{f.id}}, orig_face_{{f.id}} ) });
</script>

View File

@@ -31,6 +31,7 @@
{% block script_content %}
<script>
<!-- browsers can put the fakepath in for security, remove it -->
function DoMagic() {
str=$("#new_file_chooser").val()
console.log(str)

View File

@@ -62,4 +62,4 @@ def changedefaults():
user.default_storage_folders = False
db.session.add(user)
db.session.commit()
return "done"
return make_response( status="success" )