diff --git a/files.py b/files.py index 40ee267..4063fdf 100644 --- a/files.py +++ b/files.py @@ -1,11 +1,11 @@ -from wtforms import SubmitField, StringField, HiddenField, validators, Form from flask_wtf import FlaskForm from flask import request, render_template, redirect, send_from_directory, url_for, jsonify, make_response from main import db, app, ma -from sqlalchemy import Sequence, text +from sqlalchemy import Sequence, text, select from sqlalchemy.exc import SQLAlchemyError import os import glob +import json from PIL import Image from pymediainfo import MediaInfo import hashlib @@ -20,8 +20,8 @@ import pytz import html from flask_login import login_required, current_user from states import States, PA_UserState +from query import Query -################################################################################ # Local Class imports ################################################################################ from job import Job, JobExtra, Joblog, NewJob, SetFELog @@ -119,7 +119,7 @@ class File(db.Model): 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) + hash = db.Column(db.String) year = db.Column(db.Integer) month = db.Column(db.Integer) day = db.Column(db.Integer) @@ -141,6 +141,61 @@ class FileType(db.Model): def __repr__(self): return f"" + +################################################################################ +# this is how we order all queries based on value of 'noo' - used with +# access *order_map.get(OPT.noo) +################################################################################ +order_map = { + "Newest": (File.year.desc(),File.month.desc(),File.day.desc(),Entry.name.desc()), + "Oldest": (File.year,File.month,File.day,Entry.name), + # careful, these need to be tuples, so with a , at the end + "Z to A": (Entry.name.desc(),), + "A to Z": (Entry.name.asc(),), +} + +################################################################################ + +################################################################################ +# Schemas for Path, FileType, File, Dir - used in EntrySchema +################################################################################ +class PathType(ma.SQLAlchemyAutoSchema): + class Meta: model = PathType + load_instance = True + +class PathSchema(ma.SQLAlchemyAutoSchema): + class Meta: model = Path + load_instance = True + type = ma.Nested(PathType) + +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 + in_path = ma.Nested(PathSchema) + +################################################################################ +# Schema for Entry so we can json for data to the client +################################################################################ +class EntrySchema(ma.SQLAlchemyAutoSchema): + # gives id, name, type_id + class Meta: model = Entry + load_instance = True + + type = ma.Nested(FileTypeSchema) + file_details = ma.Nested(FileSchema) + # 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) + ################################################################################ # util function to just update the current/first/last positions needed for # viewing / using pa_user_state DB table @@ -327,9 +382,66 @@ def SetOrderStrings( OPT ): OPT.last_order_raw=f"e.name desc" return +################################################################################ +# /get_entries_by_ids -> route where we supply list of entry ids (for next/prev +# page of data we want to show). Returns json of all matching entries +################################################################################ +@app.route('/get_entries_by_ids', methods=['POST']) +@login_required +def process_ids(): + data = request.get_json() # Parse JSON body + ids = data.get('ids', []) # Extract list of 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() + + # return entries as json + return jsonify(entries_schema.dump(entries)) + +### +# query_data = { 'entry_lst': entry_lst, 'query_id': query_id, ... } +### +# Call this ONCE on first menu choice of View files, or search box submission +def GetQueryData( OPT ): + query_data = {} + query_data['query_id']=None + query_data['entry_list']=None + + # set up the sql order strings (back in OPT) based on value of noo + # FIXME: remove this for all last/first eid usage AND use order_map + SetOrderStrings( OPT ) + + if OPT.path_type == 'Search': + print ("NOT YET") + return query_data + + if OPT.folders: + entries, tmp_num_ents = GetEntriesInFolderView( OPT, prefix ) + else: + stmt = ( select(Entry.id).join(File).join(EntryDirLink).join(Dir).join(PathDirLink). + join(Path).filter(Path.path_prefix == OPT.prefix) ) + stmt = stmt.order_by(*order_map.get(OPT.noo) ) + query_data['entry_list']= db.session.execute(stmt).scalars().all() + + # first time we get the data q_offset is 0, current=first one, search never gets here, so search_term='' + # FIXME: Doubt we need cwd -- I only need originals to either invalidate this list, or recreate it... need to think about that a lot more + query = Query( path_type=OPT.path_type, noo=OPT.noo, q_offset=0, folder=OPT.folders, grouping=OPT.grouping, root=OPT.root, cwd=OPT.cwd, search_term='', + entry_list=query_data['entry_list'], current=query_data['entry_list'][0], created=datetime.now(pytz.utc) ) + db.session.add(query) + db.session.commit() + + query_data['query_id']=query.id + return query_data + ################################################################################ # /GetEntries -> helper function that Gets Entries for required files to show -# for several routes (files_ip, files_sp, files_rbp, search, view_list) +# for several routes (ifles_ip, files_sp, files_rbp, search, view_list) ################################################################################ def GetEntries( OPT ): entries=[] @@ -374,6 +486,7 @@ def GetEntries( OPT ): OPT.num_entries=num_entries pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==OPT.path_type).first() UpdatePref( pref, OPT ) + return entries @app.route("/change_file_opts", methods=["POST"]) @@ -409,7 +522,8 @@ def files_ip(): entries=GetEntries( OPT ) people = Person.query.all() move_paths = MovePathDetails() - return render_template("files.html", page_title=f"View Files ({OPT.path_type} Path)", entry_data=entries, OPT=OPT, people=people, move_paths=move_paths ) + query_data = GetQueryData( OPT ) + return render_template("files.html", page_title=f"View Files ({OPT.path_type} Path)", entry_data=entries, OPT=OPT, people=people, move_paths=move_paths, query_data=query_data ) ################################################################################ # /files -> show thumbnail view of files from storage_path