Files
photoassistant/files.py

169 lines
7.5 KiB
Python

from wtforms import SubmitField, StringField, HiddenField, validators, Form
from flask_wtf import FlaskForm
from flask import request, render_template, redirect, send_from_directory
from main import db, app, ma
from sqlalchemy import Sequence
from sqlalchemy.exc import SQLAlchemyError
from status import st, Status
import os
import glob
from PIL import Image
from pymediainfo import MediaInfo
import hashlib
import exifread
import base64
import numpy
import cv2
import time
################################################################################
# Local Class imports
################################################################################
from job import Job, Joblog, NewJob
from person import Person, PersonRefimgLink
from refimg import Refimg
from settings import Settings
################################################################################
# Class describing File in the database, and via sqlalchemy, connected to the DB as well
# This has to match one-for-one the DB table
################################################################################
class EntryDirLink(db.Model):
__tablename__ = "entry_dir_link"
entry_id = db.Column(db.Integer, db.ForeignKey("entry.id"), primary_key=True )
dir_eid = db.Column(db.Integer, db.ForeignKey("dir.eid"), primary_key=True )
def __repr__(self):
return "<entry_id: {}, dir_eid: {}>".format(self.entry_id, self.dir_eid)
class Dir(db.Model):
__tablename__ = "dir"
eid = db.Column(db.Integer, db.ForeignKey("entry.id"), primary_key=True )
path_prefix = db.Column(db.String, unique=True, nullable=False )
def __repr__(self):
return "<eid: {}, path_prefix: {}>".format(self.eid, self.path_prefix)
class Entry(db.Model):
__tablename__ = "entry"
id = db.Column(db.Integer, db.Sequence('file_id_seq'), primary_key=True )
name = db.Column(db.String, unique=False, nullable=False )
type_id = db.Column(db.Integer, db.ForeignKey("file_type.id"))
type = db.relationship("FileType")
dir_details = db.relationship( "Dir")
file_details = db.relationship( "File" )
in_dir = db.relationship ("Dir", secondary="entry_dir_link" )
def __repr__(self):
return "<id: {}, name: {}, type={}, dir_details={}, file_details={}, in_dir={}>".format(self.id, self.name, self.type, self.dir_details, self.file_details, self.in_dir)
class FileRefimgLink(db.Model):
__tablename__ = "file_refimg_link"
file_id = db.Column(db.Integer, db.ForeignKey('file.eid'), unique=True, nullable=False, primary_key=True)
refimg_id = db.Column(db.Integer, db.ForeignKey('refimg.id'), unique=True, nullable=False, primary_key=True)
when_processed = db.Column(db.Float)
matched = db.Column(db.Boolean)
def __repr__(self):
return f"<file_id: {self.file_id}, refimg_id: {self.refimg_id} when_processed={self.when_processed}, matched={self.matched}"
class File(db.Model):
__tablename__ = "file"
eid = db.Column(db.Integer, db.ForeignKey("entry.id"), primary_key=True )
size_mb = db.Column(db.Integer, unique=False, nullable=False)
thumbnail = db.Column(db.String, unique=False, nullable=True)
hash = db.Column(db.Integer)
year = db.Column(db.Integer)
month = db.Column(db.Integer)
day = db.Column(db.Integer)
woy = db.Column(db.Integer)
def __repr__(self):
return f"<eid: {self.eid}, size_mb={self.size_mb}, hash={self.hash}, year={self.year}, month={self.month}, day={self.day}, woy={self.woy}>"
class FileType(db.Model):
__tablename__ = "file_type"
id = db.Column(db.Integer, db.Sequence('file_type_id_seq'), primary_key=True )
name = db.Column(db.String, unique=True, nullable=False )
def __repr__(self):
return "<id: {}, name={}>".format(self.id, self.name )
################################################################################
# /file_list -> show detailed file list of files from import_path(s)
################################################################################
@app.route("/file_list", methods=["GET"])
def file_list():
return render_template("file_list.html", page_title='View Files (details)', entry_data=Entry.query.order_by(Entry.name).all())
################################################################################
# /files -> show thumbnail view of files from import_path(s)
################################################################################
@app.route("/files", methods=["GET", "POST"])
def files():
noo="Oldest"
grouping="Day"
how_many="50"
offset=0
size=128
if request.method=="POST":
noo=request.form['noo']
how_many=request.form['how_many']
offset=int(request.form['offset'])
grouping=request.form['grouping']
size = request.form['size']
if 'prev' in request.form:
offset -= int(how_many)
if offset < 0:
offset=0
if 'next' in request.form:
offset += int(how_many)
entries=[]
if noo == "Oldest":
entries=Entry.query.join(File).order_by(File.year,File.month,File.day,Entry.name).offset(offset).limit(how_many).all()
else:
entries=Entry.query.join(File).order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name).offset(offset).limit(how_many).all()
return render_template("files.html", page_title='View Files', entry_data=entries, noo=noo, grouping=grouping, how_many=how_many, offset=offset, size=size )
################################################################################
# /search -> show thumbnail view of files from import_path(s)
################################################################################
@app.route("/search", methods=["GET","POST"])
def search():
file_data=Entry.query.filter(Entry.name.ilike(f"%{request.form['term']}%")).all()
ai_data=Entry.query.join(File).join(FileRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(FileRefimgLink.matched==True).filter(Person.tag.ilike(f"%{request.form['term']}%")).all()
all_entries = file_data + ai_data
return render_template("files.html", page_title='View Files', entry_data=all_entries)
################################################################################
# /files/scannow -> allows us to force a check for new files
################################################################################
@app.route("/files/scannow", methods=["GET"])
def scannow():
job=NewJob("scannow" )
st.SetAlert("success")
st.SetMessage("scanning for new files in:&nbsp;<a href=/job/{}>Job #{}</a>&nbsp;(Click the link to follow progress)".format( job.id, job.id) )
return render_template("base.html")
################################################################################
# /files/forcescan -> deletes old data in DB, and does a brand new scan
################################################################################
@app.route("/files/forcescan", methods=["GET"])
def forcescan():
job=NewJob("forcescan" )
st.SetAlert("success")
st.SetMessage("force scan & rebuild data for files in:&nbsp;<a href=/job/{}>Job #{}</a>&nbsp;(Click the link to follow progress)".format( job.id, job.id) )
return render_template("base.html")
################################################################################
# /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>")
def custom_static(filename):
return send_from_directory("static/", filename)