diff --git a/TODO b/TODO index 568c926..c3226c0 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ ### -# get search to work +# get override data into view ### ### major fix - go to everywhere I call GetEntries(), and redo the logic totally... diff --git a/files.py b/files.py index ff4a74c..3435ab0 100644 --- a/files.py +++ b/files.py @@ -2,7 +2,7 @@ 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 import Sequence, text, select, union from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import joinedload import os @@ -252,173 +252,6 @@ def UpdatePref( pref, OPT ): pref.last_used=last_used db.session.add(pref) db.session.commit() - -################################################################################ -# GetEntriesInFlatView: func. to retrieve DB entries appropriate for flat view -################################################################################ -def GetEntriesInFlatView( OPT, prefix ): - entries=[] - num_entries=0 - - join = "Entry.query.join(File).join(EntryDirLink).join(Dir).join(PathDirLink).join(Path).filter(Path.path_prefix==prefix)" - entries = eval( f"{join}.{OPT.order}.offset({OPT.offset}).limit({OPT.how_many}).all()" ) - - if OPT.first_eid == 0 and OPT.offset == 0 and len(entries): - OPT.first_eid = entries[0].id - - if OPT.last_eid==0: - num_entries = eval( f"{join}.count()" ) - last_entry = eval( f"{join}.{OPT.last_order}.limit(1).first()" ) - if last_entry: - OPT.last_eid = last_entry.id - - return entries, num_entries - -################################################################################ -# GetEntriesInFolderView: func. to retrieve DB entries appropriate for folder view -# read inline comments to deal with variations / ordering... -################################################################################ -def GetEntriesInFolderView( OPT, prefix ): - entries=[] - num_entries=0 - # okay the root cwd is fake, so treat it specially - its Dir can be found by path with dir.rel_path='' - if os.path.dirname(OPT.cwd) == 'static': - dir=Entry.query.join(Dir).join(PathDirLink).join(Path).filter(Dir.rel_path=='').filter(Path.path_prefix==prefix).order_by(Entry.name).first() - # this can occur if the path in settings does not exist as it wont be in # the DB - if not dir: - return entries, num_entries - # although this is 1 entry, needs to come back via all() to be iterable - entries+= Entry.query.filter(Entry.id==dir.id).all() - else: - rp = OPT.cwd.replace( prefix, '' ) - # when in subdirs, replacing prefix will leave the first char as /, get rid of it - if len(rp) and rp[0] == '/': - rp=rp[1:] - dir=Entry.query.join(Dir).join(PathDirLink).join(Path).filter(Dir.rel_path==rp).filter(Path.path_prefix==prefix).order_by(Entry.name).first() - # this can occur if the path in settings does not exist as it wont be in # the DB - if not dir: - return entries, 0 - # dirs cant be sorted by date really, so do best I can for now - if OPT.noo == "Z to A" or OPT.noo == "Newest": - entries+= Entry.query.join(EntryDirLink).join(FileType).filter(EntryDirLink.dir_eid==dir.id).filter(FileType.name=='Directory').order_by(Entry.name.desc()).all() - # just do A to Z / Oldest by default or if no valid option - else: - entries+= Entry.query.join(EntryDirLink).join(FileType).filter(EntryDirLink.dir_eid==dir.id).filter(FileType.name=='Directory').order_by(Entry.name).all() - - # add any files at the current CWD (based on dir_eid in DB) - join="Entry.query.join(File).join(EntryDirLink).filter(EntryDirLink.dir_eid==dir.id)" - file_entries= eval( f"{join}.{OPT.order}.offset(OPT.offset).limit(OPT.how_many).all()") - - if OPT.offset == 0 and len(file_entries): - OPT.first_eid = file_entries[0].id - num_entries = eval( f"{join}.count()" ) - last_entry = eval( f"{join}.{OPT.last_order}.limit(1).first()" ) - if last_entry: - OPT.last_eid = last_entry.id - - entries += file_entries; - return entries, num_entries - - -################################################################################ -# GetEntriesInSearchView: func. to retrieve DB entries appropriate for Search view -# Defaults search is for any matching filename, contents of any matching dirname -# and any match with AI / face for that term. Explicit, only AI match via -# AI: syntax -################################################################################ -def GetEntriesInSearchView( OPT ): - search_term=OPT.orig_search_term - # turn * wildcard into sql wildcard of % - search_term=search_term.replace('*', '%' ) - if 'AI:' in OPT.orig_search_term: - search_term = search_term.replace('AI:','') - join=f"Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag == search_term)" - else: - join=f"Entry.query.join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person).filter(Person.tag.ilike('%{search_term}%'))" - if 'AI:' in OPT.orig_search_term: - all_entries = eval( f"{join}.{OPT.order}.offset(OPT.offset).limit(OPT.how_many).all()") - else: - file_data=eval( f"Entry.query.join(File).filter(Entry.name.ilike('%{search_term}%')).{OPT.order}.offset({OPT.offset}).limit({OPT.how_many}).all()" ) - dir_data =eval( f"Entry.query.join(File).join(EntryDirLink).join(Dir).filter(Dir.rel_path.ilike('%{search_term}%')).{OPT.order}.offset({OPT.offset}).limit({OPT.how_many}).all()" ) - ai_data =eval( f"{join}.{OPT.order}.offset({OPT.offset}).limit({OPT.how_many}).all()") - - # remove any duplicates from combined data - all_entries = [] - for f in file_data: - all_entries.append(f) - for d in dir_data: - add_it=1 - for f in file_data: - if d.name == f.name: - add_it=0 - break - if add_it: - all_entries.append(d) - for a in ai_data: - add_it=1 - for f in file_data: - if a.name == f.name: - add_it=0 - break - if add_it: - all_entries.append(a) - - # nothing found, just return now - if len(all_entries) == 0: - OPT.num_entries = 0 - return [] - - # for all searches first_entry is worked out when first_eid not set yet & offset is 0 and we have some entries - if OPT.first_eid == 0 and OPT.offset == 0 and len(all_entries): - OPT.first_eid = all_entries[0].id - if OPT.last_eid == 0: - by_fname= f"select e.id from entry e where e.name ilike '%%{search_term}%%'" - by_dirname=f"select e.id from entry e, entry_dir_link edl where edl.entry_id = e.id and edl.dir_eid in ( select d.eid from dir d where d.rel_path ilike '%%{search_term}%%' )" - by_ai =f"select e.id from entry e, face_file_link ffl, face_refimg_link frl, person_refimg_link prl, person p where e.id = ffl.file_eid and frl.face_id = ffl.face_id and frl.refimg_id = prl.refimg_id and prl.person_id = p.id and p.tag = '{search_term}'" - - if 'AI:' in OPT.orig_search_term: - sel_no_order=f"select e.*, f.* from entry e, file f where e.id=f.eid and e.id in ( {by_ai} ) " - else: - sel_no_order=f"select e.*, f.* from entry e, file f where e.id=f.eid and e.id in ( {by_fname} union {by_dirname} union {by_ai} ) " - - #num_entries - num_e_sql = f"select count(1) from ( {by_fname} union {by_dirname} union {by_ai} ) as foo" - with db.engine.connect() as conn: - OPT.num_entries = conn.execute( text( num_e_sql ) ).first().count - - if OPT.num_entries == 0: - return [] - - last_entry_sql= f"{sel_no_order} order by {OPT.last_order_raw} limit 1" - with db.engine.connect() as conn: - OPT.last_eid = conn.execute( text( last_entry_sql ) ).first().id - # store first/last eid into prefs - pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==OPT.path_type,PA_UserState.orig_ptype==OPT.orig_ptype,PA_UserState.orig_search_term==OPT.orig_search_term).first() - UpdatePref( pref, OPT ) - return all_entries - -################################################################################ -# set up "order strings" to use in ORM and raw queries as needed for -# GetEntries*Search*, GetEntries*Flat*, GetEntries*Fold* -################################################################################ -def SetOrderStrings( OPT ): - if OPT.noo == "Newest": - OPT.order="order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name.desc())" - OPT.last_order="order_by(File.year,File.month,File.day,Entry.name)" - OPT.last_order_raw=f"f.year, f.month, f.day, e.name" - elif OPT.noo == "Oldest": - OPT.order="order_by(File.year,File.month,File.day,Entry.name)" - OPT.last_order="order_by(File.year.desc(),File.month.desc(),File.day.desc(),Entry.name.desc())" - OPT.last_order_raw=f"f.year desc, f.month desc, f.day desc, e.name desc" - elif OPT.noo == "Z to A": - OPT.order="order_by(Entry.name.desc())" - OPT.last_order="order_by(Entry.name)" - OPT.last_order_raw=f"e.name" - else: - # A to Z - OPT.order="order_by(Entry.name)" - OPT.last_order="order_by(Entry.name.desc())" - OPT.last_order_raw=f"e.name desc" return ################################################################################ @@ -431,9 +264,6 @@ 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? - # Query DB for matching entries stmt = ( select(Entry) @@ -473,23 +303,63 @@ def get_dir_entries(): # get content of dir_id stmt=( select(Entry.id).join(EntryDirLink).filter(EntryDirLink.dir_eid==dir_id) ) - stmt=stmt.order_by(*order_map.get(OPT.noo) ) ids=db.session.execute(stmt).scalars().all() entries_schema = EntrySchema(many=True) entries = Entry.query.filter(Entry.id.in_(ids)).all() return jsonify(entries_schema.dump(entries)) + ################################################################################ -# Call this ONCE on first menu choice of View files, or search box submission -# create the list of entry ids that matcht the required viewing/list +# Get all relevant Entry.ids based on search_term passed in and OPT visuals ################################################################################ +def GetSearchQueryData(OPT): + query_data={} + query_data['entry_list']=None + query_data['root_eid']=0 + + search_term = OPT.search_term + # turn * wildcard into sql wildcard of % + search_term = search_term.replace('*', '%') + if 'AI:' in search_term: + search_term = search_term.replace('AI:', '') + + # AI searches are for specific ppl/joins in the DB AND we do them for ALL types of searches, define this once + ai_query = ( + select(Entry.id) + .join(File).join(FaceFileLink).join(Face).join(FaceRefimgLink).join(Refimg).join(PersonRefimgLink).join(Person) + .where(Person.tag == search_term) + .order_by(*order_map.get(OPT.noo) ) + ) + + if 'AI:' in search_term: + all_entries = db.session.execute(ai_query).scalars().all() + else: + # match name of File + file_query = select(Entry.id).join(File).where(Entry.name.ilike(f'%{search_term}%')).order_by(*order_map.get(OPT.noo)) + # match name of Dir + dir_query = select(Entry.id).join(File).join(EntryDirLink).join(Dir).where(Dir.rel_path.ilike(f'%{search_term}%')).order_by(*order_map.get(OPT.noo)) + + # Combine ai, file & dir matches with union() to dedup and then order them + combined_query = union( file_query, dir_query, ai_query ) + all_entries = db.session.execute(combined_query).scalars().all() + + query_data['entry_list']=all_entries + return query_data + + +################################################################################# +# Get all relevant Entry.ids based on files_ip/files_sp/files_rbp and OPT visuals +################################################################################# def GetQueryData( OPT ): query_data={} query_data['entry_list']=None # always get the top of the (OPT.prefix) Path's eid and keep it for OPT.folders toggling/use - dir_stmt=( select(Entry.id).join(Dir).join(PathDirLink).join(Path). - filter(Dir.rel_path == '').filter(Path.path_prefix==OPT.prefix) ) + dir_stmt=( + select(Entry.id) + .join(Dir).join(PathDirLink).join(Path) + .where(Dir.rel_path == '').where(Path.path_prefix==OPT.prefix) + ) # this should return the 1 Dir (that we want to see the content of) - and with only 1, no need to worry about order dir_arr=db.session.execute(dir_stmt).scalars().all() dir_id=dir_arr[0] @@ -501,75 +371,28 @@ def GetQueryData( OPT ): stmt=( select(Entry.id).join(EntryDirLink).filter(EntryDirLink.dir_eid==dir_id) ) else: # get every File that is in the OPT.prefix Path - stmt=( select(Entry.id).join(File).join(EntryDirLink).join(Dir).join(PathDirLink).join(Path). - filter(Path.path_prefix == OPT.prefix) ) + stmt=( + select(Entry.id) + .join(File).join(EntryDirLink).join(Dir).join(PathDirLink).join(Path) + .where(Path.path_prefix == OPT.prefix) + ) stmt=stmt.order_by(*order_map.get(OPT.noo) ) query_data['entry_list']=db.session.execute(stmt).scalars().all() return query_data -################################################################################ -# /GetEntries -> helper function that Gets Entries for required files to show -# for several routes (ifles_ip, files_sp, files_rbp, search, view_list) -################################################################################ -def GetEntries( OPT ): - entries=[] - - SetOrderStrings( OPT ) - if OPT.path_type == 'Search' or (OPT.path_type == 'View' and OPT.orig_ptype=='Search'): - return GetEntriesInSearchView( OPT ) - - # if we are a view, then it will be of something else, e.g. a list of - # import, storage, or bin images, reset OPT.path_type so that the paths array below works - if 'View' in OPT.path_type: - eid = OPT.url[6:] - OPT.path_type= OPT.orig_ptype - - paths = [] - if OPT.path_type == 'Storage': - path = SettingsSPath() - elif OPT.path_type == 'Import': - path = SettingsIPath() - elif OPT.path_type == 'Bin': - path = SettingsRBPath() - - num_entries=0 - path_cnt=1 - - # if we have not set last_eid yet, then we need to 'reset' it during the - # path loop below (if we have more than one dir in (say) Import path) - if OPT.last_eid == 0 or OPT.folders: - update_last_eid = True - else: - update_last_eid = False - prefix = SymlinkName(OPT.path_type,path,path+'/') - if OPT.folders: - tmp_ents, tmp_num_ents = GetEntriesInFolderView( OPT, prefix ) - else: - tmp_ents, tmp_num_ents = GetEntriesInFlatView( OPT, prefix ) - entries += tmp_ents - num_entries += tmp_num_ents - - if update_last_eid: - # find pref... via path_type if we are here - 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 - ################################################################################ # /change_file_opts -> allow sort order, how_many per page, etc. to change, and # then send back the new query_data to update entryList ################################################################################ @app.route("/change_file_opts", methods=["POST"]) @login_required -def change_file_opts2(): +def change_file_opts(): data = request.get_json() # Parse JSON body # allow dot-notation for OPT OPT = SimpleNamespace(**data) - if OPT.folders == 'True': + if hasattr(OPT, 'folders') and OPT.folders == 'True': OPT.folders=True else: OPT.folders=False @@ -581,26 +404,20 @@ def change_file_opts2(): ################################################################################ # /file_list -> show detailed file list of files from import_path(s) ################################################################################ -@app.route("/file_list_ip", methods=["GET", "POST"]) +@app.route("/file_list_ip", methods=["GET"]) @login_required def file_list_ip(): OPT=States( request ) - # now we have reset the offset, etc. into the prefs, we can use a GET and this will be back/forward browser button safe - if request.method=='POST': - redirect("/file_list_ip") - entries=GetEntries( OPT ) - return render_template("file_list.html", page_title='View File Details (Import Path)', entry_data=entries, OPT=OPT ) + query_data = GetQueryData( OPT ) + return render_template("file_list.html", page_title='View File Details (Import Path)', query_data=query_data, OPT=OPT ) ################################################################################ # /files -> show thumbnail view of files from import_path(s) ################################################################################ -@app.route("/files_ip", methods=["GET", "POST"]) +@app.route("/files_ip", methods=["GET"]) @login_required def files_ip(): OPT=States( request ) - # now we have reset the offset, etc. into the prefs, we can use a GET and this will be back/forward browser button safe - if request.method=='POST': - redirect("/files_ip") people = Person.query.all() move_paths = MovePathDetails() query_data = GetQueryData( OPT ) @@ -609,13 +426,10 @@ def files_ip(): ################################################################################ # /files -> show thumbnail view of files from storage_path ################################################################################ -@app.route("/files_sp", methods=["GET", "POST"]) +@app.route("/files_sp", methods=["GET"]) @login_required def files_sp(): OPT=States( request ) - # now we have reset the offset, etc. into the prefs, we can use a GET and this will be back/forward browser button safe - if request.method=='POST': - redirect("/files_sp") people = Person.query.all() move_paths = MovePathDetails() query_data = GetQueryData( OPT ) @@ -625,13 +439,10 @@ def files_sp(): ################################################################################ # /files -> show thumbnail view of files from recycle_bin_path ################################################################################ -@app.route("/files_rbp", methods=["GET", "POST"]) +@app.route("/files_rbp", methods=["GET"]) @login_required def files_rbp(): OPT=States( request ) - # now we have reset the offset, etc. into the prefs, we can use a GET and this will be back/forward browser button safe - if request.method=='POST': - redirect("/files_rbp") people = Person.query.all() move_paths = MovePathDetails() query_data = GetQueryData( OPT ) @@ -645,19 +456,13 @@ def files_rbp(): @app.route("/search/", methods=["GET", "POST"]) @login_required def search(search_term): -# print( f"req={request}" ) OPT=States( request ) -# print( f"OPT={OPT}" ) - - # if we posted to get here, its a change in State, so save it to pa_user_state, and go back to the GET version or URL - if request.method=="POST": - redirect("/search/"+search_term) OPT.search_term = search_term - # always show flat results for search to start with - OPT.folders=False - entries=GetEntries( OPT ) + OPT.folders = False + + query_data=GetSearchQueryData( OPT ) move_paths = MovePathDetails() - return render_template("files.html", page_title='View Files', search_term=search_term, entry_data=entries, OPT=OPT, move_paths=move_paths ) + return render_template("files.html", page_title='View Files', search_term=search_term, query_data=query_data, OPT=OPT, move_paths=move_paths ) ################################################################################ # /files/scan_ip -> allows us to force a check for new files @@ -786,80 +591,8 @@ def move_files(): return make_response( jsonify( job_id=job.id ) ) @login_required -@app.route("/view_list", methods=["POST"]) -def view_list(): - OPT=States( request ) - # Get next/prev set of data - e.g. if next set, then it will use orig_url - # to go forward how_many from offset and then use viewer.html to show that - # first obj of the new list of entries - entries=GetEntries( OPT ) - # this occurs when we went from the last image on a page (with how_many on - # it) and it just happened to also be the last in the DB... - if not entries: - SetFELog( message="DDP: DONT think this can happen anymore", level="danger", job=None, persistent=True, cant_close=True ) - - # undo the skip by how_many and getentries again - OPT.offset -= int(OPT.how_many) - entries=GetEntries( OPT ) - # now flag we are at the last in db, to reset current below - objs = {} - eids="" - resp={} - resp['objs']={} - for e in entries: - if not e.file_details: - continue - eids=eids+f"{e.id}," - resp['objs'][e.id]={} - resp['objs'][e.id]['url'] = e.FullPathOnFS() - resp['objs'][e.id]['name'] = e.name - resp['objs'][e.id]['type'] = e.type.name - if e.file_details.faces: - # model is used for whole file, so set it at that level (based on first face) - resp['objs'][e.id]['face_model'] = e.file_details.faces[0].facefile_lnk.model_used - resp['objs'][e.id]['faces'] = [] - - # put face data back into array format (for js processing) - for face in e.file_details.faces: - fd= {} - fd['x'] = face.face_left - fd['y'] = face.face_top - fd['w'] = face.w - fd['h'] = face.h - if face.refimg: - fd['pid'] = face.refimg.person.id - fd['who'] = face.refimg.person.tag - fd['distance'] = round(face.refimg_lnk.face_distance,2) - resp['objs'][e.id]['faces'].append(fd) - - eids=eids.rstrip(",") - lst = eids.split(',') - if 'next' in request.form: - OPT.current = int(lst[0]) - if 'prev' in request.form: - OPT.current = int(lst[-1]) - - resp['current']=OPT.current - # OPT.first_eid can still be 0 IF we have gone past the first page, I could - # better set this in states rather than kludge this if... think about it - if OPT.first_eid>0: - resp['first_eid']=OPT.first_eid - resp['last_eid']=OPT.last_eid - resp['eids']=eids - resp['offset']=OPT.offset -# print( f"BUG-DEBUG: /view_list route #1 - OPT={OPT}, eids={eids} ") - # save pref to keep the new current value, first/last - pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.orig_ptype==OPT.orig_ptype,PA_UserState.view_eid==OPT.view_eid).first() -# print( f"BUG-DEBUG: /view_list route #2 - OPT={OPT}, eids={eids} ") - UpdatePref( pref, OPT ) -# print( f"BUG-DEBUG: /view_list route #3 - OPT={OPT}, eids={eids} ") - - return make_response( resp ) - - -@login_required -@app.route("/newview/", methods=["POST"]) -def newview(): +@app.route("/view/", methods=["POST"]) +def view(): data = request.get_json() # Parse JSON body eid = data.get('eid', 0) # Extract list of ids @@ -882,22 +615,9 @@ def newview(): data=db.session.execute(stmt).unique().scalars().all() return jsonify(entries_schema.dump(data)) -################################################################################ -# /view/id -> grabs data from DB and views it (GET) -################################################################################ -@login_required -@app.route("/view/", methods=["GET"]) -def view(id): - OPT=States( request ) - objs = {} - entries=GetEntries( OPT ) - eids="" - for e in entries: - objs[e.id]=e - eids += f"{e.id}," - # if this is a dir, we wont view it with a click anyway, so move on... - if not e.file_details: - continue + +#### +""" # process any overrides for face in e.file_details.faces: # now get any relevant override and store it in objs... @@ -909,20 +629,6 @@ def view(id): mo.type = FaceOverrideType.query.filter( FaceOverrideType.name== 'Manual match to existing person' ).first() face.manual_override=mo - 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("/") - print( f"id={id}, eids={eids}" ) - return "200" - else: NMO_data = FaceOverrideType.query.all() setting = Settings.query.first() imp_path = setting.import_path @@ -930,18 +636,7 @@ def view(id): bin_path = setting.recycle_bin_path # print( f"BUG-DEBUG: /view/id GET route - OPT={OPT}, eids={eids}, current={int(id)} ") return render_template("viewer.html", current=int(id), eids=eids, objs=objs, OPT=OPT, NMO_data=NMO_data, imp_path=imp_path, st_path=st_path, bin_path=bin_path ) - -################################################################################## -# /view/id -> grabs data from DB and views it (POST -> set state, redirect to GET) -################################################################################## -@app.route("/view/", methods=["POST"]) -@login_required -def view_img_post(id): - # set pa_user_states... - OPT=States( request ) -# print( f"BUG-DEBUG: /view/id POST route - OPT={OPT}, id={id} ") - # then use back-button friendly URL (and use pa_user_states to view the right image in the right list - return redirect( "/view/" + id ); +""" # route called from front/end - if multiple images are being transformed, each transorm == a separate call # to this route (and therefore a separate transorm job. Each reponse allows the f/e to check the @@ -1006,14 +701,6 @@ def _jinja2_filter_toplevelfolderof(path, cwd): else: return False -############################################################################### -# This func creates a new filter in jinja2 to test to hand back the parent path -# from a given path -################################################################################ -@app.template_filter('ParentPath') -def _jinja2_filter_parentpath(path): - return os.path.dirname(path) - ############################################################################### # route to allow the Move Dialog Box to pass a date (YYYYMMDD) and returns a # json list of existing dir names that could be near it in time. Starting diff --git a/internal/js/files_support.js b/internal/js/files_support.js index 56c4eda..f12bb4a 100644 --- a/internal/js/files_support.js +++ b/internal/js/files_support.js @@ -351,11 +351,6 @@ function addFigure( obj, last, ecnt) last.printed = obj.file_details.month; } } - /* - {% if not entry_data %} - No matches for: '{{search_term}}' - {% endif %} - */ // Image/Video/Unknown entry if (obj.type.name === "Image" || obj.type.name === "Video" || obj.type.name === "Unknown") { @@ -466,9 +461,9 @@ function getDirEntries(dir_id, back) $.ajax({ type: 'POST', url: '/get_dir_entries', - data: JSON.stringify(data), // Stringify the data - contentType: 'application/json', // Set content type - dataType: 'json', // Expect JSON response + data: JSON.stringify(data), + contentType: 'application/json', + dataType: 'json', success: function(res) { document.entries=res // rebuild entryList/pageList as each dir comes with new entries @@ -525,6 +520,8 @@ function drawPageOfFigures() addFigure( obj, last, ecnt ) ecnt++ } + if( document.entries.length == 0 && OPT.search_term != '' ) + $('#figures').append( ` No matches for: '${OPT.search_term}'` ) $('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; }); $('.figure').dblclick( function(e) { dblClickToViewEntry( $(this).attr('id') ) } ) // for dir, getDirEntries 2nd param is back (or "up" a dir) @@ -532,8 +529,47 @@ function drawPageOfFigures() $(".back").click( function(e) { getDirEntries(this.id,true) } ) } +function getPageFileList(res, viewingIdx) +{ + $('#file_list_div').empty() + html='' + html+='' + for (const obj of res) { + html+=` + + + + ` + } + html+='
NameSize (MB)Path PrefixHash
+
+ + + + ${obj.name} +
+
${obj.file_details.size_mb}${obj.in_dir.in_path.path_prefix.replace("static/","")}/${obj.in_dir.rel_path}${obj.file_details.hash}
' + $('#file_list_div').append(html) +} + +// function called when we get another page from inside the files view +function getPageFigures(res, viewingIdx) +{ + // add all the figures to files_div + drawPageOfFigures() +} + +// function called when we get another page from inside the viewer +function getPageViewer(res, viewingIdx) +{ + document.viewing=document.entries[viewingIdx] + // update viewing, arrows and image/video too + ViewImageOrVideo() +} + // Function to get the 'page' of entry ids out of entryList -function getPage(pageNumber,viewing_idx=0) +function getPage(pageNumber, successCallback, viewingIdx=0) { // before we do anything, disabled left/right arrows on viewer to stop // getting another event before we have the data for the page back @@ -546,29 +582,14 @@ function getPage(pageNumber,viewing_idx=0) // set up data to send to server to get the entry data for entries in pageList data={} data.ids = pageList - data.query = 99999 $.ajax({ - type: 'POST', - url: '/get_entries_by_ids', - data: JSON.stringify(data), // Stringify the data - contentType: 'application/json', // Set content type - dataType: 'json', // Expect JSON response - success: function(res) { - document.entries=res - // add all the figures to files_div - drawPageOfFigures() - // noting we could have been in files_div, or viewer_div, update both jic - // and fix viewer_div - update viewing, arrows and image/video too - document.viewing=document.entries[viewing_idx] - resetNextPrevButtons() - ViewImageOrVideo() - }, - error: function(xhr, status, error) { - console.error("Error:", error); - } - }); - + type: 'POST', url: '/get_entries_by_ids', + data: JSON.stringify(data), contentType: 'application/json', + dataType: 'json', + success: function(res) { document.entries=res; successCallback(res,viewingIdx); }, + error: function(xhr, status, error) { console.error("Error:", error); } }); + resetNextPrevButtons() return } @@ -611,7 +632,7 @@ function resetNextPrevButtons() } // get list of eids for the next page, also make sure next/prev buttons make sense for page we are on -function nextPage() +function nextPage(successCallback) { // pageList[0] is the first entry on this page const currentPage=getPageNumberForId( pageList[0] ) @@ -621,13 +642,12 @@ function nextPage() console.error( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + OPT.how_many + " gives currentPage=" + currentPage + " and we cant go next page?" ) return } - getPage( currentPage+1 ) - resetNextPrevButtons() + getPage( currentPage+1, successCallback ) return } // get list of eids for the prev page, also make sure next/prev buttons make sense for page we are on -function prevPage() +function prevPage(successCallback) { // pageList[0] is the first entry on this page const currentPage=getPageNumberForId( pageList[0] ) @@ -637,8 +657,7 @@ function prevPage() console.error( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + OPT.how_many + " gives currentPage=" + currentPage + " and we cant go prev page?" ) return } - getPage( currentPage-1 ) - resetNextPrevButtons() + getPage( currentPage-1, successCallback ) return } @@ -646,3 +665,45 @@ function isMobile() { try{ document.createEvent("TouchEvent"); return true; } catch(e){ return false; } } + + +function changeOPT(successCallback) { + OPT.how_many=$('#how_many').val() + new_f=$('#folders').val() + new_f=( new_f == 'True' ) + // if change to/from folders, also fix the noo menu + if( new_f != OPT.folders ) + { + if( new_f ) + { + $('#noo option:lt(2)').prop('disabled', true); + $('#noo').val(OPT.default_folder_noo) + } + else + { + $('#noo option:lt(2)').prop('disabled', false); + $('#noo').val(OPT.default_flat_noo) + } + } + OPT.noo=$('#noo').val() + OPT.folders=new_f + OPT.folders=$('#folders').val() + OPT.grouping=$('#grouping').val() + OPT.size=$('#size').val() + $.ajax({ + type: 'POST', + url: '/change_file_opts', + data: JSON.stringify(OPT), + contentType: 'application/json', + success: function(resp) { + entryList=resp.query_data.entry_list + // put data back into booleans, ints, etc + OPT.folders=( OPT.folders == 'True' ) + OPT.how_many=parseInt(OPT.how_many) + $('.how_many_text').html( ` ${OPT.how_many} files ` ) + OPT.root_eid=parseInt(OPT.root_eid) + OPT.size=parseInt(OPT.size) + getPage(1,successCallback) + } + }) +} diff --git a/templates/file_list.html b/templates/file_list.html index 795225e..727bb6f 100644 --- a/templates/file_list.html +++ b/templates/file_list.html @@ -1,64 +1,49 @@ {% extends "base.html" %} {% block main_content %} + +

