diff --git a/.gitignore b/.gitignore index caaa717..d5066e8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ __pycache__/ photos/ images_to_process/ +new_img_dir/ static/* +reference_images/* diff --git a/ai.py b/ai.py new file mode 100644 index 0000000..53982f0 --- /dev/null +++ b/ai.py @@ -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/ -> 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() ) diff --git a/files.py b/files.py index 0f3c467..1ba02a0 100644 --- a/files.py +++ b/files.py @@ -14,6 +14,7 @@ import exifread import base64 import numpy import cv2 +import time ################################################################################ # Local Class imports @@ -22,11 +23,8 @@ from settings import Settings class FileData(): def __init__(self): - self.view_path='' self.view_list=[] - self.symlink='' - self.file_list=[] - + ################################################################################ # Utility Functions for Files ################################################################################ @@ -91,7 +89,6 @@ class FileData(): bt = thumb_buf.tostring() fthumbnail = base64.b64encode(bt) fthumbnail = str(fthumbnail)[2:-1] - print(fthumbnail) return fthumbnail @@ -100,43 +97,55 @@ class FileData(): # multiple valid paths in import_path) # ############################################################################## def GenerateFileData(self): - sets = Settings.query.filter(Settings.name=="import_path").all() - paths= sets[0].value.split("#") + settings = Settings.query.all() + last_import_date = settings[0].last_import_date + paths = settings[0].import_path.split("#") for path in paths: + print( "GenerateFileData: Checking {}".format( path ) ) path = self.FixPath(path) if os.path.exists( path ): - self.view_path = path # to serve static content of the images, we create a symlink # from inside the static subdir of each import_path that exists - self.symlink = self.FixPath('static/{}'.format( os.path.basename(path[0:-1]))) - if not os.path.exists(self.symlink): - os.symlink(path, self.symlink) + symlink = self.FixPath('static/{}'.format( os.path.basename(path[0:-1]))) + if not os.path.exists(symlink): + os.symlink(path, symlink) - self.file_list.append(glob.glob(self.view_path + '**', recursive=True)) - for file in self.file_list[0]: - fthumbnail = None + file_list=[] + file_list.append(glob.glob(path + '**', recursive=True)) + for file in file_list[0]: if file == path: continue - if os.path.isdir(file): - ftype = 'Directory' - elif self.isImage(file): - ftype = 'Image' - fthumbnail = self.getExif(file) - elif self.isVideo(file): - ftype = 'Video' - fthumbnail = self.generateVideoThumbnail(file) - else: - ftype = 'File' + stat = os.stat(file) + if last_import_date == 0 or stat.st_ctime > last_import_date: + print( "{} - {} is newer than {}".format( file, stat.st_ctime, last_import_date ) ) + fthumbnail = None + if os.path.isdir(file): + ftype = 'Directory' + elif self.isImage(file): + ftype = 'Image' + fthumbnail = self.getExif(file) + elif self.isVideo(file): + ftype = 'Video' + fthumbnail = self.generateVideoThumbnail(file) + else: + ftype = 'File' - if ftype != "Directory": - fhash=self.md5(file) - else: - fhash=None + if ftype != "Directory": + fhash=self.md5(file) + else: + fhash=None - fsize = round(os.stat(file).st_size/(1024*1024)) - fname=file.replace(path, "") - self.view_list.append( Files( name=fname, type=ftype, size_mb=fsize, hash=fhash, thumbnail=fthumbnail )) + fsize = round(os.stat(file).st_size/(1024*1024)) + fname=file.replace(path, "") + 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 ################################################################################ @@ -147,35 +156,59 @@ class Files(db.Model): id = db.Column(db.Integer, db.Sequence('files_id_seq'), primary_key=True ) name = db.Column(db.String, unique=True, 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) # hash might not be unique, this could be the source of dupe problems 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): return "".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) ################################################################################ @app.route("/file_list", methods=["GET"]) def file_list(): - filedata = FileData() - 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() ) + return render_template("file_list.html", page_title='View Files (details)', file_data=filedata, alert=st.GetAlert(), message=st.GetMessage() ) ################################################################################ # /files -> show thumbnail view of files from import_path(s) ################################################################################ @app.route("/files", methods=["GET"]) def files(): - filedata = FileData() - file_data=filedata.GenerateFileData() - return render_template("files.html", page_title='View Files', file_data=file_data, alert=st.GetAlert(), message=st.GetMessage() ) + return render_template("files.html", page_title='View Files', file_data=filedata, 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/") def custom_static(filename): return send_from_directory("static/", filename) diff --git a/main.py b/main.py index 748240f..a874f44 100644 --- a/main.py +++ b/main.py @@ -29,6 +29,18 @@ Bootstrap(app) ################################# Now, import non-book classes ################################### from settings import Settings 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 "".format(self.person_id, self.refimg_id) ####################################### GLOBALS ####################################### # allow jinja2 to call these python functions directly diff --git a/person.py b/person.py new file mode 100644 index 0000000..e56c105 --- /dev/null +++ b/person.py @@ -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 "".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 "".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
, 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( "Failed to add Person: {}".format(e.orig) ) + return render_template("person.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) + +################################################################################ +# /person/ -> GET/POST(save or delete) -> shows/edits/delets a single +# person +################################################################################ +@app.route("/person/", 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( "Failed to modify Person: {}".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() ) diff --git a/refimg.py b/refimg.py new file mode 100644 index 0000000..f9bb3ba --- /dev/null +++ b/refimg.py @@ -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 "".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 , 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( "Failed to add Refimg: {}".format(e.orig) ) + return render_template("refimg.html", form=form, page_title=page_title, alert=st.GetAlert(), message=st.GetMessage() ) + +################################################################################ +# /refimg/ -> GET/POST(save or delete) -> shows/edits/delets a single +# refimg +################################################################################ +@app.route("/refimg/", 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( "Failed to modify Refimg: {}".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() ) diff --git a/settings.py b/settings.py index 8f1a4a1..ea89d31 100644 --- a/settings.py +++ b/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 import request, render_template, redirect from main import db, app, ma @@ -11,13 +11,13 @@ from status import st, Status ################################################################################ class Settings(db.Model): id = db.Column(db.Integer, db.Sequence('settings_id_seq'), primary_key=True ) - name = db.Column(db.String, unique=True, nullable=True ) - value = db.Column(db.String, unique=True, nullable=True ) + import_path = db.Column(db.String) + last_import_date = db.Column(db.Float) def __repr__(self): - return "".format(self.id, self.name, self.value) + return "".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 ################################################################################ class SettingsSchema(ma.SQLAlchemyAutoSchema): @@ -25,7 +25,7 @@ class SettingsSchema(ma.SQLAlchemyAutoSchema): model = Settings ordered = True -settings_schema=SettingsSchema() +settings_schema = SettingsSchema(many=True) ################################################################################ # Helper class that defines a form for Settings, used to make html @@ -33,41 +33,36 @@ settings_schema=SettingsSchema() ################################################################################ class SettingsForm(FlaskForm): id = HiddenField() - name = StringField('Name:', [validators.DataRequired()]) - value = StringField('value:', [validators.DataRequired()]) + import_path = StringField('Path to import from:', [validators.DataRequired()]) + last_import_date = FloatField('Date of last import:', [validators.DataRequired()]) submit = SubmitField('Save' ) ################################################################################ # /settings -> show current settings ################################################################################ -@app.route("/settings", methods=["GET"]) +@app.route("/settings", methods=["GET", "POST"]) 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/ -> show current settings -################################################################################ -@app.route("/setting/", methods=["GET", "POST"]) -def setting(id): form = SettingsForm(request.form) + page_title='Settings' if request.method == 'POST' and form.validate(): 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) if 'submit' in request.form: - st.SetMessage("Successfully Updated Setting (name={})".format(s.name) ) - s.name = request.form['name'] - s.value = request.form['value'] + st.SetMessage("Successfully Updated Settings" ) + s.import_path = request.form['import_path'] + s.last_import_date = request.form['last_import_date'] db.session.commit() return redirect( '/settings' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) st.SetMessage( "Failed to modify Setting: {}".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: - s = Settings.query.get(id) - page_title='Edit Setting: {}'.format(s.name) - form = SettingsForm(request.values, obj=s) - return render_template("setting.html", objects=s, form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() ) + tmp_sets = Settings.query.all() + sets = settings_schema.dump( tmp_sets ) + form = SettingsForm(obj=tmp_sets[0]) + return render_template("settings.html", form=form, page_title = page_title, alert=st.GetAlert(), message=st.GetMessage() ) diff --git a/templates/aistats.html b/templates/aistats.html new file mode 100644 index 0000000..985119a --- /dev/null +++ b/templates/aistats.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block main_content %} +

Placeholder

+{% endblock main_content %} diff --git a/templates/base.html b/templates/base.html index e62e29f..ce1994e 100644 --- a/templates/base.html +++ b/templates/base.html @@ -49,15 +49,37 @@ View Details + + + - + diff --git a/templates/file_list.html b/templates/file_list.html index ddf5c1e..7534897 100644 --- a/templates/file_list.html +++ b/templates/file_list.html @@ -2,24 +2,37 @@

{{page_title}} -- {{file_data.view_path}}

- - +
NameSize (MB)Hash
+ {% for obj in file_data.view_list %} + {% if obj.type == "Directory" %} +
{{obj.name}} + {% else %} +
+ +
{{obj.name}}
+
+ {% endif %} + {% endfor %}
NameSize (MB)Path PrefixHash
- {% if obj.type=="Directory" %} - - {% elif obj.type=="Image" %} - - {% elif obj.type=="Video" %} - - {% else %} - - {% endif %} - {% if obj.type=="Image" %} - - {% endif %} - {{obj.name}} - {{obj.size_mb}}{{obj.hash}}
{{obj.size_mb}}{{obj.path_prefix}}{{obj.hash}}
diff --git a/templates/files.html b/templates/files.html index df3fcb7..1c43db3 100644 --- a/templates/files.html +++ b/templates/files.html @@ -7,30 +7,36 @@
- +
- +
- +
- +
- +
+
{% for obj in file_data.view_list %} {% if obj.type != "Directory" %}
{% if obj.type=="Image" %} - + {% elif obj.type == "Video" %} - +
+ +
+ +
+
{% endif %}
{{obj.name}}
@@ -42,10 +48,11 @@ {% endblock main_content %} {% block script_content %} diff --git a/templates/edit_setting.html b/templates/person.html similarity index 89% rename from templates/edit_setting.html rename to templates/person.html index 35ac3b1..8ace7b2 100644 --- a/templates/edit_setting.html +++ b/templates/person.html @@ -8,7 +8,7 @@ {{field}}
{% elif field.type != 'SubmitField' %}
- {{ field.label( class="col-lg-2" ) }} + {{ field.label( class="col-lg-4" ) }} {{ field( class="form-control col-lg-4" ) }}
{% endif %} @@ -17,7 +17,7 @@
- {{ 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 %} {{ form.delete( class="btn btn-outline-danger col-lg-2" )}} {% endif %} diff --git a/templates/persons.html b/templates/persons.html new file mode 100644 index 0000000..e68328e --- /dev/null +++ b/templates/persons.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + +{% block main_content %} +

Show All People

+ + + + + + {% for person in persons %} + + + {% endfor %} + +
TagFirstname(s)Surname
{{person.tag}}{{person.firstname}}{{person.surname}}
+{% endblock main_content %} diff --git a/templates/refimg.html b/templates/refimg.html new file mode 100644 index 0000000..162a47a --- /dev/null +++ b/templates/refimg.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% block main_content %} +
+

{{page_title}}

+
+
+ {{form.id}} + {{form.csrf_token}} +
+ {{ form.fname.label( class="col-lg-2" ) }} + {{ form.fname( id="fname", class="form-control col-lg-4" ) }} +   + +
+
+
+
+
+ {{ 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 %} +
+
+
+
+{% endblock main_content %} + +{% block script_content %} + +{% endblock script_content %} diff --git a/templates/refimgs.html b/templates/refimgs.html new file mode 100644 index 0000000..1af2343 --- /dev/null +++ b/templates/refimgs.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block main_content %} +

Show All Reference Images

+ + + + + + {% for refimg in refimgs %} + + {% endfor %} + +
Filename
{{refimg.fname}}
+{% endblock main_content %} diff --git a/templates/setting.html b/templates/setting.html deleted file mode 100644 index 843da7c..0000000 --- a/templates/setting.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} {% block main_content %} -
-

{{page_title}}

-
-
- {% for field in form %} - {% if field.type == 'HiddenField' or field.type == 'CSRFTokenField' %} - {{field}} - {% elif field.type != 'SubmitField' %} -
- {{ field.label( class="col-lg-2" ) }} - {{ field( class="form-control col-lg-4" ) }} -
- {% endif %} - {% endfor %} -
-
-
-
- {{ form.submit( class="btn btn-primary offset-lg-2 col-lg-2" )}} -
-
-
-
-{% endblock main_content %} diff --git a/templates/settings.html b/templates/settings.html index 2ac004a..70dd336 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -1,13 +1,22 @@ {% extends "base.html" %} {% block main_content %} -
-

{{page_title}}

-
- - - {% for obj in objects %} - - {% endfor %} -
NameValue
{{obj.name}}{{obj.value}}
-
-
+
+

{{page_title}}

+
+
+ {% for field in form %} + {% if field.type == 'HiddenField' or field.type == 'CSRFTokenField' %} + {{field}}
+ {% elif field.type != 'SubmitField' %} +
+ {{ field.label( class="col-lg-2" ) }} + {{ field( class="form-control col-lg-10" ) }} +
+ {% endif %} + {% endfor %} +
+ {{form.submit(class="btn btn-primary offset-lg-2 col-lg-2" )}} +
+
+
+
{% endblock main_content %}