Merge branch 'master' of mara.ddp.net:photoassistant
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,4 +1,6 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
photos/
|
photos/
|
||||||
images_to_process/
|
images_to_process/
|
||||||
|
new_img_dir/
|
||||||
static/*
|
static/*
|
||||||
|
reference_images/*
|
||||||
|
|||||||
15
ai.py
Normal file
15
ai.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from wtforms import SubmitField, StringField, HiddenField, validators, Form
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask import request, render_template, redirect
|
||||||
|
from main import db, app, ma
|
||||||
|
from sqlalchemy import Sequence
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from status import st, Status
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /refimg/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
|
||||||
|
# refimg
|
||||||
|
################################################################################
|
||||||
|
@app.route("/aistats", methods=["GET", "POST"])
|
||||||
|
def aistats():
|
||||||
|
return render_template("aistats.html", page_title='Placeholder', alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
109
files.py
109
files.py
@@ -14,6 +14,7 @@ import exifread
|
|||||||
import base64
|
import base64
|
||||||
import numpy
|
import numpy
|
||||||
import cv2
|
import cv2
|
||||||
|
import time
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Local Class imports
|
# Local Class imports
|
||||||
@@ -22,11 +23,8 @@ from settings import Settings
|
|||||||
|
|
||||||
class FileData():
|
class FileData():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.view_path=''
|
|
||||||
self.view_list=[]
|
self.view_list=[]
|
||||||
self.symlink=''
|
|
||||||
self.file_list=[]
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Utility Functions for Files
|
# Utility Functions for Files
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -91,7 +89,6 @@ class FileData():
|
|||||||
bt = thumb_buf.tostring()
|
bt = thumb_buf.tostring()
|
||||||
fthumbnail = base64.b64encode(bt)
|
fthumbnail = base64.b64encode(bt)
|
||||||
fthumbnail = str(fthumbnail)[2:-1]
|
fthumbnail = str(fthumbnail)[2:-1]
|
||||||
print(fthumbnail)
|
|
||||||
return fthumbnail
|
return fthumbnail
|
||||||
|
|
||||||
|
|
||||||
@@ -100,43 +97,55 @@ class FileData():
|
|||||||
# multiple valid paths in import_path) #
|
# multiple valid paths in import_path) #
|
||||||
##############################################################################
|
##############################################################################
|
||||||
def GenerateFileData(self):
|
def GenerateFileData(self):
|
||||||
sets = Settings.query.filter(Settings.name=="import_path").all()
|
settings = Settings.query.all()
|
||||||
paths= sets[0].value.split("#")
|
last_import_date = settings[0].last_import_date
|
||||||
|
paths = settings[0].import_path.split("#")
|
||||||
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
|
print( "GenerateFileData: Checking {}".format( path ) )
|
||||||
path = self.FixPath(path)
|
path = self.FixPath(path)
|
||||||
if os.path.exists( path ):
|
if os.path.exists( path ):
|
||||||
self.view_path = path
|
|
||||||
# to serve static content of the images, we create a symlink
|
# to serve static content of the images, we create a symlink
|
||||||
# from inside the static subdir of each import_path that exists
|
# from inside the static subdir of each import_path that exists
|
||||||
self.symlink = self.FixPath('static/{}'.format( os.path.basename(path[0:-1])))
|
symlink = self.FixPath('static/{}'.format( os.path.basename(path[0:-1])))
|
||||||
if not os.path.exists(self.symlink):
|
if not os.path.exists(symlink):
|
||||||
os.symlink(path, self.symlink)
|
os.symlink(path, symlink)
|
||||||
|
|
||||||
self.file_list.append(glob.glob(self.view_path + '**', recursive=True))
|
file_list=[]
|
||||||
for file in self.file_list[0]:
|
file_list.append(glob.glob(path + '**', recursive=True))
|
||||||
fthumbnail = None
|
for file in file_list[0]:
|
||||||
if file == path:
|
if file == path:
|
||||||
continue
|
continue
|
||||||
if os.path.isdir(file):
|
stat = os.stat(file)
|
||||||
ftype = 'Directory'
|
if last_import_date == 0 or stat.st_ctime > last_import_date:
|
||||||
elif self.isImage(file):
|
print( "{} - {} is newer than {}".format( file, stat.st_ctime, last_import_date ) )
|
||||||
ftype = 'Image'
|
fthumbnail = None
|
||||||
fthumbnail = self.getExif(file)
|
if os.path.isdir(file):
|
||||||
elif self.isVideo(file):
|
ftype = 'Directory'
|
||||||
ftype = 'Video'
|
elif self.isImage(file):
|
||||||
fthumbnail = self.generateVideoThumbnail(file)
|
ftype = 'Image'
|
||||||
else:
|
fthumbnail = self.getExif(file)
|
||||||
ftype = 'File'
|
elif self.isVideo(file):
|
||||||
|
ftype = 'Video'
|
||||||
|
fthumbnail = self.generateVideoThumbnail(file)
|
||||||
|
else:
|
||||||
|
ftype = 'File'
|
||||||
|
|
||||||
if ftype != "Directory":
|
if ftype != "Directory":
|
||||||
fhash=self.md5(file)
|
fhash=self.md5(file)
|
||||||
else:
|
else:
|
||||||
fhash=None
|
fhash=None
|
||||||
|
|
||||||
fsize = round(os.stat(file).st_size/(1024*1024))
|
fsize = round(os.stat(file).st_size/(1024*1024))
|
||||||
fname=file.replace(path, "")
|
fname=file.replace(path, "")
|
||||||
self.view_list.append( Files( name=fname, type=ftype, size_mb=fsize, hash=fhash, thumbnail=fthumbnail ))
|
path_prefix=symlink.replace(path,"")
|
||||||
|
file_obj = Files( name=fname, type=ftype, size_mb=fsize, hash=fhash, path_prefix=path_prefix, thumbnail=fthumbnail )
|
||||||
|
db.session.add(file_obj)
|
||||||
|
else:
|
||||||
|
print( "{} - {} is OLDER than {}".format( file, stat.st_ctime, last_import_date ) )
|
||||||
|
settings[0].last_import_date = time.time()
|
||||||
|
db.session.commit()
|
||||||
|
self.view_list = Files.query.all()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
@@ -147,35 +156,59 @@ class Files(db.Model):
|
|||||||
id = db.Column(db.Integer, db.Sequence('files_id_seq'), primary_key=True )
|
id = db.Column(db.Integer, db.Sequence('files_id_seq'), primary_key=True )
|
||||||
name = db.Column(db.String, unique=True, nullable=False )
|
name = db.Column(db.String, unique=True, nullable=False )
|
||||||
type = db.Column(db.String, unique=False, nullable=False)
|
type = db.Column(db.String, unique=False, nullable=False)
|
||||||
|
path_prefix = db.Column(db.String, unique=False, nullable=False)
|
||||||
size_mb = db.Column(db.Integer, unique=False, nullable=False)
|
size_mb = db.Column(db.Integer, unique=False, nullable=False)
|
||||||
# hash might not be unique, this could be the source of dupe problems
|
# hash might not be unique, this could be the source of dupe problems
|
||||||
hash = db.Column(db.Integer, unique=True, nullable=True)
|
hash = db.Column(db.Integer, unique=True, nullable=True)
|
||||||
thumbnail = db.Column(db.LargeBinary, unique=False, nullable=True)
|
thumbnail = db.Column(db.String, unique=False, nullable=True)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<id: {}, name: {}>".format(self.id, self.name )
|
return "<id: {}, name: {}>".format(self.id, self.name )
|
||||||
|
|
||||||
|
|
||||||
|
### Initiatlise the file data set (GenerateFileData is clever enough to not
|
||||||
|
### re-process files when we run twice in quick succession, e.g. when running
|
||||||
|
### Flask in DEBUG mode
|
||||||
|
filedata = FileData()
|
||||||
|
filedata.GenerateFileData()
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /file_list -> show detailed file list of files from import_path(s)
|
# /file_list -> show detailed file list of files from import_path(s)
|
||||||
################################################################################
|
################################################################################
|
||||||
@app.route("/file_list", methods=["GET"])
|
@app.route("/file_list", methods=["GET"])
|
||||||
def file_list():
|
def file_list():
|
||||||
filedata = FileData()
|
return render_template("file_list.html", page_title='View Files (details)', file_data=filedata, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
file_data=filedata.GenerateFileData()
|
|
||||||
return render_template("file_list.html", page_title='View Files (details)', file_data=file_data, alert=st.GetAlert(), message=st.GetMessage() )
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /files -> show thumbnail view of files from import_path(s)
|
# /files -> show thumbnail view of files from import_path(s)
|
||||||
################################################################################
|
################################################################################
|
||||||
@app.route("/files", methods=["GET"])
|
@app.route("/files", methods=["GET"])
|
||||||
def files():
|
def files():
|
||||||
filedata = FileData()
|
return render_template("files.html", page_title='View Files', file_data=filedata, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
file_data=filedata.GenerateFileData()
|
|
||||||
return render_template("files.html", page_title='View Files', file_data=file_data, alert=st.GetAlert(), message=st.GetMessage() )
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /files/scannow -> allows us to force a check for new files
|
||||||
|
################################################################################
|
||||||
|
@app.route("/files/scannow", methods=["GET"])
|
||||||
|
def scannow():
|
||||||
|
filedata.GenerateFileData()
|
||||||
|
return render_template("base.html", page_title='Forced look for new items', file_data=filedata, alert="success", message="Scanned for new files" )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /files/forcescan -> deletes old data in DB, and does a brand new scan
|
||||||
|
################################################################################
|
||||||
|
@app.route("/files/forcescan", methods=["GET"])
|
||||||
|
def forcescan():
|
||||||
|
Files.query.delete()
|
||||||
|
Settings.query.all()[0].last_import_date=0
|
||||||
|
db.session.commit()
|
||||||
|
filedata.GenerateFileData()
|
||||||
|
return render_template("base.html", page_title='Forced look for new items', file_data=filedata, alert="success", message="Forced remove and recreation of all file data" )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /static -> returns the contents of any file referenced inside /static.
|
||||||
|
# we create/use symlinks in static/ to reference the images to show
|
||||||
|
################################################################################
|
||||||
@app.route("/static/<filename>")
|
@app.route("/static/<filename>")
|
||||||
def custom_static(filename):
|
def custom_static(filename):
|
||||||
return send_from_directory("static/", filename)
|
return send_from_directory("static/", filename)
|
||||||
|
|||||||
12
main.py
12
main.py
@@ -29,6 +29,18 @@ Bootstrap(app)
|
|||||||
################################# Now, import non-book classes ###################################
|
################################# Now, import non-book classes ###################################
|
||||||
from settings import Settings
|
from settings import Settings
|
||||||
from files import Files
|
from files import Files
|
||||||
|
from person import Person
|
||||||
|
from refimg import Refimg
|
||||||
|
from ai import *
|
||||||
|
|
||||||
|
####################################### CLASSES / DB model #######################################
|
||||||
|
class Person_Refimg_Link(db.Model):
|
||||||
|
__tablename__ = "person_refimg_link"
|
||||||
|
person_id = db.Column(db.Integer, db.ForeignKey('person.id'), unique=True, nullable=False, primary_key=True)
|
||||||
|
refimg_id = db.Column(db.Integer, db.ForeignKey('refimg.id'), unique=True, nullable=False, primary_key=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<person_id: {}, refimg_id>".format(self.person_id, self.refimg_id)
|
||||||
|
|
||||||
####################################### GLOBALS #######################################
|
####################################### GLOBALS #######################################
|
||||||
# allow jinja2 to call these python functions directly
|
# allow jinja2 to call these python functions directly
|
||||||
|
|||||||
112
person.py
Normal file
112
person.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
from wtforms import SubmitField, StringField, HiddenField, validators, Form
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask import request, render_template, redirect
|
||||||
|
from main import db, app, ma
|
||||||
|
from sqlalchemy import Sequence
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from status import st, Status
|
||||||
|
|
||||||
|
from files import Files
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Class describing Person in the database, and via sqlalchemy, connected to the DB as well
|
||||||
|
################################################################################
|
||||||
|
class Person(db.Model):
|
||||||
|
id = db.Column(db.Integer, db.Sequence('person_id_seq'), primary_key=True )
|
||||||
|
tag = db.Column(db.String(48), unique=False, nullable=False)
|
||||||
|
surname = db.Column(db.String(48), unique=False, nullable=False)
|
||||||
|
firstname = db.Column(db.String(48), unique=False, nullable=False)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<tag: {}, firstname: {}, surname: {}>".format(self.tag,self.firstname, self.surname)
|
||||||
|
|
||||||
|
class File_Person_Link(db.Model):
|
||||||
|
__tablename__ = "file_person_link"
|
||||||
|
file_id = db.Column(db.Integer, db.ForeignKey('files.id'), unique=True, nullable=False, primary_key=True)
|
||||||
|
person_id = db.Column(db.Integer, db.ForeignKey('person.id'), unique=True, nullable=False, primary_key=True)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<file_id: {}, person_id: {}>".format(self.file_id, self.person_id)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Helper class that inherits a .dump() method to turn class Person into json / useful in jinja2
|
||||||
|
################################################################################
|
||||||
|
class PersonSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Person
|
||||||
|
ordered = True
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Helper class that defines a form for person, used to make html <form>, with field validation (via wtforms)
|
||||||
|
################################################################################
|
||||||
|
class PersonForm(FlaskForm):
|
||||||
|
id = HiddenField()
|
||||||
|
tag = StringField('Tag (searchable name):', [validators.DataRequired()])
|
||||||
|
firstname = StringField('FirstName(s):', [validators.DataRequired()])
|
||||||
|
surname = StringField('Surname:', [validators.DataRequired()])
|
||||||
|
submit = SubmitField('Save' )
|
||||||
|
delete = SubmitField('Delete' )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Routes for person data
|
||||||
|
#
|
||||||
|
# /persons -> GET only -> prints out list of all persons
|
||||||
|
################################################################################
|
||||||
|
@app.route("/persons", methods=["GET"])
|
||||||
|
def persons():
|
||||||
|
persons = Person.query.all()
|
||||||
|
return render_template("persons.html", persons=persons, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /person -> GET/POST -> creates a new person type and when created, takes you back to /persons
|
||||||
|
################################################################################
|
||||||
|
@app.route("/person", methods=["GET", "POST"])
|
||||||
|
def new_person():
|
||||||
|
form = PersonForm(request.form)
|
||||||
|
page_title='Create new Person'
|
||||||
|
if 'surname' not in request.form:
|
||||||
|
return render_template("person.html", form=form, page_title=page_title )
|
||||||
|
else:
|
||||||
|
person = Person( tag=request.form["tag"], surname=request.form["surname"], firstname=request.form["firstname"] )
|
||||||
|
try:
|
||||||
|
db.session.add(person)
|
||||||
|
db.session.commit()
|
||||||
|
print(person)
|
||||||
|
st.SetMessage( "Created new Person ({})".format(person.tag) )
|
||||||
|
return redirect( '/persons' )
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
st.SetAlert( "danger" )
|
||||||
|
st.SetMessage( "<b>Failed to add Person:</b> {}".format(e.orig) )
|
||||||
|
return render_template("person.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /person/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
|
||||||
|
# person
|
||||||
|
################################################################################
|
||||||
|
@app.route("/person/<id>", methods=["GET", "POST"])
|
||||||
|
def person(id):
|
||||||
|
form = PersonForm(request.form)
|
||||||
|
page_title='Edit Person'
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
person = Person.query.get(id)
|
||||||
|
if 'delete' in request.form:
|
||||||
|
st.SetMessage("Successfully deleted Person: ({})".format( person.tag ) )
|
||||||
|
person = Person.query.filter(Person.id==id).delete()
|
||||||
|
if 'submit' in request.form and form.validate():
|
||||||
|
st.SetMessage("Successfully Updated Person: (From: {}, {}, {})".format(person.tag, person.firstname, person.surname) )
|
||||||
|
person.tag = request.form['tag']
|
||||||
|
person.surname = request.form['surname']
|
||||||
|
person.firstname = request.form['firstname']
|
||||||
|
st.AppendMessage(" To: ({}, {}, {})".format(person.tag, person.firstname, person.surname) )
|
||||||
|
db.session.commit()
|
||||||
|
return redirect( '/persons' )
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
st.SetAlert( "danger" )
|
||||||
|
st.SetMessage( "<b>Failed to modify Person:</b> {}".format(e.orig) )
|
||||||
|
return render_template("person.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
else:
|
||||||
|
person = Person.query.get(id)
|
||||||
|
form = PersonForm(request.values, obj=person)
|
||||||
|
return render_template("person.html", object=person, form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
105
refimg.py
Normal file
105
refimg.py
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
from wtforms import SubmitField, StringField, HiddenField, FileField, validators, Form
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from flask import request, render_template, redirect
|
||||||
|
from main import db, app, ma
|
||||||
|
from sqlalchemy import Sequence
|
||||||
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
from status import st, Status
|
||||||
|
import os
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Class describing Refimg in the database, and via sqlalchemy, connected to the DB as well
|
||||||
|
################################################################################
|
||||||
|
class Refimg(db.Model):
|
||||||
|
id = db.Column(db.Integer, db.Sequence('refimg_id_seq'), primary_key=True )
|
||||||
|
fname = db.Column(db.String(256), unique=True, nullable=False)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<tag: {}, fname: {}>".format(self.id, self.fname )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Helper class that inherits a .dump() method to turn class Refimg into json / useful in jinja2
|
||||||
|
################################################################################
|
||||||
|
class RefimgSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Refimg
|
||||||
|
ordered = True
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Helper class that defines a form for refimg, used to make html <form>, with field validation (via wtforms)
|
||||||
|
################################################################################
|
||||||
|
class RefimgForm(FlaskForm):
|
||||||
|
id = HiddenField()
|
||||||
|
fname = StringField('File name:', [validators.DataRequired()])
|
||||||
|
refimg_file = FileField('File name:')
|
||||||
|
submit = SubmitField('Save' )
|
||||||
|
delete = SubmitField('Delete' )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Routes for refimg data
|
||||||
|
#
|
||||||
|
# /refimgs -> GET only -> prints out list of all refimgs
|
||||||
|
################################################################################
|
||||||
|
@app.route("/refimgs", methods=["GET"])
|
||||||
|
def refimgs():
|
||||||
|
refimgs = Refimg.query.all()
|
||||||
|
return render_template("refimgs.html", refimgs=refimgs, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /refimg -> GET/POST -> creates a new refimg type and when created, takes you back to /refimgs
|
||||||
|
################################################################################
|
||||||
|
@app.route("/refimg", methods=["GET", "POST"])
|
||||||
|
def new_refimg():
|
||||||
|
form = RefimgForm(request.form)
|
||||||
|
page_title='Create new Reference Image'
|
||||||
|
if request.method == 'GET':
|
||||||
|
return render_template("refimg.html", form=form, page_title=page_title )
|
||||||
|
else:
|
||||||
|
# save the actual uploaded image to reference_images/
|
||||||
|
f=request.files['refimg_file']
|
||||||
|
f.save(os.path.join("reference_images/", request.form["fname"]))
|
||||||
|
|
||||||
|
# now save into the DB
|
||||||
|
refimg = Refimg( fname=request.form["fname"] )
|
||||||
|
try:
|
||||||
|
db.session.add(refimg)
|
||||||
|
db.session.commit()
|
||||||
|
st.SetMessage( "Created new Refimg ({})".format(refimg.fname) )
|
||||||
|
return redirect( '/refimgs' )
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
st.SetAlert( "danger" )
|
||||||
|
st.SetMessage( "<b>Failed to add Refimg:</b> {}".format(e.orig) )
|
||||||
|
return render_template("refimg.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /refimg/<id> -> GET/POST(save or delete) -> shows/edits/delets a single
|
||||||
|
# refimg
|
||||||
|
################################################################################
|
||||||
|
@app.route("/refimg/<id>", methods=["GET", "POST"])
|
||||||
|
def refimg(id):
|
||||||
|
form = RefimgForm(request.form)
|
||||||
|
page_title='Edit Reference Image'
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
refimg = Refimg.query.get(id)
|
||||||
|
os.remove("reference_images/{}".format(refimg.fname) )
|
||||||
|
if 'delete' in request.form:
|
||||||
|
st.SetMessage("Successfully deleted Refimg: ({})".format( refimg.fname ) )
|
||||||
|
refimg = Refimg.query.filter(Refimg.id==id).delete()
|
||||||
|
if 'submit' in request.form and form.validate():
|
||||||
|
st.SetMessage("Successfully Updated Refimg: (From: {})".format(refimg.fname))
|
||||||
|
refimg.fname = request.form['fname']
|
||||||
|
st.AppendMessage(" To: ({})".format(refimg.fname) )
|
||||||
|
# save the actual uploaded image to reference_images/
|
||||||
|
f=request.files['refimg_file']
|
||||||
|
f.save(os.path.join("reference_images/", request.form["fname"]))
|
||||||
|
db.session.commit()
|
||||||
|
return redirect( '/refimgs' )
|
||||||
|
except SQLAlchemyError as e:
|
||||||
|
st.SetAlert( "danger" )
|
||||||
|
st.SetMessage( "<b>Failed to modify Refimg:</b> {}".format(e.orig) )
|
||||||
|
return render_template("refimg.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
else:
|
||||||
|
refimg = Refimg.query.get(id)
|
||||||
|
form = RefimgForm(request.values, obj=refimg)
|
||||||
|
return render_template("refimg.html", object=refimg, form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
49
settings.py
49
settings.py
@@ -1,4 +1,4 @@
|
|||||||
from wtforms import SubmitField, StringField, HiddenField, validators, Form
|
from wtforms import SubmitField, StringField, FloatField, HiddenField, validators, Form
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask import request, render_template, redirect
|
from flask import request, render_template, redirect
|
||||||
from main import db, app, ma
|
from main import db, app, ma
|
||||||
@@ -11,13 +11,13 @@ from status import st, Status
|
|||||||
################################################################################
|
################################################################################
|
||||||
class Settings(db.Model):
|
class Settings(db.Model):
|
||||||
id = db.Column(db.Integer, db.Sequence('settings_id_seq'), primary_key=True )
|
id = db.Column(db.Integer, db.Sequence('settings_id_seq'), primary_key=True )
|
||||||
name = db.Column(db.String, unique=True, nullable=True )
|
import_path = db.Column(db.String)
|
||||||
value = db.Column(db.String, unique=True, nullable=True )
|
last_import_date = db.Column(db.Float)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<id: {}, name: {}, value: {}>".format(self.id, self.name, self.value)
|
return "<id: {}, import_path: {}, last_import_date: {}>".format(self.id, self.import_path, self.last_import_date)
|
||||||
|
|
||||||
###############################################################################
|
################################################################################
|
||||||
# Helper class that inherits a .dump() method to turn class Author into json / useful in jinja2
|
# Helper class that inherits a .dump() method to turn class Author into json / useful in jinja2
|
||||||
################################################################################
|
################################################################################
|
||||||
class SettingsSchema(ma.SQLAlchemyAutoSchema):
|
class SettingsSchema(ma.SQLAlchemyAutoSchema):
|
||||||
@@ -25,7 +25,7 @@ class SettingsSchema(ma.SQLAlchemyAutoSchema):
|
|||||||
model = Settings
|
model = Settings
|
||||||
ordered = True
|
ordered = True
|
||||||
|
|
||||||
settings_schema=SettingsSchema()
|
settings_schema = SettingsSchema(many=True)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# Helper class that defines a form for Settings, used to make html <form>
|
# Helper class that defines a form for Settings, used to make html <form>
|
||||||
@@ -33,41 +33,36 @@ settings_schema=SettingsSchema()
|
|||||||
################################################################################
|
################################################################################
|
||||||
class SettingsForm(FlaskForm):
|
class SettingsForm(FlaskForm):
|
||||||
id = HiddenField()
|
id = HiddenField()
|
||||||
name = StringField('Name:', [validators.DataRequired()])
|
import_path = StringField('Path to import from:', [validators.DataRequired()])
|
||||||
value = StringField('value:', [validators.DataRequired()])
|
last_import_date = FloatField('Date of last import:', [validators.DataRequired()])
|
||||||
submit = SubmitField('Save' )
|
submit = SubmitField('Save' )
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /settings -> show current settings
|
# /settings -> show current settings
|
||||||
################################################################################
|
################################################################################
|
||||||
@app.route("/settings", methods=["GET"])
|
@app.route("/settings", methods=["GET", "POST"])
|
||||||
def settings():
|
def settings():
|
||||||
sets = Settings.query.all()
|
|
||||||
form = SettingsForm()
|
|
||||||
page_title='Show Settings'
|
|
||||||
return render_template("settings.html", objects=sets, form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# /setting/<id> -> show current settings
|
|
||||||
################################################################################
|
|
||||||
@app.route("/setting/<id>", methods=["GET", "POST"])
|
|
||||||
def setting(id):
|
|
||||||
form = SettingsForm(request.form)
|
form = SettingsForm(request.form)
|
||||||
|
page_title='Settings'
|
||||||
if request.method == 'POST' and form.validate():
|
if request.method == 'POST' and form.validate():
|
||||||
try:
|
try:
|
||||||
|
# HACK, I don't really need an id here, but sqlalchemy get weird
|
||||||
|
# without one, so just grab the id of the only row there, it will
|
||||||
|
# do...
|
||||||
|
id = Settings.query.all()[0].id
|
||||||
s = Settings.query.get(id)
|
s = Settings.query.get(id)
|
||||||
if 'submit' in request.form:
|
if 'submit' in request.form:
|
||||||
st.SetMessage("Successfully Updated Setting (name={})".format(s.name) )
|
st.SetMessage("Successfully Updated Settings" )
|
||||||
s.name = request.form['name']
|
s.import_path = request.form['import_path']
|
||||||
s.value = request.form['value']
|
s.last_import_date = request.form['last_import_date']
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect( '/settings' )
|
return redirect( '/settings' )
|
||||||
except SQLAlchemyError as e:
|
except SQLAlchemyError as e:
|
||||||
st.SetAlert( "danger" )
|
st.SetAlert( "danger" )
|
||||||
st.SetMessage( "<b>Failed to modify Setting:</b> {}".format(e.orig) )
|
st.SetMessage( "<b>Failed to modify Setting:</b> {}".format(e.orig) )
|
||||||
return render_template("edit_setting.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
return render_template("settings.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
else:
|
else:
|
||||||
s = Settings.query.get(id)
|
tmp_sets = Settings.query.all()
|
||||||
page_title='Edit Setting: {}'.format(s.name)
|
sets = settings_schema.dump( tmp_sets )
|
||||||
form = SettingsForm(request.values, obj=s)
|
form = SettingsForm(obj=tmp_sets[0])
|
||||||
return render_template("setting.html", objects=s, form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
return render_template("settings.html", form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() )
|
||||||
|
|||||||
5
templates/aistats.html
Normal file
5
templates/aistats.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block main_content %}
|
||||||
|
<h3>Placeholder</h3>
|
||||||
|
{% endblock main_content %}
|
||||||
@@ -49,15 +49,37 @@
|
|||||||
<a class="dropdown-item" href="{{url_for('file_list')}}">View Details</a>
|
<a class="dropdown-item" href="{{url_for('file_list')}}">View Details</a>
|
||||||
</div>
|
</div>
|
||||||
</div class="nav-item dropdown">
|
</div class="nav-item dropdown">
|
||||||
|
<div class="nav-item dropdown">
|
||||||
|
<a class="nav-item dropdown nav-link dropdown-toggle" href="#" id="PersonMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Person</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="AIMenuPerson">
|
||||||
|
<a class="dropdown-item" href="{{url_for('new_person')}}">Create Person</a>
|
||||||
|
<a class="dropdown-item" href="{{url_for('persons')}}">Show People</a>
|
||||||
|
</div>
|
||||||
|
</div class="nav-item dropdown">
|
||||||
|
<div class="nav-item dropdown">
|
||||||
|
<a class="nav-item dropdown nav-link dropdown-toggle" href="#" id="RefMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Ref Image</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="AIMenu">
|
||||||
|
<a class="dropdown-item" href="{{url_for('new_refimg')}}">Create Reference Image</a>
|
||||||
|
<a class="dropdown-item" href="{{url_for('refimgs')}}">View Reference Images</a>
|
||||||
|
</div>
|
||||||
|
</div class="nav-item dropdown">
|
||||||
|
<div class="nav-item dropdown">
|
||||||
|
<a class="nav-item dropdown nav-link dropdown-toggle" href="#" id="AIMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">AI</a>
|
||||||
|
<div class="dropdown-menu" aria-labelledby="AIMenu">
|
||||||
|
<a class="dropdown-item" href="{{url_for('aistats')}}">View Stats</a>
|
||||||
|
</div>
|
||||||
|
</div class="nav-item dropdown">
|
||||||
<div class="nav-item dropdown">
|
<div class="nav-item dropdown">
|
||||||
<a class="nav-item dropdown nav-link dropdown-toggle" href="#" id="AdminMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Admin</a>
|
<a class="nav-item dropdown nav-link dropdown-toggle" href="#" id="AdminMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Admin</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="AdminMenu">
|
<div class="dropdown-menu" aria-labelledby="AdminMenu">
|
||||||
<a class="dropdown-item" href="{{url_for('settings')}}">Settings</a>
|
<a class="dropdown-item" href="{{url_for('settings')}}">Edit Settings</a>
|
||||||
|
<a class="dropdown-item" href="{{url_for('scannow')}}">Scan now (for new files)</a>
|
||||||
|
<a class="dropdown-item" href="{{url_for('forcescan')}}">Force Scan (delete data & rebuild)</a>
|
||||||
</div class="dropdow-menu">
|
</div class="dropdow-menu">
|
||||||
</div class="nav-item dropdown">
|
</div class="nav-item dropdown">
|
||||||
</div clas="navbar-nav">
|
</div clas="navbar-nav">
|
||||||
<form class="form-inline my-2 my-lg-0" method="POST" action="/search">
|
<form class="form-inline my-2 my-lg-0" method="POST" action="/search">
|
||||||
<input class="form-control mr-sm-2" type="search" placeholder="Search by title" aria-label="Search" name="term">
|
<input class="form-control mr-sm-2" type="search" placeholder="by file, date (YYYMMDD) or tag" aria-label="Search" name="term">
|
||||||
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
|
<button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
|
||||||
</form>
|
</form>
|
||||||
</div class="collapse navbar-collapse">
|
</div class="collapse navbar-collapse">
|
||||||
|
|||||||
@@ -2,24 +2,37 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 class="offset-lg-2">{{page_title}} -- {{file_data.view_path}}</h3>
|
<h3 class="offset-lg-2">{{page_title}} -- {{file_data.view_path}}</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<table class="table table table-sm col-xl-12">
|
<table class="table table-striped table-sm col-xl-12">
|
||||||
<thead><tr class="thead-light"><th>Name</th><th>Size (MB)</th><th>Hash</th></tr></thead><tbody>
|
<thead><tr class="thead-light"><th>Name</th><th>Size (MB)</th><th>Path Prefix</th><th>Hash</th></tr></thead><tbody>
|
||||||
{% for obj in file_data.view_list %}
|
{% for obj in file_data.view_list %}
|
||||||
<tr><td>
|
<tr><td>
|
||||||
{% if obj.type=="Directory" %}
|
{% if obj.type == "Directory" %}
|
||||||
<i class="fas fa-folder"></i>
|
<i style="font-size:48;" class="fas fa-folder"></i><br><span class="figure-caption">{{obj.name}}</span>
|
||||||
{% elif obj.type=="Image" %}
|
{% else %}
|
||||||
<i class="fas fa-file-image"></i>
|
<figure class="figure" font-size: 24px;>
|
||||||
{% elif obj.type=="Video" %}
|
<div style="position:relative; width:100%">
|
||||||
<i class="fas fa-file-video"></i>
|
{% if obj.type=="Image" %}
|
||||||
{% else %}
|
{% set icon="fa-file-image" %}
|
||||||
<i class="fas fa-question-circle"></i>
|
<a href="{{obj.path_prefix}}/{{obj.name}}">
|
||||||
{% endif %}
|
{% elif obj.type == "Video" %}
|
||||||
{% if obj.type=="Image" %}
|
{% set icon="fa-film" %}
|
||||||
<a href="{{file_data.symlink}}/{{obj.name}}"><img width="128" height="128" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img></a>
|
{% elif obj.type == "Directory" %}
|
||||||
{% endif %}
|
{% set icon="fa-folder" %}
|
||||||
{{obj.name}}
|
{% else %}
|
||||||
</td></td><td>{{obj.size_mb}}</td><td>{{obj.hash}}</tr>
|
{% set icon="fa-question-circle" %}
|
||||||
|
{% endif %}
|
||||||
|
<img class="thumb" style="display:block" height="48" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img>
|
||||||
|
{% if obj.type=="Image" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<div style="position:absolute; top: 2; left: 2;">
|
||||||
|
<i style="font-size:16;background-color:black;color:white" class="fas {{icon}}"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<figcaption class="figure-caption">{{obj.name}}</figcaption>
|
||||||
|
</figure>
|
||||||
|
{% endif %}
|
||||||
|
</td></td><td>{{obj.size_mb}}</td><td>{{obj.path_prefix}}</td><td>{{obj.hash}}</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody></table>
|
</tbody></table>
|
||||||
</div class="row">
|
</div class="row">
|
||||||
|
|||||||
@@ -7,30 +7,36 @@
|
|||||||
<button class="btn btn-outline-info disabled" disabled>Size:</button>
|
<button class="btn btn-outline-info disabled" disabled>Size:</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button id="sz-but-64" class="sz-but btn btn-outline-info" onClick="ChangeSize(64)">XS</button>
|
<button class="sz-but btn btn-outline-info" onClick="ChangeSize(this,64)">XS</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button id="sz-but-96" class="sz-but btn btn-outline-info" onClick="ChangeSize(96)">S</button>
|
<button class="sz-but btn btn-outline-info" onClick="ChangeSize(this,96)">S</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button id="sz-but-128" class="sz-but btn btn-info" onClick="ChangeSize(128)">M</button>
|
<button class="sz-but btn btn-info" onClick="ChangeSize(this,128)">M</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button id="sz-but-192" class="sz-but btn btn-outline-info" onClick="ChangeSize(192)">L</button>
|
<button class="sz-but btn btn-outline-info" onClick="ChangeSize(this,192)">L</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button id="sz-but-256" class="sz-but btn btn-outline-info" onClick="ChangeSize(256)">XL</button>
|
<button class="sz-but btn btn-outline-info" onClick="ChangeSize(this,256)">XL</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for obj in file_data.view_list %}
|
{% for obj in file_data.view_list %}
|
||||||
{% if obj.type != "Directory" %}
|
{% if obj.type != "Directory" %}
|
||||||
<center>
|
<center>
|
||||||
<figure class="figure px-2" font-size: 24px;>
|
<figure class="figure px-2" font-size: 24px;>
|
||||||
{% if obj.type=="Image" %}
|
{% if obj.type=="Image" %}
|
||||||
<a href="{{file_data.symlink}}/{{obj.name}}"><img class="thumb" height="128" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img></a>
|
<a href="{{obj.path_prefix}}/{{obj.name}}"><img class="thumb" height="128" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img></a>
|
||||||
{% elif obj.type == "Video" %}
|
{% elif obj.type == "Video" %}
|
||||||
<img class="thumb" height="128" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img>
|
<div style="position:relative; width:100%">
|
||||||
|
<img class="thumb" style="display:block" height="128" src="data:image/jpeg;base64,{{obj.thumbnail}}"></img>
|
||||||
|
<div style="position:absolute; top: 2; left: 2;">
|
||||||
|
<i style="font-size:32;background-color:black;color:white" class="fas fa-film"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<figcaption class="figure-caption text-center">{{obj.name}}</figcaption>
|
<figcaption class="figure-caption text-center">{{obj.name}}</figcaption>
|
||||||
</figure>
|
</figure>
|
||||||
@@ -42,10 +48,11 @@
|
|||||||
{% endblock main_content %}
|
{% endblock main_content %}
|
||||||
{% block script_content %}
|
{% block script_content %}
|
||||||
<script>
|
<script>
|
||||||
function ChangeSize(sz)
|
function ChangeSize(clicked_button,sz)
|
||||||
{
|
{
|
||||||
|
console.log(clicked_button)
|
||||||
old_but=$('.sz-but.btn-info').removeClass('btn-info').addClass('btn-outline-info')
|
old_but=$('.sz-but.btn-info').removeClass('btn-info').addClass('btn-outline-info')
|
||||||
$('#sz-but-'+sz).addClass('btn-info').removeClass('btn-outline-info')
|
$(clicked_button).addClass('btn-info').removeClass('btn-outline-info')
|
||||||
$('.thumb').attr( {height: sz, style: 'font-size:'+sz } )
|
$('.thumb').attr( {height: sz, style: 'font-size:'+sz } )
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
{{field}}<br>
|
{{field}}<br>
|
||||||
{% elif field.type != 'SubmitField' %}
|
{% elif field.type != 'SubmitField' %}
|
||||||
<div class="form-row col-lg-12">
|
<div class="form-row col-lg-12">
|
||||||
{{ field.label( class="col-lg-2" ) }}
|
{{ field.label( class="col-lg-4" ) }}
|
||||||
{{ field( class="form-control col-lg-4" ) }}
|
{{ field( class="form-control col-lg-4" ) }}
|
||||||
</div class="form-row col-lg-12">
|
</div class="form-row col-lg-12">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<br>
|
<br>
|
||||||
</div class="row">
|
</div class="row">
|
||||||
<div class="form-row col-lg-12">
|
<div class="form-row col-lg-12">
|
||||||
{{ form.submit( class="btn btn-primary offset-lg-2 col-lg-2" )}}
|
{{ form.submit( class="btn btn-primary offset-lg-4 col-lg-2" )}}
|
||||||
{% if 'Edit' in page_title %}
|
{% if 'Edit' in page_title %}
|
||||||
{{ form.delete( class="btn btn-outline-danger col-lg-2" )}}
|
{{ form.delete( class="btn btn-outline-danger col-lg-2" )}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
16
templates/persons.html
Normal file
16
templates/persons.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block main_content %}
|
||||||
|
<h3>Show All People</h3>
|
||||||
|
<table id="person_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
|
||||||
|
<thead>
|
||||||
|
<tr class="thead-light"><th>Tag</th><th>Firstname(s)</th><th>Surname</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for person in persons %}
|
||||||
|
<tr><td><a href="{{url_for('person', id=person.id )}}">{{person.tag}}</td><td>{{person.firstname}}</td>
|
||||||
|
<td>{{person.surname}}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock main_content %}
|
||||||
42
templates/refimg.html
Normal file
42
templates/refimg.html
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block main_content %}
|
||||||
|
<div class="container">
|
||||||
|
<h3 class="offset-lg-2">{{page_title}}</h3>
|
||||||
|
<div class="row">
|
||||||
|
<form class="form form-inline col-xl-12" action="" method="POST" enctype="multipart/form-data">
|
||||||
|
{{form.id}}
|
||||||
|
{{form.csrf_token}}
|
||||||
|
<div class="form-row col-lg-12">
|
||||||
|
{{ form.fname.label( class="col-lg-2" ) }}
|
||||||
|
{{ form.fname( id="fname", class="form-control col-lg-4" ) }}
|
||||||
|
|
||||||
|
<label class="btn btn-outline-primary col-lg-2">
|
||||||
|
Choose File
|
||||||
|
<input name="refimg_file" type="file" onChange="DoMagic()" style="display:none;" id="new_file_chooser">
|
||||||
|
</label>
|
||||||
|
</div class="form-row col-lg-12">
|
||||||
|
<div class="row col-lg-12">
|
||||||
|
<br>
|
||||||
|
</div class="row">
|
||||||
|
<div class="form-row col-lg-12">
|
||||||
|
{{ form.submit( class="btn btn-primary offset-lg-2 col-lg-2" )}}
|
||||||
|
{% if 'Edit' in page_title %}
|
||||||
|
{{ form.delete( class="btn btn-outline-danger col-lg-2" )}}
|
||||||
|
{% endif %}
|
||||||
|
</div class="form-row">
|
||||||
|
</form>
|
||||||
|
</div class="row">
|
||||||
|
</div class="container">
|
||||||
|
{% endblock main_content %}
|
||||||
|
|
||||||
|
{% block script_content %}
|
||||||
|
<script>
|
||||||
|
function DoMagic() {
|
||||||
|
str=$("#new_file_chooser").val()
|
||||||
|
console.log(str)
|
||||||
|
str=str.replace('C:\\fakepath\\', '' )
|
||||||
|
console.log(str)
|
||||||
|
$("#fname").val(str)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock script_content %}
|
||||||
15
templates/refimgs.html
Normal file
15
templates/refimgs.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block main_content %}
|
||||||
|
<h3>Show All Reference Images</h3>
|
||||||
|
<table id="refimg_table" class="table table-striped table-sm" data-toolbar="#toolbar" data-search="true">
|
||||||
|
<thead>
|
||||||
|
<tr class="thead-light"><th>Filename</th></tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for refimg in refimgs %}
|
||||||
|
<tr><td><a href="{{url_for('refimg', id=refimg.id )}}">{{refimg.fname}}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endblock main_content %}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{% extends "base.html" %} {% block main_content %}
|
|
||||||
<div class="container">
|
|
||||||
<h3 class="offset-lg-2">{{page_title}}</h3>
|
|
||||||
<div class="row">
|
|
||||||
<form class="form form-inline col-xl-12" action="" method="POST">
|
|
||||||
{% for field in form %}
|
|
||||||
{% if field.type == 'HiddenField' or field.type == 'CSRFTokenField' %}
|
|
||||||
{{field}}
|
|
||||||
{% elif field.type != 'SubmitField' %}
|
|
||||||
<div class="form-row col-lg-12">
|
|
||||||
{{ field.label( class="col-lg-2" ) }}
|
|
||||||
{{ field( class="form-control col-lg-4" ) }}
|
|
||||||
</div class="form-row col-lg-12">
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<div class="row col-lg-12">
|
|
||||||
<br>
|
|
||||||
</div class="row">
|
|
||||||
<div class="form-row col-lg-12">
|
|
||||||
{{ form.submit( class="btn btn-primary offset-lg-2 col-lg-2" )}}
|
|
||||||
</div class="form-row">
|
|
||||||
</form>
|
|
||||||
</div class="row">
|
|
||||||
</div class="container">
|
|
||||||
{% endblock main_content %}
|
|
||||||
@@ -1,13 +1,22 @@
|
|||||||
{% extends "base.html" %} {% block main_content %}
|
{% extends "base.html" %} {% block main_content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h3 class="offset-lg-2">{{page_title}}</h3>
|
<h3 class="offset-lg-2">{{page_title}}</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<table id="settings_tbl" class="table table-sm">
|
<form class="form form-inline col-xl-12" action="" method="POST">
|
||||||
<thead><tr class="thead-light"><th>Name</th><th>Value</th></tr></thead><tbody>
|
{% for field in form %}
|
||||||
{% for obj in objects %}
|
{% if field.type == 'HiddenField' or field.type == 'CSRFTokenField' %}
|
||||||
<tr><td><a href="{{url_for('setting', id=obj.id)}}">{{obj.name}}</a></td><td>{{obj.value}}</td></tr>
|
{{field}}<br>
|
||||||
{% endfor %}
|
{% elif field.type != 'SubmitField' %}
|
||||||
</tbody></table>
|
<div class="form-row col-lg-12">
|
||||||
</div class="row">
|
{{ field.label( class="col-lg-2" ) }}
|
||||||
</div class="container">
|
{{ field( class="form-control col-lg-10" ) }}
|
||||||
|
</div class="form-row col-lg-12">
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
<div class="form-row col-lg-12">
|
||||||
|
{{form.submit(class="btn btn-primary offset-lg-2 col-lg-2" )}}
|
||||||
|
</div class="row">
|
||||||
|
</div class="form">
|
||||||
|
</div class="row">
|
||||||
|
</div class="container">
|
||||||
{% endblock main_content %}
|
{% endblock main_content %}
|
||||||
|
|||||||
Reference in New Issue
Block a user