{{page_title}}

-
- - -
- {{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "$('#offset').val(0)", "rounded-start py-1 my-1")|safe }} - {{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"], "", "rounded-end py-1 my-1" )|safe }} + {{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "changeOPT(getPageFileList); return false", "rounded-start py-1 my-1")|safe }} + {{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"], "changeOPT(getPageFileList); return false", "rounded-end py-1 my-1" )|safe }}
- {% set prv_disabled="" %} - {% if OPT.offset|int == 0 %} - {% set prv_disabled="disabled" %} - {% endif %} - -  {{OPT.how_many}} files  - {% set nxt_disabled="" %} - {% if entry_data|length < OPT.how_many|int %} - {% set nxt_disabled="disabled" %} - {% endif %} -
-
- - - {% for obj in entry_data %} - - {% if obj.type.name != "Directory" %} - - {% else %} - - {% endif %} - - {% endfor %} -
NameSize (MB)Path PrefixHash
- {% if obj.type.name == "Image" or obj.type.name == "Video" %} -
- -
{{obj.name}}
-
- {% endif %} -
{{obj.file_details.size_mb}}{{obj.in_dir.in_path.path_prefix.replace("static/","")}}/{{obj.in_dir.rel_path}}{{obj.file_details.hash}}
-
+
+
+
+ +
+
+ +  {{OPT.how_many}} files  + +
+
{% endblock main_content %} +{% block script_content %} + +{% endblock script_content %} diff --git a/templates/files.html b/templates/files.html index 81be1af..4c38109 100644 --- a/templates/files.html +++ b/templates/files.html @@ -23,19 +23,8 @@ document.viewing_eid=null; document.viewing=null; - var OPT={} - OPT.noo='{{OPT.noo}}' - OPT.how_many={{OPT.how_many}} - OPT.folders="{{OPT.folders}}" === "True" - OPT.grouping='{{OPT.grouping}}' - OPT.cwd='{{OPT.cwd}}' - OPT.root_eid={{query_data.root_eid}} - OPT.search_term='{{OPT.orig_search_term}}' - OPT.size={{OPT.size}} - OPT.prefix='{{OPT.prefix}}' - OPT.default_flat_noo='{{OPT.default_flat_noo}}' - OPT.default_folder_noo='{{OPT.default_folder_noo}}' - OPT.default_search_noo='{{OPT.default_search_noo}}' + var OPT = {{ OPT.to_dict()|tojson }}; + OPT.root_eid = {{ query_data.root_eid }}; // this is the list of entry ids for the images for ALL matches for this query var entryList={{query_data.entry_list}} @@ -43,57 +32,17 @@ // pageList is just those entries shown on this page from the full entryList var pageList=[] // force pageList to set pageList for & render the first page - getPage(1) + getPage(1,getPageFigures) - function cFO() { - OPT.how_many=$('#how_many').val() - new_f=$('#folders').val() - new_f=( new_f == 'True' ) - // if change to/from folders, also fix the noo menu - if( new_f != OPT.folders ) - { - if( new_f ) - { - $('#noo option:lt(2)').prop('disabled', true); - $('#noo').val(OPT.default_folder_noo) - } - else - { - $('#noo option:lt(2)').prop('disabled', false); - $('#noo').val(OPT.default_flat_noo) - } - } - OPT.noo=$('#noo').val() - OPT.folders=new_f - OPT.folders=$('#folders').val() - OPT.grouping=$('#grouping').val() - OPT.size=$('#size').val() - $.ajax({ - type: 'POST', - url: '/change_file_opts', - data: JSON.stringify(OPT), - contentType: 'application/json', - success: function(resp) { - entryList=resp.query_data.entry_list - // put data back into booleans, ints, etc - OPT.folders=( OPT.folders == 'True' ) - OPT.how_many=parseInt(OPT.how_many) - $('.how_many_text').html( ` ${OPT.how_many} files ` ) - OPT.root_eid=parseInt(OPT.root_eid) - OPT.size=parseInt(OPT.size) - getPage(1) - } - }) + function changeSize() + { + sz=$('input[name="size"]:checked').val(); + $('.thumb').prop('height',sz); } -
- - {% if search_term is defined %} - - {% endif %}
{% if OPT.folders %}
@@ -113,13 +62,12 @@ {% endif %}
- {{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "cFO(); return false", "rounded-start py-2")|safe }} - {{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"], "cFO(); return false" )|safe }} + {{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "changeOPT(getPageFigures); return false", "rounded-start py-2")|safe }} + {{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"], "changeOPT(getPageFigures); return false" )|safe }} {% if OPT.folders %} - - {{CreateFoldersSelect( OPT.folders, "cFO(); return false", "rounded-end" )|safe }} + {{CreateFoldersSelect( OPT.folders, "changeOPT(getPageFigures); return false", "rounded-end" )|safe }} {% else %} - {{CreateFoldersSelect( OPT.folders, "cFO(); return false" )|safe }} + {{CreateFoldersSelect( OPT.folders, "changeOPT(getPageFigures); return false" )|safe }} grouped by: {{CreateSelect( "grouping", OPT.grouping, ["None", "Day", "Week", "Month"], "OPT.grouping=$('#grouping').val();drawPageOfFigures();return false", "rounded-end")|safe }} {% endif %} @@ -134,11 +82,11 @@ {% endif %}
-  {{OPT.how_many}} files  -
-
- {% if OPT.size == 64 %} - {% set bt="btn-info text-white" %} - {% else %} - {% set bt="btn-outline-info" %} - {% endif %} - - {% if OPT.size == 96 %} - {% set bt="btn-info text-white" %} - {% else %} - {% set bt="btn-outline-info" %} - {% endif %} - - {% if OPT.size == 128 %} - {% set bt="btn-info text-white" %} - {% else %} - {% set bt="btn-outline-info" %} - {% endif %} - - {% if OPT.size == 192 %} - {% set bt="btn-info text-white" %} - {% else %} - {% set bt="btn-outline-info" %} - {% endif %} - - {% if OPT.size == 256 %} - {% set bt="btn-info text-white" %} - {% else %} - {% set bt="btn-outline-info" %} - {% endif %} - -
+
+ + + + + + + + + + + + + + +
- -
- {% set eids=namespace( str="" ) %} - {# gather all the file eids and collect them in case we go gallery mode #} - {% for obj in query_data.entry_list %} - {% set eids.str = eids.str + obj|string +"," %} - {% endfor %} -
-
-  {{OPT.how_many}} files  -
@@ -219,6 +142,8 @@