viewer now works for files_ip, still have broken bits everywhere - files_rbp, change_opts, do I want a back button? lots of dead/old code, probably cam move more js into *_support, and do I want to keep files_support separate to view_support

This commit is contained in:
2025-09-30 00:29:11 +10:00
parent 0851b79e16
commit a0e06717ac
4 changed files with 477 additions and 228 deletions

123
files.py
View File

@@ -1,8 +1,10 @@
from flask_wtf import FlaskForm
from flask import request, render_template, redirect, send_from_directory, url_for, jsonify, make_response
from marshmallow import Schema, fields
from main import db, app, ma
from sqlalchemy import Sequence, text, select
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import joinedload
import os
import glob
import json
@@ -172,16 +174,43 @@ class FileTypeSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = FileType
load_instance = True
class FileSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = File
load_instance = True
class DirSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = Dir
load_instance = True
eid = ma.auto_field() # Explicitly include eid
in_path = ma.Nested(PathSchema)
class FaceFileLinkSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = FaceFileLink
load_instance = True
class PersonSchema(ma.SQLAlchemyAutoSchema):
class Meta: model=Person
load_instance = True
class RefimgSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Refimg
exclude = ('face',)
load_instance = True
person = ma.Nested(PersonSchema)
class FaceRefimgLinkSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = FaceRefimgLink
load_instance = True
class FaceSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model=Face
exclude = ('face',)
load_instance = True
refimg = ma.Nested(RefimgSchema,allow_none=True)
class FileSchema(ma.SQLAlchemyAutoSchema):
class Meta: model = File
load_instance = True
faces = ma.Nested(FaceSchema,many=True,allow_none=True)
################################################################################
# Schema for Entry so we can json for data to the client
################################################################################
@@ -191,11 +220,19 @@ class EntrySchema(ma.SQLAlchemyAutoSchema):
load_instance = True
type = ma.Nested(FileTypeSchema)
file_details = ma.Nested(FileSchema)
file_details = ma.Nested(FileSchema,allow_none=True)
# noting dir_details needs in_path to work
dir_details = ma.Nested(DirSchema)
# noting in_dir needs in_path and in_path.type to work
in_dir = ma.Nested(DirSchema)
# allow us to use FullPathOnFS()
FullPathOnFS = fields.Method("get_full_path")
def get_full_path(self, obj):
return obj.FullPathOnFS()
# global - this will be use more than once below, so do it once for efficiency
entries_schema = EntrySchema(many=True)
################################################################################
# util function to just update the current/first/last positions needed for
@@ -396,14 +433,26 @@ def process_ids():
# DDP: debate here, do I get query_id, do I validate whether we are asking
# for ids not in the query? OR, dont even make/store/have query?
# marshmallow will allow us to json the data the way we need for the client
entries_schema = EntrySchema(many=True)
# Query DB for matching entries
entries = Entry.query.filter(Entry.id.in_(ids)).all()
stmt = (
select(Entry)
.options(
joinedload(Entry.file_details).joinedload(File.faces),
joinedload(Entry.file_details).joinedload(File.faces).joinedload(Face.refimg).joinedload(Refimg.person)
)
.where(Entry.id.in_(ids))
)
# return entries as json
return jsonify(entries_schema.dump(entries))
# unique as the ORM query returns a Cartesian product for the joins. E.g if file has 3 faces, the result has 3 rows of the same entry and file data, but different face data
data=db.session.execute(stmt).unique().scalars().all()
# data is now in whatever order the DB returns- faster in python than DB supposedly. So, create a mapping from id to entry for quick lookup
entry_map = {entry.id: entry for entry in data}
# Sort the entries according to the order of ids
sorted_data = [entry_map[id_] for id_ in ids if id_ in entry_map]
return jsonify(entries_schema.dump(sorted_data))
################################################################################
@@ -806,6 +855,40 @@ def view_list():
return make_response( resp )
@login_required
@app.route("/newview/", methods=["POST"])
def newview():
data = request.get_json() # Parse JSON body
eid = data.get('eid', 0) # Extract list of ids
# need appropriate schema? to get FaceData with entry, lists should just be
# what we have in entryList so it can help with next/prev
# include Entry for name/path, ffl (model_used), frl (distance), Face (for w/h, etc), Person (id,tag)
#stmt=select(Entry).filter(Entry.id==eid)
stmt = (
select(Entry)
.options(
joinedload(Entry.file_details).joinedload(File.faces),
joinedload(Entry.file_details).joinedload(File.faces).joinedload(Face.refimg).joinedload(Refimg.person)
)
.where(Entry.id == eid)
)
print( stmt )
# this needs unique because:
# entry (one row for id=660)
# file (one row, since file_details is a one-to-one relationship)
# face (many rows, since a file can have many faces)
# refimg and person (one row per face, via the link tables)
# The SQL query returns a Cartesian product for the joins involving collections (like faces). For example, if your file has 3 faces,
# the result set will have 3 rows, each with the same entry and file data, but different face, refimg, and person data.
data=db.session.execute(stmt).unique().scalars().all()
print( data )
return jsonify(entries_schema.dump(data))
################################################################################
# /view/id -> grabs data from DB and views it (GET)
################################################################################
@@ -836,14 +919,16 @@ def view(id):
eids=eids.rstrip(",")
# jic, sometimes we trip this, and rather than show broken pages / destroy
if id not in eids:
SetFELog( message=f"ERROR: viewing an id, but its not in eids OPT={OPT}, id={id}, eids={eids}", level="danger", persistent=True, cant_close=False)
msg="Sorry, viewing data is confused, cannot view this image now"
if os.environ['ENV'] == "production":
msg += "Clearing out all states. This means browser back buttons will not work, please start a new tab and try again"
PA_UserState.query.delete()
db.session.commit()
SetFELog( msg, "warning", persistent=True, cant_close=False )
return redirect("/")
# SetFELog( message=f"ERROR: viewing an id, but its not in eids OPT={OPT}, id={id}, eids={eids}", level="danger", persistent=True, cant_close=False)
# msg="Sorry, viewing data is confused, cannot view this image now"
# if os.environ['ENV'] == "production":
# msg += "Clearing out all states. This means browser back buttons will not work, please start a new tab and try again"
# PA_UserState.query.delete()
# db.session.commit()
# SetFELog( msg, "warning", persistent=True, cant_close=False )
# return redirect("/")
print( f"id={id}, eids={eids}" )
return "200"
else:
NMO_data = FaceOverrideType.query.all()
setting = Settings.query.first()