added code to support changing noo/how_many/folders and do this with json data back and forth, update the UI, all works - only search is missing now. Lots of dead code can still be deleted

This commit is contained in:
2025-10-01 23:48:19 +10:00
parent b9b7a24326
commit 2e952deda0
2 changed files with 96 additions and 67 deletions

View File

@@ -21,11 +21,12 @@ from datetime import datetime, timedelta
import pytz
import html
from flask_login import login_required, current_user
from states import States, PA_UserState
from query import Query
from types import SimpleNamespace
# Local Class imports
################################################################################
from states import States, PA_UserState
from query import Query
from job import Job, JobExtra, Joblog, NewJob, SetFELog
from path import PathType, Path, MovePathDetails
from person import Refimg, Person, PersonRefimgLink
@@ -472,8 +473,7 @@ def get_dir_entries():
# get content of dir_id
stmt=( select(Entry.id).join(EntryDirLink).filter(EntryDirLink.dir_eid==dir_id) )
# FIXME: what do we do with ordering anyway???
#stmt=stmt.order_by(*order_map.get(OPT.noo) )
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()
@@ -485,39 +485,28 @@ def get_dir_entries():
################################################################################
def GetQueryData( OPT ):
query_data={}
query_data['query_id']=None
query_data['entry_list']=None
if OPT.path_type == 'Search':
print ("NOT YET")
return query_data
# 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).
filter(Dir.rel_path == '').filter(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]
# used to know the parent/root (in folder view), in flat view - just ignore/safe though
query_data['root_eid']=dir_id
if OPT.folders:
# start folder view with only the root folder
stmt=( select(Entry.id).join(EntryDirLink).filter(EntryDirLink.dir_eid==dir_id) )
query_data['entry_list']=db.session.execute(stmt).scalars().all()
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=stmt.order_by(*order_map.get(OPT.noo) )
query_data['entry_list']= db.session.execute(stmt).scalars().all()
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()
# not sure I need this in hindsight - any value at 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
################################################################################
@@ -570,13 +559,25 @@ def GetEntries( 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_opts():
# reset options based on form post, then redirect back to orig page (with a GET to allow back button to work)
OPT=States( request )
return redirect( request.referrer )
def change_file_opts2():
data = request.get_json() # Parse JSON body
# allow dot-notation for OPT
OPT = SimpleNamespace(**data)
if OPT.folders == 'True':
OPT.folders=True
else:
OPT.folders=False
# so create a new entryList, and handle that on the client
query_data = GetQueryData( OPT )
return make_response( jsonify( query_data=query_data ) )
################################################################################
# /file_list -> show detailed file list of files from import_path(s)
################################################################################
@@ -862,12 +863,6 @@ 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(
@@ -877,8 +872,7 @@ def newview():
.where(Entry.id == eid)
)
print( stmt )
# this needs unique because:
# 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)
@@ -886,7 +880,6 @@ def newview():
# 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 File

@@ -5,6 +5,8 @@
<script src="{{ url_for( 'internal', filename='js/files_transform.js')}}"></script>
<script>
// FIXME: used by viewer code - should probably get rid of this?
var fullscreen=false;
document.fake_shift=0
document.fake_ctrl=0
var move_paths=[]
@@ -17,22 +19,23 @@
{% endfor %}
// GLOBALS
// OPTions set via GUI, will change if we alter drop-downs, etc. in GUI
// TODO: reference these from GUI, so we can finally ditch the form to submit/change them.
// BUT -- must handle noo changing with a form/post as it requires a new ordering
// this is which eid we are viewing an image/video (when we dbl-click & then next/prev)
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.folders="{{OPT.folders}}" === "True"
OPT.howMany={{OPT.how_many}}
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}}'
// this is the list of entry ids for the images for ALL matches for this query
var entryList={{query_data.entry_list}}
@@ -41,6 +44,48 @@
var pageList=[]
// force pageList to set pageList for & render the first page
getPage(1)
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( `&nbsp;${OPT.how_many} files&nbsp;` )
OPT.root_eid=parseInt(OPT.root_eid)
OPT.size=parseInt(OPT.size)
getPage(1)
}
})
}
</script>
<div id="files_div">
@@ -68,13 +113,13 @@
{% endif %}
<div class="col col-auto">
<div class="input-group">
{{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "$('#offset').val(0)", "rounded-start py-2")|safe }}
{{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"])|safe }}
{{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 }}
{% if OPT.folders %}
<input type="hidden" name="grouping" id="grouping" value="{{OPT.grouping}}">
{{CreateFoldersSelect( OPT.folders, "rounded-end" )|safe }}
{{CreateFoldersSelect( OPT.folders, "cFO(); return false", "rounded-end" )|safe }}
{% else %}
{{CreateFoldersSelect( OPT.folders )|safe }}
{{CreateFoldersSelect( OPT.folders, "cFO(); return false" )|safe }}
<span class="sm-txt my-auto btn btn-outline-info disabled border-top border-bottom">grouped by:</span>
{{CreateSelect( "grouping", OPT.grouping, ["None", "Day", "Week", "Month"], "OPT.grouping=$('#grouping').val();drawPageOfFigures();return false", "rounded-end")|safe }}
{% endif %}
@@ -92,7 +137,7 @@
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary disabled" onClick="prevPage()" disabled>
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
</button>
<span class="sm-txt my-auto">&nbsp;{{OPT.how_many}} files&nbsp;</span>
<span class="how_many_text sm-txt my-auto">&nbsp;{{OPT.how_many}} files&nbsp;</span>
<button aria-label="next" id="next" name="next" class="next sm-txt btn btn-outline-secondary" onClick="nextPage()">
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
</button>
@@ -163,7 +208,7 @@
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary disabled" onClick="prevPage()" disabled>
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
</button>
<span class="sm-txt my-auto">&nbsp;{{OPT.how_many}} files&nbsp;</span>
<span class="how_many_text sm-txt my-auto">&nbsp;{{OPT.how_many}} files&nbsp;</span>
<button aria-label="next" id="next" name="next" class="next sm-txt btn btn-outline-secondary" onClick="nextPage()">
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
</button>
@@ -227,11 +272,11 @@
function getPreviousEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.howMany)
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex > 0) {
currentIndex--;
pageOffset=Math.floor(currentIndex / OPT.howMany)
currentIndex=currentIndex-(pageOffset*OPT.howMany)
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// pref page, load it
if( oldPageOffset != pageOffset )
// pref page is pageOffset+1 now
@@ -244,11 +289,11 @@
function getNextEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.howMany)
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex < entryList.length - 1) {
currentIndex++
pageOffset=Math.floor(currentIndex / OPT.howMany)
currentIndex=currentIndex-(pageOffset*OPT.howMany)
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// next page, load it
if( oldPageOffset != pageOffset )
// next page is pageOffset+1 now
@@ -268,9 +313,9 @@
function setEntryById(id) {
var currentIndex = entryList.indexOf(parseInt(id));
// if we are on a different page, adjust as document.entries only has <= howMany
pageOffset=Math.floor(currentIndex / OPT.howMany)
currentIndex = currentIndex-(pageOffset*OPT.howMany)
// if we are on a different page, adjust as document.entries only has <= how_many
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex = currentIndex-(pageOffset*OPT.how_many)
document.viewing=document.entries[currentIndex]
}
@@ -533,7 +578,7 @@ $.contextMenu({
return {
callback: function( key, options) {
if( key == "details" ) { DetailsDBox() }
if( key == "view" ) { CallViewRoute( $(this).attr('id') ) }
if( key == "view" ) { dblClickToViewEntry( $(this).attr('id') ) }
if( key == "move" ) { MoveDBox(move_paths, "{{url_for('internal', filename='icons.svg')}}") }
if( key == "del" ) { DelDBox('Delete') }
if( key == "undel") { DelDBox('Restore') }
@@ -552,15 +597,6 @@ $.contextMenu({
});
$(document).ready(function() {
if( {{OPT.offset}} == 0 )
{
$('.prev').addClass('disabled')
$('.prev').prop('disabled', true)
}
$(".dir").click( function(e) { $('#offset').val(0) ; $('#cwd').val( $(this).attr('dir') ) ; $('#main_form').submit() } )
} )
$( document ).keydown(function(event) {
switch (event.key)
{