from wtforms import SubmitField, StringField, HiddenField, validators, Form from flask_wtf import FlaskForm from flask import request, render_template, redirect, url_for from main import db, app, ma from sqlalchemy import Sequence from sqlalchemy.exc import SQLAlchemyError from status import st, Status from flask_login import login_required, current_user from werkzeug import secure_filename from shared import GenFace, GenThumb from face import FaceRefimgLink import os # pylint: disable=no-member ################################################################################ # Class describing Person 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) face = db.Column(db.LargeBinary, unique=True, nullable=False) thumbnail = db.Column(db.String, unique=True, nullable=False) created_on = db.Column(db.Float) def __repr__(self): return "".format(self.id, self.fname ) class PersonRefimgLink(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 f"" 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) refimg = db.relationship('Refimg', secondary=PersonRefimgLink.__table__) def __repr__(self): return "".format(self.tag,self.firstname, self.surname, self.refimg) ################################################################################ # 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()]) save = SubmitField('Save' ) delete = SubmitField('Delete' ) ################################################################################ # Routes for person data # # /persons -> GET only -> prints out list of all persons ################################################################################ @app.route("/persons", methods=["GET"]) @login_required def persons(): persons = Person.query.all() return render_template("persons.html", persons=persons) ################################################################################ # /person -> GET/POST -> creates a new person type and when created, takes you back to /persons ################################################################################ @app.route("/person", methods=["GET", "POST"]) @login_required def new_person(): form = PersonForm(request.form) page_title='Create new Person' if 'surname' not in request.form: return render_template("person.html", person=None, 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() 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", person=person, form=form, page_title = page_title) ################################################################################ # /person/ -> GET/POST(save or delete) -> shows/edits/delets a single # person ################################################################################ @app.route("/person/", methods=["GET", "POST"]) @login_required 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 ) ) # do the linkage tables by hand db.session.execute( f"delete from face_refimg_link frl where refimg_id in ( select refimg_id from person_refimg_link where person_id = {id} )" ) db.session.execute( f"delete from person_refimg_link where person_id = {id}" ) person = Person.query.filter(Person.id==id).delete() db.session.commit() return redirect( f'/persons' ) elif 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'] new_refs=[] for ref_img in person.refimg: if "ref-img-id-{}".format(ref_img.id) in request.form: new_refs.append(ref_img) person.refimg = new_refs db.session.add(person) st.AppendMessage(" To: ({}, {}, {})".format(person.tag, person.firstname, person.surname) ) db.session.commit() return redirect( f'/person/{person.id}' ) except SQLAlchemyError as e: st.SetAlert( "danger" ) st.SetMessage( "Failed to modify Person: {}".format(e) ) return render_template("person.html", form=form, page_title=page_title) else: person = Person.query.get(id) form = PersonForm(request.values, obj=person) return render_template("person.html", person=person, form=form, page_title = page_title) ################################################################################ # /add_refimg -> POST(add new refimg to a person) ################################################################################ @app.route("/add_refimg", methods=["POST"]) @login_required def add_refimg(): # now save into the DB person = Person.query.get(request.form['person_id']); if not person: raise Exception("could not find person to add reference image too!") f=request.files['refimg_file'] refimg = Refimg( fname=f.filename ) try: # save the actual uploaded image to reference_images/ fname=secure_filename(f.filename) if fname == "": raise Exception("invalid filename") fname = f"/tmp/{fname}" f.save( fname ) refimg.thumbnail = GenThumb( fname ) refimg.face = GenFace( fname ) os.remove(fname) person.refimg.append(refimg) db.session.add(person) db.session.add(refimg) db.session.commit() st.SetMessage( f"Associated new Refimg ({refimg.fname}) with person: {person.tag}" ) except SQLAlchemyError as e: st.SetAlert( "danger" ) st.SetMessage( f"Failed to add Refimg: {e.orig}" ) except Exception as e: st.SetAlert( "danger" ) st.SetMessage( f"Failed to modify Refimg: {e}" ) return redirect( url_for( 'person', id=person.id) )