changing options on files_*, search, also next/prev all now use POST->redirect model, so should allow back/forward browser buttons to work - commit so we can test in PROD
This commit is contained in:
23
TODO
23
TODO
@@ -1,28 +1,25 @@
|
||||
## GENERAL
|
||||
* for below.. make Options( request )
|
||||
- remove all "Options()" that are not set by a user-choice in the F/E and just make sure new "Options" sets all defaults as needed
|
||||
-- ALSO, preferences are different to app settings... allow prefs from "ddp" menu
|
||||
-> it should allow default pagesize, default thumbsize, etc.
|
||||
-- viewlist can work out new view_eids server side, and pass them back as json data
|
||||
- can consider an optim-- new_view page makes calls to viewlist to ADD json data only, so only trigger a new "viewlist" if we dont have data for that part of the eids
|
||||
- in fact if we "re-use" a pa_user_state, we need to update it with anything that changed -- not sure but that should just happen in State()
|
||||
- use prefs in States() for defaults, rather than hardcoding "50", etc.
|
||||
- search allows folder/flat change in GUI, should disable it
|
||||
- search NOO -- no code to handle noo!!!
|
||||
|
||||
* going forward into search page (and probably all POSTs) does not work
|
||||
don't render_template instead do a redirect to a GET of the new, or list? for del...
|
||||
* need to catch "option changes" as POSTs to make them GETs somehow...
|
||||
(search, files_ip POST change trigger issue)
|
||||
-- I think it is the same URL being used issue? -- validate this in PROD only I think
|
||||
|
||||
[DONE] files.py:@app.route("/view/<id>", methods=["POST"])
|
||||
files.py:@app.route("/viewlist", methods=["POST"])
|
||||
-- this will need a total rewrite for viewer to handle fullscreen across offset/size boundaries so fix that instead
|
||||
* going forward into search page (and probably all POSTs) does not work -- use POST -> redirect to GET
|
||||
/viewlist ? -> think it works, BUT, need a rewrite anyway to use json data rather than new urls...
|
||||
job.py:@app.route("/jobs", methods=["GET", "POST"])
|
||||
job.py:@app.route("/job/<id>", methods=["GET","POST"])
|
||||
-- these need to store 'job prefs' somewhere...
|
||||
files.py:@app.route("/fix_dups", methods=["POST"])
|
||||
???
|
||||
|
||||
* optim to not run_ai_on_* for scan, needs to make sure last run_ai_on actually ran/worked - might have failed (or in my case was marked stale and I cancelled it)
|
||||
-- also the case for get file details though, need to make sure last one was completed
|
||||
|
||||
* viewlist can work out new view_eids server side, and pass them back as json data
|
||||
- can consider an optim-- new_view page makes calls to viewlist to ADD json data only, so only trigger a new "viewlist" if we dont have data for that part of the eids
|
||||
|
||||
* per file you could select an unknown face and add it as a ref img to an existing person, or make a new person and attach?
|
||||
* [DONE] order/ find face with largest size and at least show that as unmatched
|
||||
- could also try to check it vs. other faces, if it matches more than say 10? we offer it up as a required ref img, then cut that face (with margin) out and use it is a new ref image / person
|
||||
|
||||
12
ai.py
12
ai.py
@@ -94,14 +94,16 @@ def unmatched_faces():
|
||||
faces=Face.query.join(FaceFileLink).join(FaceRefimgLink, isouter=True).filter(FaceRefimgLink.refimg_id==None).order_by(Face.h.desc()).limit(10).all()
|
||||
imgs={}
|
||||
for face in faces:
|
||||
face.locn=json.loads("["+face.locn+"]")
|
||||
face.locn=json.loads(face.locn)
|
||||
f = Entry.query.join(File).join(FaceFileLink).filter(FaceFileLink.face_id==face.id).first()
|
||||
face.file_eid=f.id
|
||||
face.url=f.FullPathOnFS()
|
||||
x=face.locn[0][3]*0.95
|
||||
y=face.locn[0][0]*0.95
|
||||
x2=face.locn[0][1]*1.05
|
||||
y2=face.locn[0][2]*1.05
|
||||
x=face.locn[3]*0.95
|
||||
y=face.locn[0]*0.95
|
||||
x2=face.locn[1]*1.05
|
||||
y2=face.locn[2]*1.05
|
||||
|
||||
print( f"l={face.locn}, x='{x}'" )
|
||||
|
||||
im = Image.open(f.FullPathOnFS())
|
||||
region = im.crop((x, y, x2, y2))
|
||||
|
||||
13
files.py
13
files.py
@@ -239,6 +239,7 @@ def GetEntriesInFolderView( OPT, prefix ):
|
||||
def GetEntries( OPT ):
|
||||
entries=[]
|
||||
if OPT.path_type == 'Search' or (OPT.path_type == 'View' and OPT.orig_ptype=='Search'):
|
||||
print( f"getting entries: OPT={OPT}" )
|
||||
search_term=OPT.orig_search_term
|
||||
if 'AI:' in search_term:
|
||||
search_term = search_term.replace('AI:','')
|
||||
@@ -307,6 +308,15 @@ def clear_jm_msg(id):
|
||||
ClearJM_Message(id)
|
||||
return redirect( url_for("main_page") )
|
||||
|
||||
@app.route("/ChangeFileOpts", methods=["POST"])
|
||||
@login_required
|
||||
def ChangeFileOpts():
|
||||
# reset options based on form post, then redirect back to orig page
|
||||
OPT=States( request )
|
||||
for el in request.form:
|
||||
print( f"{el}={request.form[el]}")
|
||||
return redirect( request.referrer )
|
||||
|
||||
################################################################################
|
||||
# /file_list -> show detailed file list of files from import_path(s)
|
||||
################################################################################
|
||||
@@ -577,6 +587,8 @@ def view(id):
|
||||
entries=GetEntries( OPT )
|
||||
eids=""
|
||||
for e in entries:
|
||||
print( f"id={e.id}, len(faces)={len(e.file_details.faces)}")
|
||||
print(e.id)
|
||||
objs[e.id]=e
|
||||
eids += f"{e.id},"
|
||||
# if this is a dir, we wont view it with a click anyway, so move on...
|
||||
@@ -584,6 +596,7 @@ def view(id):
|
||||
continue
|
||||
# put locn data back into array format
|
||||
for face in e.file_details.faces:
|
||||
print( f"face.locn before json: {face.locn}" )
|
||||
face.locn = json.loads(face.locn)
|
||||
eids=eids.rstrip(",")
|
||||
return render_template("viewer.html", current=int(id), eids=eids, objs=objs, OPT=OPT )
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Define this once and before it will be called, hence at the top of this file
|
||||
function DrawRefimg(fig, img, canvas, orig_face )
|
||||
function DrawUnmatchedFace(fig, img, canvas, orig_face )
|
||||
{
|
||||
context=canvas.getContext('2d')
|
||||
// another call to this func will occur on load, so skip this one
|
||||
@@ -13,3 +13,32 @@ function DrawRefimg(fig, img, canvas, orig_face )
|
||||
context.drawImage(img, 0, 0, img.width/(img.height/canvas.height), canvas.height);
|
||||
fig.width(canvas.width)
|
||||
}
|
||||
|
||||
function DrawRefimg(fig, img, canvas, orig_face)
|
||||
{
|
||||
// FIXME: should get this from shared.py, not sure why this doesnt work at present
|
||||
thumbsize=256
|
||||
|
||||
context=canvas.getContext('2d')
|
||||
// another call to this func will occur on load, so skip this one
|
||||
if( img.width == 0 )
|
||||
return
|
||||
|
||||
// only set canvas.width once we have valid img dimensions
|
||||
canvas.width=img.width/2
|
||||
|
||||
// actually draw the pixel images to the canvas at the right size
|
||||
context.drawImage(img, 0, 0, img.width/(img.height/canvas.height), canvas.height);
|
||||
fig.width(canvas.width)
|
||||
|
||||
// draw rectangle on face
|
||||
context.beginPath();
|
||||
new_x=(orig_face.x/orig_face.orig_w)*img.width/(img.height/canvas.height)
|
||||
new_y=(orig_face.y/orig_face.orig_h)*thumbsize/(img.height/canvas.height)
|
||||
new_w=(orig_face.w/orig_face.orig_w)*img.width/(img.height/canvas.height)
|
||||
new_h=(orig_face.h/orig_face.orig_h)*thumbsize/(img.height/canvas.height)
|
||||
context.rect(new_x, new_y, new_w, new_h)
|
||||
context.lineWidth = 2;
|
||||
context.strokeStyle = 'green';
|
||||
context.stroke();
|
||||
}
|
||||
|
||||
2
main.py
2
main.py
@@ -92,7 +92,7 @@ def save_user(dn, username, data, memberships):
|
||||
# if we already have a valid user/session, and say the web has restarted, just re-use it, dont make more users
|
||||
if pau:
|
||||
return pau
|
||||
pau=PAUser(dn=dn)
|
||||
pau=PAUser(dn=dn, default_noo="Oldest", default_grouping="None", default_how_many=50, default_size=128, default_folders=True, default_fullscreen=False)
|
||||
db.session.add(pau)
|
||||
db.session.commit()
|
||||
return pau
|
||||
|
||||
@@ -211,6 +211,7 @@ def add_refimg():
|
||||
settings = Settings.query.first()
|
||||
model=AIModel.query.get(settings.default_refimg_model)
|
||||
refimg.face, face_locn = GenFace( fname, model=model.name )
|
||||
print( f"GenFace -> locn={face_locn}, locn[0][0]={face_locn[0][0]}" )
|
||||
refimg.face_locn = json.dumps(face_locn)
|
||||
refimg.model_used = settings.default_refimg_model
|
||||
refimg.created_on = time.time()
|
||||
|
||||
99
states.py
99
states.py
@@ -3,7 +3,7 @@ from flask import request, render_template, redirect, url_for
|
||||
from flask_login import login_required, current_user
|
||||
from main import db, app, ma
|
||||
from shared import PA
|
||||
|
||||
from user import PAUser
|
||||
|
||||
################################################################################
|
||||
# PA_UserState: preference data for a given user / path_type combo, so a given user
|
||||
@@ -30,9 +30,10 @@ class PA_UserState(db.Model):
|
||||
orig_ptype = db.Column(db.String, unique=False, nullable=False )
|
||||
# only used if view and orig_ptype was search
|
||||
orig_search_term = db.Column(db.String, unique=False, nullable=False )
|
||||
orig_url = db.Column(db.String, unique=False, nullable=False )
|
||||
|
||||
def __repr__(self):
|
||||
return f"<pa_user_dn: {self.pa_user_dn}, path_type: {self.path_type}, noo: {self.noo}, grouping: {self.grouping}, how_many: {self.how_many}, st_offset: {self.st_offset}, size: {self.size}, folders: {self.folders}, root: {self.root}, cwd: {self.cwd}, view_eid: {self.view_eid}, orig_ptype: {self.orig_ptype}, orig_search_term: {self.orig_search_term}>"
|
||||
return f"<pa_user_dn: {self.pa_user_dn}, path_type: {self.path_type}, noo: {self.noo}, grouping: {self.grouping}, how_many: {self.how_many}, st_offset: {self.st_offset}, size: {self.size}, folders: {self.folders}, root: {self.root}, cwd: {self.cwd}, view_eid: {self.view_eid}, orig_ptype: {self.orig_ptype}, orig_search_term: {self.orig_search_term}, orig_url: {self.orig_url}>"
|
||||
|
||||
|
||||
################################################################################
|
||||
@@ -44,52 +45,89 @@ class States(PA):
|
||||
def __init__(self, request):
|
||||
self.path_type=''
|
||||
self.url = request.path
|
||||
|
||||
# this occurs ONLY when a POST to /view/<id> occurs (at this stage orig_url will be from an import, storage, bin or search)
|
||||
if 'orig_url' in request.form:
|
||||
self.path_type='View'
|
||||
# use orig url to define defaults/look up states for 'last' import/storage/bin/search
|
||||
url = request.form['orig_url']
|
||||
# get the eid out of the url /view/<id>
|
||||
self.view_eid = request.path[6:]
|
||||
else:
|
||||
url = request.path
|
||||
self.view_eid = None
|
||||
|
||||
if 'files_ip' in url or 'file_list_ip' in url:
|
||||
print( f"States() - path={request.path}, ref={request.referrer}" )
|
||||
|
||||
# this is any next/prev or noo, grouping, etc. change (so use referrer to work out what to do with this)
|
||||
# because this can happen on a view, or files_up, etc. change this FIRST
|
||||
if 'ChangeFileOpts' in request.path:
|
||||
ref=request.referrer
|
||||
base=request.base_url
|
||||
base=base.replace("ChangeFileOpts", "")
|
||||
self.url = "/"+ref.replace(base, "" )
|
||||
print( f"started with ChangeFileOpts, so self.url now is {self.url}, bu={request.base_url}")
|
||||
|
||||
# if viewlist, then we really are a view, and view_eid should be in the form
|
||||
if 'viewlist' in request.path:
|
||||
self.path_type = 'View'
|
||||
self.view_eid = request.form['view_eid']
|
||||
self.url = request.form['orig_url']
|
||||
for el in request.form:
|
||||
print( f"{el}={request.form[el]}" )
|
||||
# this occurs ONLY when a POST to /view/<id> occurs (at this stage orig_url will be from an import, storage, bin or search)
|
||||
elif 'view' in request.path:
|
||||
self.path_type = 'View'
|
||||
self.view_eid = self.url[6:]
|
||||
# use orig url to define defaults/look up states for 'last' import/storage/bin/search
|
||||
if request.method == "POST":
|
||||
self.url = request.form['orig_url']
|
||||
else:
|
||||
# GET's occur on redirect, and we don't have a form, so get it
|
||||
# from pref
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.view_eid==self.view_eid).first()
|
||||
self.url = pref.orig_url
|
||||
print( f"view/ so change url to: '{self.url}'" )
|
||||
|
||||
print( f"NOW, url={self.url}" )
|
||||
|
||||
if 'files_ip' in self.url or 'file_list_ip' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Import'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Import'
|
||||
elif 'files_sp' in url:
|
||||
elif 'files_sp' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Storage'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Storage'
|
||||
elif 'files_rbp' in url:
|
||||
elif 'files_rbp' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Bin'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Bin'
|
||||
elif 'search' in url:
|
||||
elif 'search' in self.url:
|
||||
# okay if we are a search, but came from a view then get last_search_state form prefs and use it
|
||||
self.orig_search_term = self.url[8:]
|
||||
if self.path_type == "View":
|
||||
last_search_state = PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type=='Search').first()
|
||||
self.orig_search_term = last_search_state.orig_search_term
|
||||
print(f"view, url={self.url}")
|
||||
self.orig_ptype = 'Search'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.orig_search_term = url[8:]
|
||||
self.path_type = 'Search'
|
||||
elif 'view' in url:
|
||||
elif 'view' in self.url:
|
||||
# use url to get eid of viewed entry
|
||||
self.view_eid = self.url[6:]
|
||||
self.path_type="View"
|
||||
self.orig_url=self.url
|
||||
print( f"in view, eid={self.view_eid}, orig_url={self.orig_url}" )
|
||||
elif 'ChangeFileOpts' in self.url:
|
||||
print( f"ChangeFileOpts called, so all good?" )
|
||||
else:
|
||||
print( f"ERROR: DDP messed up, failed to match URL {url} for settings this will fail, redirecting to home" )
|
||||
print( f"referrer={request.referrer}" )
|
||||
print( f"ERROR: DDP messed up, failed to match URL {self.url} for settings this will fail, redirecting to home" )
|
||||
return
|
||||
|
||||
if self.path_type == 'View':
|
||||
print( f"its is a view, find the pref: {self.view_eid}" )
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.view_eid==self.view_eid).first()
|
||||
if not pref:
|
||||
print( f"no pref" )
|
||||
else:
|
||||
print( f"pref={pref}" )
|
||||
elif self.path_type == 'Search':
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.orig_search_term==self.orig_search_term).first()
|
||||
else:
|
||||
@@ -106,6 +144,8 @@ class States(PA):
|
||||
self.cwd=pref.cwd
|
||||
self.orig_ptype=pref.orig_ptype
|
||||
self.orig_search_term=pref.orig_search_term
|
||||
self.orig_url = pref.orig_url
|
||||
self.view_eid = pref.view_eid
|
||||
else:
|
||||
self.folders=False
|
||||
self.noo="Oldest"
|
||||
@@ -122,10 +162,13 @@ class States(PA):
|
||||
self.orig_ptype=None
|
||||
if not hasattr(self, 'orig_search_term'):
|
||||
self.orig_search_term=None
|
||||
|
||||
self.orig_url = self.url
|
||||
|
||||
# the above are defaults, if we are here, then we have current values, use them instead if they are set -- AI: searches dont set them so then we use those in the DB first
|
||||
if request.method=="POST":
|
||||
print("this was a POST, so use form vals to update PREF" )
|
||||
for el in request.form:
|
||||
print( f"{el}={request.form[el]}" )
|
||||
if 'noo' in request.form:
|
||||
self.noo=request.form['noo']
|
||||
if 'how_many' in request.form:
|
||||
@@ -146,6 +189,8 @@ class States(PA):
|
||||
# view with grouping (otherwise we print out group headings for
|
||||
# child content that is not in the CWD)
|
||||
self.grouping=None
|
||||
if 'orig_url' in request.form:
|
||||
self.orig_url = request.form['orig_url']
|
||||
|
||||
# possible to not be set for an AI: search
|
||||
if 'cwd' in request.form:
|
||||
@@ -164,7 +209,8 @@ class States(PA):
|
||||
# now save pref (if this is 'another' search, view, etc. then it will add a row for it with matching search_term, or view_eid, etc.
|
||||
if not pref:
|
||||
pref=PA_UserState( pa_user_dn=current_user.dn, path_type=self.path_type, view_eid=self.view_eid, noo=self.noo, grouping=self.grouping, how_many=self.how_many,
|
||||
st_offset=self.offset, size=self.size, folders=self.folders, root=self.root, cwd=self.cwd, orig_ptype=self.orig_ptype, orig_search_term=self.orig_search_term )
|
||||
st_offset=self.offset, size=self.size, folders=self.folders, root=self.root, cwd=self.cwd,
|
||||
orig_ptype=self.orig_ptype, orig_search_term=self.orig_search_term, orig_url=self.orig_url )
|
||||
else:
|
||||
pref.pa_user_dn=current_user.dn
|
||||
pref.path_type=self.path_type
|
||||
@@ -179,10 +225,13 @@ class States(PA):
|
||||
pref.cwd = self.cwd
|
||||
pref.orig_ptype = self.orig_ptype
|
||||
pref.orig_search_term = self.orig_search_term
|
||||
pref.orig_url = self.orig_url
|
||||
|
||||
db.session.add(pref)
|
||||
db.session.commit()
|
||||
|
||||
print( f"saved pref={pref}" )
|
||||
|
||||
return
|
||||
|
||||
################################################################################
|
||||
@@ -191,6 +240,6 @@ class States(PA):
|
||||
@app.route("/states", methods=["GET"])
|
||||
@login_required
|
||||
def states():
|
||||
user = PAUser.query.filter( PAUser.dn==current_user.dn ).one()
|
||||
states = PA_UserState.query.filter( PA_UserState.pa_user_dn==current_user.dn ).all()
|
||||
return render_template("states.html", states=states )
|
||||
|
||||
return render_template("states.html", user=user, states=states )
|
||||
|
||||
@@ -27,7 +27,7 @@ create table PA_USER(
|
||||
constraint PK_PA_USER_ID primary key(ID) );
|
||||
|
||||
-- this should reference pa_user_id not pa_user_dn
|
||||
create table PA_USER_STATE ( ID integer, PA_USER_DN varchar(128), PATH_TYPE varchar(16), NOO varchar(16), GROUPING varchar(16), HOW_MANY integer, ST_OFFSET integer, SIZE integer, FOLDERS Boolean, FULLSCREEN Boolean, ROOT varchar, CWD varchar, VIEW_EID integer, ORIG_PTYPE varchar, ORIG_SEARCH_TERM varchar,
|
||||
create table PA_USER_STATE ( ID integer, PA_USER_DN varchar(128), PATH_TYPE varchar(16), NOO varchar(16), GROUPING varchar(16), HOW_MANY integer, ST_OFFSET integer, SIZE integer, FOLDERS Boolean, FULLSCREEN Boolean, ROOT varchar, CWD varchar, VIEW_EID integer, ORIG_PTYPE varchar, ORIG_SEARCH_TERM varchar, ORIG_URL varchar,
|
||||
constraint FK_PA_USER_DN foreign key (PA_USER_DN) references PA_USER(DN),
|
||||
constraint PK_PA_USER_STATES_ID primary key(ID ) );
|
||||
|
||||
|
||||
@@ -109,8 +109,6 @@
|
||||
<input type="hidden" id="search_how_many" name="how_many" value="">
|
||||
<input type="hidden" id="search_offset" name="offset" value="">
|
||||
<input type="hidden" id="search_size" name="size" value="">
|
||||
<input type="hidden" id="search_folders" name="folders" value="">
|
||||
<input type="hidden" id="search_cwd" name="cwd" value="">
|
||||
<input id="search_term" class="form-control" type="search" placeholder="by file, date (YYYMMDD) or tag" aria-label="Search" name="search_term">
|
||||
<button class="btn btn-outline-success" onClick="javascript:st=$('#search_term').val(); document.location.href='/search/'+st" type="button">Search</button>
|
||||
</form>
|
||||
@@ -119,7 +117,7 @@
|
||||
<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#user"/></svg>
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarDropdownMenuLink">
|
||||
<div><a class="dropdown-item" href="{{url_for('states')}}">{{current_user|Username}}</a></div>
|
||||
<div><a class="dropdown-item" href="{{url_for('states')}}">{{current_user.dn|Username}}</a></div>
|
||||
<div><a class="dropdown-item" href="{{url_for('logout')}}">Logout</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,17 +18,17 @@
|
||||
fig_{{f.id}}=$('#fig_{{f.id}}')
|
||||
// store this stuff in an javascript Object to use when document is ready event is triggered
|
||||
var orig_face_{{f.id}}=new Object;
|
||||
orig_face_{{f.id}}.x = (({{f.locn[0][1]}}*1.05 - {{f.locn[0][3]}}*.95) - {{f.w}}) / 2
|
||||
orig_face_{{f.id}}.y = (({{f.locn[0][2]}}*1.05 - {{f.locn[0][0]}}*.95) - {{f.h}}) / 2
|
||||
orig_face_{{f.id}}.x = (({{f.locn[1]}}*1.05 - {{f.locn[3]}}*.95) - {{f.w}}) / 2
|
||||
orig_face_{{f.id}}.y = (({{f.locn[2]}}*1.05 - {{f.locn[0]}}*.95) - {{f.h}}) / 2
|
||||
orig_face_{{f.id}}.w = {{f.w}}
|
||||
orig_face_{{f.id}}.h = {{f.h}}
|
||||
orig_face_{{f.id}}.orig_w = {{f.locn[0][1]}}*1.05 - {{f.locn[0][3]}}*.95
|
||||
orig_face_{{f.id}}.orig_h = {{f.locn[0][2]}}*1.05 - {{f.locn[0][0]}}*.95
|
||||
orig_face_{{f.id}}.orig_w = {{f.locn[1]}}*1.05 - {{f.locn[3]}}*.95
|
||||
orig_face_{{f.id}}.orig_h = {{f.locn[2]}}*1.05 - {{f.locn[0]}}*.95
|
||||
|
||||
console.log( orig_face_{{f.id}} )
|
||||
|
||||
// when the document is ready, then DrawRefimg
|
||||
$(function() { DrawRefimg( fig_{{f.id}}, im_{{f.id}}, c_{{f.id}}, orig_face_{{f.id}} ) });
|
||||
$(function() { DrawUnmatchedFace( fig_{{f.id}}, im_{{f.id}}, c_{{f.id}}, orig_face_{{f.id}} ) });
|
||||
</script>
|
||||
<figcaption>Face #{{f.id}}</figcation>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</script>
|
||||
|
||||
<div class="container-fluid">
|
||||
<form id="main_form" method="POST">
|
||||
<form id="main_form" method="POST" action="/ChangeFileOpts">
|
||||
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
||||
{% if search_term is defined %}
|
||||
<input type="hidden" name="search_term" id="view_term" value="{{search_term}}">
|
||||
@@ -83,36 +83,36 @@
|
||||
</div>
|
||||
<div class="d-flex col col-auto justify-content-end">
|
||||
<div class="btn-group">
|
||||
{% if OPT.size == "64" %}
|
||||
{% if OPT.size == 64 %}
|
||||
{% set bt="btn-info text-white" %}
|
||||
{% else %}
|
||||
{% set bt="btn-outline-info" %}
|
||||
{% endif %}
|
||||
<button aria-label="extra small" id="64" class="px-2 sm-txt sz-but btn {{bt}}" onClick="ChangeSize(this,64); return false;">XS</button>
|
||||
{% if OPT.size == "96" %}
|
||||
<button aria-label="extra small" id="64" class="px-2 sm-txt sz-but btn {{bt}}" onClick="$('#size').val(64)">XS</button>
|
||||
{% if OPT.size == 96 %}
|
||||
{% set bt="btn-info text-white" %}
|
||||
{% else %}
|
||||
{% set bt="btn-outline-info" %}
|
||||
{% endif %}
|
||||
<button aria-label="small" id="96" class="px-2 sm-txt sz-but btn {{bt}}" onClick="ChangeSize(this,96); return false;">S</button>
|
||||
{% if OPT.size == "128" %}
|
||||
<button aria-label="small" id="96" class="px-2 sm-txt sz-but btn {{bt}}" onClick="$('#size').val(96)">S</button>
|
||||
{% if OPT.size == 128 %}
|
||||
{% set bt="btn-info text-white" %}
|
||||
{% else %}
|
||||
{% set bt="btn-outline-info" %}
|
||||
{% endif %}
|
||||
<button aria-label="medium" id="128" class="px-2 sm-txt sz-but btn {{bt}}" onClick="ChangeSize(this,128); return false;">M</button>
|
||||
{% if OPT.size == "192" %}
|
||||
<button aria-label="medium" id="128" class="px-2 sm-txt sz-but btn {{bt}}" onClick="$('#size').val(128)">M</button>
|
||||
{% if OPT.size == 192 %}
|
||||
{% set bt="btn-info text-white" %}
|
||||
{% else %}
|
||||
{% set bt="btn-outline-info" %}
|
||||
{% endif %}
|
||||
<button aria-label="large" id="192" class="px-2 sm-txt sz-but btn {{bt}}" onClick="ChangeSize(this,192); return false;">L</button>
|
||||
{% if OPT.size == "256" %}
|
||||
<button aria-label="large" id="192" class="px-2 sm-txt sz-but btn {{bt}}" onClick="$('#size').val(192)">L</button>
|
||||
{% if OPT.size == 256 %}
|
||||
{% set bt="btn-info text-white" %}
|
||||
{% else %}
|
||||
{% set bt="btn-outline-info" %}
|
||||
{% endif %}
|
||||
<button aria-label="extra large" id="256" class="px-2 sm-txt sz-but btn {{bt}}" onClick="ChangeSize(this,256); return false;">XL</button>
|
||||
<button aria-label="extra large" id="256" class="px-2 sm-txt sz-but btn {{bt}}" onClick="$('#size').val(256)">XL</button>
|
||||
</div class="btn-group">
|
||||
</div class="col">
|
||||
<input id="offset" type="hidden" name="offset" value="{{OPT.offset}}">
|
||||
@@ -250,7 +250,7 @@
|
||||
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<form id="nav_form" method="POST"">
|
||||
<form id="nav_form" method="POST" action="/ChangeFileOpts">
|
||||
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
||||
<div class="row">
|
||||
<div class="col my-auto d-flex justify-content-center">
|
||||
@@ -282,15 +282,12 @@ function CallViewRoute(id)
|
||||
{
|
||||
s='<form id="_fm" method="POST" action="/view/' + id + '">'
|
||||
s+='<input type="hidden" name="eids" value="'+$("#eids").val() + '">'
|
||||
s+='<input type="hidden" name="noo" value="{{OPT.noo}}">'
|
||||
s+='<input type="hidden" name="cwd" value="{{OPT.cwd}}">'
|
||||
s+='<input type="hidden" name="root" value="{{OPT.root}}">'
|
||||
s+='<input type="hidden" name="size" value="{{OPT.size}}">'
|
||||
s+='<input type="hidden" name="grouping" value="{{OPT.grouping}}">'
|
||||
s+='<input type="hidden" name="offset" value="{{OPT.offset}}">'
|
||||
s+='<input type="hidden" name="folders" value="{{OPT.folders}}">'
|
||||
s+='<input type="hidden" name="how_many" value="{{OPT.how_many}}">'
|
||||
s+='<input type="hidden" name="orig_url" value="{{request.path}}">'
|
||||
s+='<input type="hidden" name="view_eid" value="'+id+'">'
|
||||
{% if search_term is defined %}
|
||||
s+='<input type="hidden" name="search_term" value="{{search_term}}">'
|
||||
{% endif %}
|
||||
|
||||
@@ -1,12 +1,54 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block main_content %}
|
||||
<h3>PA User state page</h3>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<div class="row">
|
||||
<alert class="alert alert-warning">The following values are based on navigating the application and are not set by hand. This page is for checking/debugging only.</alert>
|
||||
<h3>Defaults for {{user.dn|Username}}</h3>
|
||||
<div class="col-6">
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">When viewing show in fullscreen:</label>
|
||||
{{CreateSelect( "size", user.default_size, ["XS", "S", "M", "L", "XL"], "ChangeDefault();return false", "col-2 rounded-end", { 0:64, 1:96, 2:128, 3:192, 4:256 } )|safe }}
|
||||
</div>
|
||||
</div class="row">
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">Ordered by:</label>
|
||||
{{CreateSelect( "noo", user.default_noo, ["Oldest", "Newest","A to Z", "Z to A"], "ChangeDefault();return false", "col-2 rounded-end")|safe }}
|
||||
</div class="input-group">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">How many thumbnails to show at once:</label>
|
||||
{{CreateSelect( "how_many", user.default_how_many, [10, 25, 50, 75, 100, 150, 200, 500], "ChangeDefault();return false", "col-2 rounded-end")|safe }}
|
||||
</div class="input-group">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">Group by:</label>
|
||||
{{CreateSelect( "grouping", user.default_grouping, ["None", "Day", "Week", "Month"], "ChangeDefault();return false", "col-2 rounded-end")|safe }}
|
||||
</div class="input-group">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">Folders or Flat view of thumbnails:</label>
|
||||
{{CreateSelect( "folders", user.default_folders, ["In Folder", "Flat View"], "ChangeDefault();return false", "col-2 rounded-end", { 0:true, 1:false } )|safe }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-group">
|
||||
<label class="py-1 input-group-text col-6 justify-content-end">When viewing show in fullscreen:</label>
|
||||
{{CreateSelect( "fullscreen", user.default_fullscreen, [True,False], "ChangeDefault();return false", "col-2 rounded-end")|safe }}
|
||||
</div>
|
||||
</div>
|
||||
</div class="col-6">
|
||||
|
||||
<div class="row pt-5">
|
||||
<alert class="alert alert-warning">The following values are based on the defaults above and subsequent changes as you navigate the application and are not set by hand. The following content is for checking/debugging only.</alert>
|
||||
</div class="row">
|
||||
|
||||
<div class="row">
|
||||
<table id="pa_user_state_tbl" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
|
||||
<thead>
|
||||
@@ -43,3 +85,19 @@
|
||||
</div class="row">
|
||||
</div class="container-fluid">
|
||||
{% endblock main_content %}
|
||||
{% block script_content %}
|
||||
<script>
|
||||
function ChangeDefault()
|
||||
{
|
||||
data="dn={{user.dn}}"
|
||||
data+="&default_size="+$('#size').val()
|
||||
data+="&default_noo="+$('#noo').val()
|
||||
data+="&default_how_many="+$('#how_many').val()
|
||||
data+="&default_grouping="+$('#grouping').val()
|
||||
data+="&default_folders="+$('#folders').val()
|
||||
data+="&default_fullscreen="+$('#fullscreen').val()
|
||||
console.log("Changing Default:"+data )
|
||||
$.ajax({ type: 'POST', data: data, url: '/changedefaults', success: function(data){ window.location='/states'; return false; } })
|
||||
}
|
||||
</script>
|
||||
{% endblock script_content %}
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
s+='<input type="hidden" name="folders" value="{{OPT.folders}}">'
|
||||
s+='<input type="hidden" name="how_many" value="{{OPT.how_many}}">'
|
||||
s+='<input type="hidden" name="orig_url" value="{{OPT.orig_url}}">'
|
||||
s+='<input type="hidden" name="view_eid" value="{{OPT.view_eid}}">'
|
||||
s+='<input type="hidden" name="fullscreen" value="' + fullscreen + '">'
|
||||
s+='<input type="hidden" name="' + dir + '" value="1">'
|
||||
{% if search_term is defined %}
|
||||
|
||||
46
user.py
46
user.py
@@ -1,9 +1,12 @@
|
||||
from main import db
|
||||
from sqlalchemy import Sequence
|
||||
from flask_login import UserMixin
|
||||
from flask import request, redirect
|
||||
from flask_login import UserMixin, login_required
|
||||
from main import db, app, ma
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from status import st, Status
|
||||
|
||||
|
||||
# pylint: disable=no-member
|
||||
|
||||
################################################################################
|
||||
@@ -17,9 +20,48 @@ class PAUser(UserMixin,db.Model):
|
||||
__tablename__ = "pa_user"
|
||||
id = db.Column(db.Integer, db.Sequence('pa_user_id_seq'), primary_key=True)
|
||||
dn = db.Column(db.String)
|
||||
default_noo = db.Column(db.String)
|
||||
default_grouping = db.Column(db.String)
|
||||
default_how_many = db.Column(db.Integer)
|
||||
default_size = db.Column(db.Integer)
|
||||
default_folders = db.Column(db.Boolean)
|
||||
default_fullscreen = db.Column(db.Boolean)
|
||||
|
||||
def __repr__(self):
|
||||
return self.dn
|
||||
str=f"<{self.__class__.__name__}("
|
||||
for k, v in self.__dict__.items():
|
||||
str += f"{k}={v!r}, "
|
||||
str=str.rstrip(", ") + ")>"
|
||||
return str
|
||||
|
||||
def get_id(self):
|
||||
return self.dn
|
||||
|
||||
################################################################################
|
||||
# /changedefaults -> POST only -> changes defaults in DB, states.html reloads
|
||||
# /states once its successful
|
||||
################################################################################
|
||||
@app.route("/changedefaults", methods=["POST"])
|
||||
@login_required
|
||||
def changedefaults():
|
||||
print( "change defaults -- need to sanitize form!?" )
|
||||
for el in request.form:
|
||||
print( f"{el}={request.form[el]}" )
|
||||
user=PAUser.query.filter(PAUser.dn==request.form['dn']).one()
|
||||
user.default_size = request.form['default_size']
|
||||
user.default_noo = request.form['default_noo']
|
||||
user.default_how_many = request.form['default_how_many']
|
||||
user.default_grouping = request.form['default_grouping']
|
||||
if request.form['default_folders'] == 'True':
|
||||
user.default_folders = True
|
||||
else:
|
||||
user.default_folders = False
|
||||
print("fullscreen is failing?")
|
||||
if request.form['default_fullscreen'] == 'True':
|
||||
user.default_fullscreen = True
|
||||
else:
|
||||
user.default_fullscreen = False
|
||||
print( user )
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return "done"
|
||||
|
||||
Reference in New Issue
Block a user