unmatched faces now sorts size of face desc, and is slightly prettier -- still slow and only takes you to the file anyway, might optimise that later. still no code to auto deal with unmatched face, but will add some capabilities next. Also, remembered last dir when file_ip/sp/bin. Also throw error if try to find unknown person - happened since I allowed the back/forward.
This commit is contained in:
29
TODO
29
TODO
@@ -1,19 +1,17 @@
|
|||||||
## GENERAL
|
## GENERAL
|
||||||
*** Need to double-check scheduled jobs running in PROD (can use new pa_job_manager.log)
|
* optimise run_ai_on (and for that matter getfiledetails, etc.)...
|
||||||
|
- e.g. with last scan*, STORE: new files?
|
||||||
* remember last import dir, so you can just go straight back to it
|
- if last scan no new files, dont getfiledetails, don't re-run ai job
|
||||||
|
|
||||||
* when hitting back button to a search, it doesnt handle the post, etc.
|
|
||||||
$(document).ready(function() {
|
|
||||||
window.onpopstate = function() {
|
|
||||||
# this seems to work, but feels like no protection at all???
|
|
||||||
# (what about back when it goes onto a POST of deleting a file!)
|
|
||||||
window.history.back()
|
|
||||||
};
|
|
||||||
});
|
|
||||||
-- maybe window.history.replace() is needed on unsafe URLs?
|
|
||||||
|
|
||||||
* 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?
|
* 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
|
||||||
|
* on viewer: allow face to be used to create person, add to existing person, and allow 'ignore', mark as 'not a face', etc. -> all into DB
|
||||||
|
- so need face 'treatment' -> could be matched via face_refimg_link, but also could be 'ignore' or 'not a face', in each case we could exclude those faces from
|
||||||
|
matching for the future, and reporting on matches, etc.
|
||||||
|
- context-menu with rects on a canvas
|
||||||
|
https://stackoverflow.com/questions/31601393/create-context-menu-using-jquery-with-html-5-canvas
|
||||||
|
- also allow joblog search from the viewer for that file...
|
||||||
|
|
||||||
* delete folder
|
* delete folder
|
||||||
|
|
||||||
@@ -46,14 +44,15 @@
|
|||||||
|
|
||||||
* comment your code -> only html files remaining
|
* comment your code -> only html files remaining
|
||||||
|
|
||||||
* from menu, we could try to get smart/fancy... say find face with largest size, 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
|
* read that guys face matching / clustering / nearest neighbour examples, for a whole new AI capability
|
||||||
- read that guys face matching / clustering / nearest neighbour examples, for a whole new AI capability
|
|
||||||
https://www.pyimagesearch.com/2018/07/09/face-clustering-with-python/
|
https://www.pyimagesearch.com/2018/07/09/face-clustering-with-python/
|
||||||
|
|
||||||
* fix up logging in general
|
* fix up logging in general
|
||||||
|
|
||||||
* support animated gifs in html5 canvas
|
* support animated gifs in html5 canvas
|
||||||
|
|
||||||
|
* think about security - in job_mgr anywhere I can os.replace/remove NEED to protect, etc
|
||||||
|
|
||||||
## DB
|
## DB
|
||||||
* Dir can have date in the DB, so we can do Oldest/Newest dirs in Folder view
|
* Dir can have date in the DB, so we can do Oldest/Newest dirs in Folder view
|
||||||
|
|
||||||
|
|||||||
3
ai.py
3
ai.py
@@ -91,7 +91,7 @@ def run_ai_on_storage():
|
|||||||
@app.route("/unmatched_faces")
|
@app.route("/unmatched_faces")
|
||||||
@login_required
|
@login_required
|
||||||
def unmatched_faces():
|
def unmatched_faces():
|
||||||
faces=Face.query.join(FaceFileLink).join(FaceRefimgLink, isouter=True).filter(FaceRefimgLink.refimg_id==None).limit(10).all()
|
faces=Face.query.join(FaceFileLink).join(FaceRefimgLink, isouter=True).filter(FaceRefimgLink.refimg_id==None).order_by(Face.h.desc()).limit(10).all()
|
||||||
imgs={}
|
imgs={}
|
||||||
for face in faces:
|
for face in faces:
|
||||||
face.locn=json.loads("["+face.locn+"]")
|
face.locn=json.loads("["+face.locn+"]")
|
||||||
@@ -102,6 +102,7 @@ def unmatched_faces():
|
|||||||
y=face.locn[0][0]*0.95
|
y=face.locn[0][0]*0.95
|
||||||
x2=face.locn[0][1]*1.05
|
x2=face.locn[0][1]*1.05
|
||||||
y2=face.locn[0][2]*1.05
|
y2=face.locn[0][2]*1.05
|
||||||
|
|
||||||
im = Image.open(f.FullPathOnFS())
|
im = Image.open(f.FullPathOnFS())
|
||||||
region = im.crop((x, y, x2, y2))
|
region = im.crop((x, y, x2, y2))
|
||||||
img_bytearray = io.BytesIO()
|
img_bytearray = io.BytesIO()
|
||||||
|
|||||||
9
face.py
9
face.py
@@ -2,6 +2,12 @@ from main import db, app, ma
|
|||||||
from sqlalchemy import Sequence
|
from sqlalchemy import Sequence
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
# DEL ME SOON
|
||||||
|
from flask_login import login_required
|
||||||
|
from flask import render_template
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -17,6 +23,8 @@ class Face(db.Model):
|
|||||||
id = db.Column(db.Integer, db.Sequence('face_id_seq'), primary_key=True )
|
id = db.Column(db.Integer, db.Sequence('face_id_seq'), primary_key=True )
|
||||||
face = db.Column( db.LargeBinary )
|
face = db.Column( db.LargeBinary )
|
||||||
locn = db.Column( db.String )
|
locn = db.Column( db.String )
|
||||||
|
w = db.Column( db.Integer )
|
||||||
|
h = db.Column( db.Integer )
|
||||||
refimg_lnk = db.relationship("FaceRefimgLink", uselist=False, viewonly=True)
|
refimg_lnk = db.relationship("FaceRefimgLink", uselist=False, viewonly=True)
|
||||||
facefile_lnk = db.relationship("FaceFileLink", uselist=False, viewonly=True)
|
facefile_lnk = db.relationship("FaceFileLink", uselist=False, viewonly=True)
|
||||||
refimg =db.relationship("Refimg", secondary="face_refimg_link", uselist=False)
|
refimg =db.relationship("Refimg", secondary="face_refimg_link", uselist=False)
|
||||||
@@ -53,4 +61,3 @@ class FaceRefimgLink(db.Model):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}, face_distance: {self.face_distance}"
|
return f"<face_id: {self.face_id}, refimg_id={self.refimg_id}, face_distance: {self.face_distance}"
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,15 @@
|
|||||||
// Define this once and before it will be called, hence at the top of this file
|
// Define this once and before it will be called, hence at the top of this file
|
||||||
function DrawRefimg(fig, img, canvas, orig_face )
|
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')
|
context=canvas.getContext('2d')
|
||||||
// another call to this func will occur on load, so skip this one
|
// another call to this func will occur on load, so skip this one
|
||||||
if( img.width == 0 )
|
if( img.width == 0 )
|
||||||
return
|
return
|
||||||
|
|
||||||
// only set canvas.width once we have valid img dimensions
|
// only set canvas.width once we have valid img dimensions
|
||||||
canvas.width=img.width/2
|
canvas.width=img.width/(img.height/canvas.height)
|
||||||
|
|
||||||
// actually draw the pixel images to the canvas at the right size
|
// 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);
|
context.drawImage(img, 0, 0, img.width/(img.height/canvas.height), canvas.height);
|
||||||
fig.width(canvas.width)
|
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();
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
options.py
16
options.py
@@ -21,9 +21,11 @@ class PA_PREF(db.Model):
|
|||||||
size = db.Column(db.Integer, unique=False, nullable=False )
|
size = db.Column(db.Integer, unique=False, nullable=False )
|
||||||
folders = db.Column(db.Boolean, unique=False, nullable=False )
|
folders = db.Column(db.Boolean, unique=False, nullable=False )
|
||||||
fullscreen = db.Column(db.Boolean, unique=False, nullable=False )
|
fullscreen = db.Column(db.Boolean, unique=False, nullable=False )
|
||||||
|
root = db.Column(db.String, unique=False, nullable=False )
|
||||||
|
cwd = db.Column(db.String, unique=False, nullable=False )
|
||||||
|
|
||||||
def __repr__(self):
|
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}>"
|
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}>"
|
||||||
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -85,14 +87,16 @@ class Options(PA):
|
|||||||
self.how_many=pref.how_many
|
self.how_many=pref.how_many
|
||||||
self.offset=pref.st_offset
|
self.offset=pref.st_offset
|
||||||
self.size=pref.size
|
self.size=pref.size
|
||||||
|
self.root=pref.root
|
||||||
|
self.cwd=pref.cwd
|
||||||
else:
|
else:
|
||||||
self.grouping="None"
|
self.grouping="None"
|
||||||
self.how_many="50"
|
self.how_many="50"
|
||||||
self.offset="0"
|
self.offset="0"
|
||||||
self.size="128"
|
self.size="128"
|
||||||
|
self.root='static/' + self.path_type
|
||||||
|
self.cwd=self.root
|
||||||
|
|
||||||
self.cwd='static/' + self.path_type
|
|
||||||
self.root=self.cwd
|
|
||||||
|
|
||||||
# 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
|
# 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":
|
if request.method=="POST":
|
||||||
@@ -134,7 +138,7 @@ class Options(PA):
|
|||||||
pref=PA_PREF.query.filter(PA_PREF.pa_user_dn==current_user.dn,PA_PREF.path_type==self.path_type).first()
|
pref=PA_PREF.query.filter(PA_PREF.pa_user_dn==current_user.dn,PA_PREF.path_type==self.path_type).first()
|
||||||
if not pref:
|
if not pref:
|
||||||
pref=PA_PREF( pa_user_dn=current_user.dn, path_type=self.path_type, noo=self.noo, grouping=self.grouping, how_many=self.how_many,
|
pref=PA_PREF( pa_user_dn=current_user.dn, path_type=self.path_type, noo=self.noo, grouping=self.grouping, how_many=self.how_many,
|
||||||
st_offset=self.offset, size=self.size, folders=self.folders)
|
st_offset=self.offset, size=self.size, folders=self.folders, root=self.root, cwd=self.cwd)
|
||||||
else:
|
else:
|
||||||
pref.noo=self.noo
|
pref.noo=self.noo
|
||||||
pref.grouping=self.grouping
|
pref.grouping=self.grouping
|
||||||
@@ -142,10 +146,14 @@ class Options(PA):
|
|||||||
pref.st_offset=self.offset
|
pref.st_offset=self.offset
|
||||||
pref.size=self.size
|
pref.size=self.size
|
||||||
pref.folders=self.folders
|
pref.folders=self.folders
|
||||||
|
pref.root = self.root
|
||||||
|
pref.cwd = self.cwd
|
||||||
|
|
||||||
db.session.add(pref)
|
db.session.add(pref)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /prefs -> GET only -> prints out list of all prefs (simple for now)
|
# /prefs -> GET only -> prints out list of all prefs (simple for now)
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|||||||
@@ -320,6 +320,8 @@ class Face(Base):
|
|||||||
id = Column(Integer, Sequence('face_id_seq'), primary_key=True )
|
id = Column(Integer, Sequence('face_id_seq'), primary_key=True )
|
||||||
face = Column( LargeBinary )
|
face = Column( LargeBinary )
|
||||||
locn = Column(String)
|
locn = Column(String)
|
||||||
|
w = Column(Integer)
|
||||||
|
h = Column(Integer)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<id: {self.id}, face={self.face}"
|
return f"<id: {self.id}, face={self.face}"
|
||||||
@@ -1848,7 +1850,9 @@ def InitialValidationChecks():
|
|||||||
# AddFaceToFile(): adds the specified face, location & model_used to the specified file
|
# AddFaceToFile(): adds the specified face, location & model_used to the specified file
|
||||||
####################################################################################################################################
|
####################################################################################################################################
|
||||||
def AddFaceToFile( locn_data, face_data, file_eid, model_id ):
|
def AddFaceToFile( locn_data, face_data, file_eid, model_id ):
|
||||||
face = Face( face=face_data.tobytes(), locn=json.dumps(locn_data) )
|
w = locn_data[1] - locn_data[3]
|
||||||
|
h = locn_data[2] - locn_data[0]
|
||||||
|
face = Face( face=face_data.tobytes(), locn=json.dumps(locn_data), w=w, h=h )
|
||||||
session.add(face)
|
session.add(face)
|
||||||
session.commit()
|
session.commit()
|
||||||
ffl = FaceFileLink( face_id=face.id, file_eid=file_eid, model_used=model_id )
|
ffl = FaceFileLink( face_id=face.id, file_eid=file_eid, model_used=model_id )
|
||||||
|
|||||||
@@ -178,6 +178,10 @@ def person(id):
|
|||||||
return render_template("person.html", form=form, page_title=page_title)
|
return render_template("person.html", form=form, page_title=page_title)
|
||||||
else:
|
else:
|
||||||
person = Person.query.get(id)
|
person = Person.query.get(id)
|
||||||
|
if not person:
|
||||||
|
st.SetMessage( f"No such person with id: {id}", "danger" )
|
||||||
|
return render_template("base.html" )
|
||||||
|
|
||||||
for r in person.refimg:
|
for r in person.refimg:
|
||||||
r.face_locn=json.loads(r.face_locn)
|
r.face_locn=json.loads(r.face_locn)
|
||||||
form = PersonForm(request.values, obj=person)
|
form = PersonForm(request.values, obj=person)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ create table SETTINGS(
|
|||||||
constraint FK_DEFAULT_REFIMG_MODEL foreign key (DEFAULT_REFIMG_MODEL) references AI_MODEL(ID),
|
constraint FK_DEFAULT_REFIMG_MODEL foreign key (DEFAULT_REFIMG_MODEL) references AI_MODEL(ID),
|
||||||
constraint FK_DEFAULT_SCAN_MODEL foreign key (DEFAULT_SCAN_MODEL) references AI_MODEL(ID) );
|
constraint FK_DEFAULT_SCAN_MODEL foreign key (DEFAULT_SCAN_MODEL) references AI_MODEL(ID) );
|
||||||
|
|
||||||
create table PA_PREF ( 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,
|
create table PA_PREF ( 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,
|
||||||
constraint PK_PA_USER_DN_PATH_TYPE primary key(PA_USER_DN, PATH_TYPE ) );
|
constraint PK_PA_USER_DN_PATH_TYPE primary key(PA_USER_DN, PATH_TYPE ) );
|
||||||
|
|
||||||
create table PA_USER( ID integer, dn varchar,
|
create table PA_USER( ID integer, dn varchar,
|
||||||
@@ -61,7 +61,7 @@ create table REFIMG ( ID integer, FNAME varchar(128), FACE bytea, ORIG_W integer
|
|||||||
constraint PK_REFIMG_ID primary key(ID),
|
constraint PK_REFIMG_ID primary key(ID),
|
||||||
constraint FK_REFIMG_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) );
|
constraint FK_REFIMG_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) );
|
||||||
|
|
||||||
create table FACE( ID integer, FACE bytea, LOCN varchar(32), constraint PK_FACE_ID primary key(ID) );
|
create table FACE( ID integer, FACE bytea, LOCN varchar(32), W integer, H integer, constraint PK_FACE_ID primary key(ID) );
|
||||||
|
|
||||||
create table FACE_FILE_LINK( FACE_ID integer, FILE_EID integer, MODEL_USED integer,
|
create table FACE_FILE_LINK( FACE_ID integer, FILE_EID integer, MODEL_USED integer,
|
||||||
constraint PK_FFL_FACE_ID_FILE_ID primary key(FACE_ID, FILE_EID),
|
constraint PK_FFL_FACE_ID_FILE_ID primary key(FACE_ID, FILE_EID),
|
||||||
|
|||||||
@@ -163,6 +163,10 @@
|
|||||||
|
|
||||||
{% if not InDBox %}
|
{% if not InDBox %}
|
||||||
<script>
|
<script>
|
||||||
|
// do our own back button handling, TODO: 'dangerous' URLs, will be replaced with GETs first via history.replaceState()
|
||||||
|
window.onpopstate = function(e) {
|
||||||
|
window.history.back()
|
||||||
|
}
|
||||||
function SetViewingOptionsForSearchForm()
|
function SetViewingOptionsForSearchForm()
|
||||||
{
|
{
|
||||||
if( $('#noo').length )
|
if( $('#noo').length )
|
||||||
|
|||||||
@@ -6,20 +6,10 @@
|
|||||||
<h3>Unmatched Faces</h3>
|
<h3>Unmatched Faces</h3>
|
||||||
<div class="row mt-3">
|
<div class="row mt-3">
|
||||||
{% for f in faces %}
|
{% for f in faces %}
|
||||||
<div id="F{{f.id}}" class="col-2 px-0">
|
<div id="F{{f.id}}" class="col col-auto mt-3 pr-1">
|
||||||
<form id="_fm" method="POST" action="/view/{{f.file_eid}}">
|
<form id="_fm" method="POST" action="/view/{{f.file_eid}}">
|
||||||
<input type="hidden" name="eids" value="{{f.file_eid}},">
|
<input type="hidden" name="eids" value="{{f.file_eid}},">
|
||||||
<input type="hidden" name="noo" value="newest">
|
<figure id="fig_{{f.id}}" class="mb-0">
|
||||||
<input type="hidden" name="cwd" value="/">
|
|
||||||
<input type="hidden" name="root" value="/">
|
|
||||||
<input type="hidden" name="size" value="128">
|
|
||||||
<input type="hidden" name="grouping" value="1">
|
|
||||||
<input type="hidden" name="offset" value="0">
|
|
||||||
<input type="hidden" name="folders" value="false">
|
|
||||||
<input type="hidden" name="how_many" value="1">
|
|
||||||
<input type="hidden" name="orig_url" value="{{request.path}}">'
|
|
||||||
|
|
||||||
<figure id="fig_{{f.id}}">
|
|
||||||
<div style="position:relative">
|
<div style="position:relative">
|
||||||
<canvas id="c_{{f.id}}" height="128"></canvas>
|
<canvas id="c_{{f.id}}" height="128"></canvas>
|
||||||
<script>
|
<script>
|
||||||
@@ -28,20 +18,22 @@
|
|||||||
fig_{{f.id}}=$('#fig_{{f.id}}')
|
fig_{{f.id}}=$('#fig_{{f.id}}')
|
||||||
// store this stuff in an javascript Object to use when document is ready event is triggered
|
// store this stuff in an javascript Object to use when document is ready event is triggered
|
||||||
var orig_face_{{f.id}}=new Object;
|
var orig_face_{{f.id}}=new Object;
|
||||||
orig_face_{{f.id}}.x = {{f.locn[0][3]}}
|
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][0]}}
|
orig_face_{{f.id}}.y = (({{f.locn[0][2]}}*1.05 - {{f.locn[0][0]}}*.95) - {{f.h}}) / 2
|
||||||
orig_face_{{f.id}}.w = {{f.locn[0][1]}}-{{f.locn[0][3]}}
|
orig_face_{{f.id}}.w = {{f.w}}
|
||||||
orig_face_{{f.id}}.h = {{f.locn[0][2]}}-{{f.locn[0][0]}}
|
orig_face_{{f.id}}.h = {{f.h}}
|
||||||
orig_face_{{f.id}}.orig_w = orig_face_{{f.id}}.w
|
orig_face_{{f.id}}.orig_w = {{f.locn[0][1]}}*1.05 - {{f.locn[0][3]}}*.95
|
||||||
orig_face_{{f.id}}.orig_h = orig_face_{{f.id}}.h
|
orig_face_{{f.id}}.orig_h = {{f.locn[0][2]}}*1.05 - {{f.locn[0][0]}}*.95
|
||||||
|
|
||||||
|
console.log( orig_face_{{f.id}} )
|
||||||
|
|
||||||
// when the document is ready, then DrawRefimg
|
// when the document is ready, then DrawRefimg
|
||||||
$(function() { DrawRefimg( fig_{{f.id}}, im_{{f.id}}, c_{{f.id}}, orig_face_{{f.id}} ) });
|
$(function() { DrawRefimg( fig_{{f.id}}, im_{{f.id}}, c_{{f.id}}, orig_face_{{f.id}} ) });
|
||||||
</script>
|
</script>
|
||||||
<figcaption>{{f.id}}</figcation>
|
<figcaption>Face #{{f.id}}</figcation>
|
||||||
</div>
|
</div>
|
||||||
</figure>
|
</figure>
|
||||||
<button>Go</button>
|
<button class="btn btn-outline-info">Go</button>
|
||||||
</form>
|
</form>
|
||||||
</div id="/F*">
|
</div id="/F*">
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<table id="prefs_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
|
<table id="prefs_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="table-primary"><th>Path</th><th>New or Oldest</th><th>How Many</th><th>Folders?</th><th>Group by</th><th>Thumb size</th><th>Fullscreen</th><th>DB retrieve offset</th></tr>
|
<tr class="table-primary"><th>Path</th><th>New or Oldest</th><th>How Many</th><th>Folders?</th><th>Group by</th><th>Thumb size</th><th>Fullscreen</th><th>DB retrieve offset</th><th>Root</th><th>cwd</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for pref in prefs %}
|
{% for pref in prefs %}
|
||||||
@@ -18,6 +18,8 @@
|
|||||||
<td>{{pref.size}}</td>
|
<td>{{pref.size}}</td>
|
||||||
<td>{{pref.fullscreen}}</td>
|
<td>{{pref.fullscreen}}</td>
|
||||||
<td>{{pref.st_offset}}</td>
|
<td>{{pref.st_offset}}</td>
|
||||||
|
<td>{{pref.root}}</td>
|
||||||
|
<td>{{pref.cwd}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
Reference in New Issue
Block a user