Compare commits
5 Commits
175e43c9bb
...
2e952deda0
| Author | SHA1 | Date | |
|---|---|---|---|
| 2e952deda0 | |||
| b9b7a24326 | |||
| a7ce8e66b5 | |||
| 6199c042e3 | |||
| c32b99f53e |
24
TODO
24
TODO
@@ -1,20 +1,8 @@
|
||||
###
|
||||
# get search to work
|
||||
###
|
||||
|
||||
### major fix - go to everywhere I call GetEntries(), and redo the logic totally...
|
||||
* firstly, run the query as per normal, but get just the matched eids into an entry_lst
|
||||
* make a unique query_id for this entry_lst, and store entry_ids into "query" table, with a unique query_id
|
||||
* take most of pa_user_state that relates to query state and move it to the "query" table per query_id
|
||||
* pa_user_state then becomes defaults for next query (so how_many, noo, etc)
|
||||
|
||||
* we can age out queries form the query_table after a few months?
|
||||
* client side always has query_id. IF DB does not have query_id, then its really old? - just say so...
|
||||
|
||||
* client side takes query_id, entry_lst, current_eid, offset, first/last_eid, etc. as part of its first route / html creation.
|
||||
* get this data as a json blob? or ask chatgpt to see how best to take the data and turn it into jscript data
|
||||
* it then decides based on all this to GetEntryDetails( subset of entry_lst ) <- needs new route
|
||||
* IN THEORY some of the subset of entry_lst don't exist -- BUT, we can handle that on response, e.g. say my query used to have 1,2,3, and since then another user/action deleted 2:
|
||||
- I ask for details on 1,2,3 and get back details on 1,3 only.
|
||||
- On client-side, I can say, since you ran this query, data in PA has changed - image#2 is no longer in PA.
|
||||
Please run a new query (or bonus points, maybe have enough of the original query to note this and ask, do you want to ignore changes, or re-run query and get latest data?)
|
||||
* client can go fwd or back in the entry_lst same as now (disabling buttons as needed), BUT as entry_lst is NOT recreated per page move, then no chance to get confused about first/last
|
||||
* client side:
|
||||
* for real chance to stop confusion, instead of removing deleted images from DOM, we should gray them out and put a big Del (red circle with line?) though it as overlay.
|
||||
* Create another table is entry_ammendments - note the deletions, rotations, flips of specific eids - then reproduce that on the client side visually as needed
|
||||
@@ -22,10 +10,6 @@
|
||||
- When job that flips, rotates, deletes completes then lets update the query details (e.g. remove eids, or remove the ammendments)
|
||||
- this actually is quite an improvement, if someone is deleting 2 as per above, I will see that as a pending change in my unrelated query, ditto flips, etc.
|
||||
|
||||
* NEED to work through how we deal with directories when we do the json data versions above?
|
||||
- e.g. does entry_list only contain files? OR filter the details in the jscript?
|
||||
- how do we do dirs in this context? (when folders=True)
|
||||
|
||||
### GENERAL
|
||||
* jobs for AI should show path name
|
||||
* rm dups job should show progress bar
|
||||
|
||||
65
files.py
65
files.py
@@ -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))
|
||||
|
||||
################################################################################
|
||||
|
||||
@@ -473,7 +473,7 @@ function getDirEntries(dir_id, back)
|
||||
document.entries=res
|
||||
// rebuild entryList/pageList as each dir comes with new entries
|
||||
entryList=res.map(obj => obj.id);
|
||||
pageList=entryList.slice(0, OPT.howMany)
|
||||
pageList=entryList.slice(0, OPT.how_many)
|
||||
if( back )
|
||||
document.back_id = res[0].in_dir.eid
|
||||
drawPageOfFigures()
|
||||
@@ -539,8 +539,8 @@ function getPage(pageNumber,viewing_idx=0)
|
||||
// getting another event before we have the data for the page back
|
||||
$('#la').prop('disabled', true)
|
||||
$('#ra').prop('disabled', true)
|
||||
const startIndex = (pageNumber - 1) * OPT.howMany;
|
||||
const endIndex = startIndex + OPT.howMany;
|
||||
const startIndex = (pageNumber - 1) * OPT.how_many;
|
||||
const endIndex = startIndex + OPT.how_many;
|
||||
pageList = entryList.slice(startIndex, endIndex);
|
||||
|
||||
// set up data to send to server to get the entry data for entries in pageList
|
||||
@@ -581,7 +581,7 @@ function isFirstPage(pageNumber)
|
||||
// Function to check if we are on the last page
|
||||
function isLastPage(pageNumber)
|
||||
{
|
||||
const totalPages = Math.ceil(entryList.length / OPT.howMany);
|
||||
const totalPages = Math.ceil(entryList.length / OPT.how_many);
|
||||
return pageNumber >= totalPages;
|
||||
}
|
||||
|
||||
@@ -592,7 +592,7 @@ function getPageNumberForId(id) {
|
||||
if (idx === -1) {
|
||||
return -1; // or null, if you prefer
|
||||
}
|
||||
return Math.floor(idx / OPT.howMany) + 1;
|
||||
return Math.floor(idx / OPT.how_many) + 1;
|
||||
}
|
||||
|
||||
// if we are on first page, disable prev, it not ensure next is enabled
|
||||
@@ -618,7 +618,7 @@ function nextPage()
|
||||
// should never happen / just return pageList unchanged
|
||||
if ( currentPage === -1 || isLastPage( currentPage ) )
|
||||
{
|
||||
console.log( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + OPT.howMany + " gives currentPage=" + currentPage + " and we cant go next page?" )
|
||||
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 )
|
||||
@@ -634,7 +634,7 @@ function prevPage()
|
||||
// should never happen / just return pageList unchanged
|
||||
if (currentPage === 1 || currentPage === -1 )
|
||||
{
|
||||
console.log( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + OPT.howMany + " gives currentPage=" + currentPage + " and we cant go prev page?" )
|
||||
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 )
|
||||
|
||||
@@ -93,8 +93,8 @@ def CreateSelect(name, selected, list, js="", add_class="", vals={} ):
|
||||
# TODO: can this be collapsed into using above - probably if the 'selected' passed in was 'In Folder' or 'Flat View' -- but I think that isn't in a var???
|
||||
# Helper function used in html files to create a bootstrap'd select with options. Same as CreateSelect() really, only contains
|
||||
# hard-coded True/False around the if selected part, but with string based "True"/"False" in the vals={}, and list has "In Folders", "Flat View"
|
||||
def CreateFoldersSelect(selected, add_class=""):
|
||||
str = f'<select id="folders" name="folders" class="{add_class} sm-txt bg-white text-info border-info border-1 p-1" onChange="this.form.submit()">'
|
||||
def CreateFoldersSelect(selected, js="", add_class=""):
|
||||
str = f'<select id="folders" name="folders" class="{add_class} sm-txt bg-white text-info border-info border-1 p-1" onChange="{js};this.form.submit()">'
|
||||
# if selected is true, then folders == true, so make this the selected option
|
||||
if( selected ):
|
||||
str += '<option selected value="True">In Folders</option>'
|
||||
|
||||
293
states.py
293
states.py
@@ -52,274 +52,51 @@ class PA_UserState(db.Model):
|
||||
################################################################################
|
||||
class States(PA):
|
||||
def __init__(self, request):
|
||||
self.path_type=''
|
||||
self.orig_search_term = ''
|
||||
self.url = request.path
|
||||
self.view_eid = None
|
||||
self.current=0
|
||||
self.first_eid=0
|
||||
self.last_eid=0
|
||||
self.num_entries=0
|
||||
self.prefix=None
|
||||
|
||||
# this is any next/prev or noo, grouping, etc. change (so use referrer to work out what to do with this)
|
||||
# because this can happen on a view, or files_up, etc. change this FIRST
|
||||
if 'change_file_opts' in request.path:
|
||||
base=request.base_url
|
||||
base=base.replace("change_file_opts", "")
|
||||
self.url = "/"+request.referrer.replace(base, "" )
|
||||
|
||||
# if view_list, then we really are a view, and view_eid should be in the form
|
||||
if 'view_list' in request.path:
|
||||
self.path_type = 'View'
|
||||
self.view_eid = request.form['view_eid']
|
||||
self.url = request.form['orig_url']
|
||||
# this occurs ONLY when a POST to /view/<id> occurs (at this stage orig_url will be from an import, storage, bin or search)
|
||||
elif 'view' in request.path:
|
||||
self.path_type = 'View'
|
||||
self.view_eid = self.url[6:]
|
||||
# use orig url to define defaults/look up states for 'last' import/storage/bin/search
|
||||
if request.method == "POST":
|
||||
self.url = request.form['orig_url']
|
||||
else:
|
||||
# GET's occur on redirect, and we don't have a form, so get it from pref
|
||||
st=self.url[8:]
|
||||
if request.referrer and 'search' in request.referrer:
|
||||
st=re.sub( '.+/search/', '', request.referrer )
|
||||
else:
|
||||
st=''
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.view_eid==self.view_eid,PA_UserState.orig_search_term==st).first()
|
||||
if not pref:
|
||||
SetFELog( message=f"ERROR: pref not found - dn={current_user.dn}, st={st}, s={self}????" , level="danger", persistent=True, cant_close=False )
|
||||
SetFELog( message=f"WARNING: I think this error occurred because you reloaded a page and the server had restarted between your original page load and this page reload, is that possible?" , level="warning", persistent=True, cant_close=False )
|
||||
redirect("/")
|
||||
else:
|
||||
if not hasattr( pref, 'orig_url' ):
|
||||
SetFELog( message=f"ERROR: orig_url not in pref - dn={current_user.dn}, st={st}, self={self}, pref={pref}????" , level="danger", persistent=True, cant_close=True )
|
||||
redirect("/")
|
||||
self.url = pref.orig_url
|
||||
|
||||
if 'files_ip' in self.url or 'file_list_ip' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Import'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Import'
|
||||
elif 'files_sp' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Storage'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Storage'
|
||||
elif 'files_rbp' in self.url:
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Bin'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Bin'
|
||||
elif 'search' in self.url:
|
||||
# okay if we are a search, but came from a view then get last_search_state form prefs and use it
|
||||
m=re.match( '.*search/(.+)$', self.url )
|
||||
if m == None:
|
||||
SetFELog( message=f"ERROR: DDP messed up, seems we are processing a search, but cant see the search term - is this even possible?" )
|
||||
return
|
||||
self.orig_search_term = m[1]
|
||||
if self.path_type == "View":
|
||||
self.orig_ptype = 'Search'
|
||||
self.orig_url = self.url
|
||||
else:
|
||||
self.path_type = 'Search'
|
||||
elif 'view' in self.url:
|
||||
# use url to get eid of viewed entry
|
||||
self.view_eid = self.url[6:]
|
||||
|
||||
# force this to be a search so rest of code won't totally die, but also not return anything
|
||||
self.path_type="Search"
|
||||
self.orig_url=self.url
|
||||
elif 'change_file_opts' not in self.url:
|
||||
SetFELog( message=f"ERROR: DDP messed up, failed to match URL {self.url} for settings this will fail, redirecting to home" , level="danger", persistent=True, cant_close=True )
|
||||
SetFELog( message=f"referrer={request.referrer}" , level="danger", persistent=True, cant_close=True )
|
||||
return
|
||||
|
||||
if self.path_type == 'View':
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.view_eid==self.view_eid,PA_UserState.orig_search_term==self.orig_search_term).first()
|
||||
if not hasattr( self, 'orig_ptype' ):
|
||||
self.orig_ptype='View'
|
||||
self.orig_url=''
|
||||
SetFELog( message=f"ERROR: No orig ptype? s={self} - pref={pref}, redirecting to home" , level="danger", persistent=True, cant_close=True )
|
||||
SetFELog( message=f"referrer={request.referrer}" , level="danger", persistent=True, cant_close=True )
|
||||
redirect("/")
|
||||
|
||||
# should find original path or search for this view (if not a search, search_term='')
|
||||
orig_pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.orig_ptype,PA_UserState.orig_search_term==self.orig_search_term).first()
|
||||
if not orig_pref:
|
||||
SetFELog( message=f"ERROR: DDP messed up 2, failed to find orig_pref for a view pt={self.path_type} for search={self.orig_search_term}" , level="danger", persistent=True, cant_close=True )
|
||||
SetFELog( message=f"referrer={request.referrer}" , level="danger", persistent=True, cant_close=True )
|
||||
return
|
||||
elif self.path_type == 'Search':
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type,PA_UserState.orig_search_term==self.orig_search_term).first()
|
||||
else:
|
||||
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==self.path_type).first()
|
||||
|
||||
if pref:
|
||||
self.grouping=pref.grouping
|
||||
self.how_many=pref.how_many
|
||||
self.offset=pref.st_offset
|
||||
self.size=pref.size
|
||||
self.cwd=pref.cwd
|
||||
self.orig_ptype=pref.orig_ptype
|
||||
self.orig_search_term=pref.orig_search_term
|
||||
self.orig_url = pref.orig_url
|
||||
self.view_eid = pref.view_eid
|
||||
self.current = pref.current
|
||||
if self.path_type == "View":
|
||||
self.root='static/' + self.orig_ptype
|
||||
self.first_eid=orig_pref.first_eid
|
||||
self.last_eid=orig_pref.last_eid
|
||||
self.num_entries=orig_pref.num_entries
|
||||
self.noo=orig_pref.noo
|
||||
self.folders=orig_pref.folders
|
||||
self.orig_search_term=orig_pref.orig_search_term
|
||||
else:
|
||||
self.root=pref.root
|
||||
self.first_eid = pref.first_eid
|
||||
self.last_eid = pref.last_eid
|
||||
self.num_entries = pref.num_entries
|
||||
self.noo=pref.noo
|
||||
self.folders=pref.folders
|
||||
else:
|
||||
# retreive defaults from 'PAUser' where defaults are stored
|
||||
u=PAUser.query.filter(PAUser.dn==current_user.dn).one()
|
||||
self.grouping=u.default_grouping
|
||||
self.how_many=u.default_how_many
|
||||
self.offset=0
|
||||
self.size=u.default_size
|
||||
if self.path_type == "View":
|
||||
self.root='static/' + self.orig_ptype
|
||||
self.first_eid=orig_pref.first_eid
|
||||
self.last_eid=orig_pref.last_eid
|
||||
self.num_entries=orig_pref.num_entries
|
||||
self.noo=orig_pref.noo
|
||||
self.folders=orig_pref.folders
|
||||
self.orig_search_term=orig_pref.orig_search_term
|
||||
else:
|
||||
self.root='static/' + self.path_type
|
||||
if self.path_type == 'Import':
|
||||
self.noo = u.default_import_noo
|
||||
self.folders = u.default_import_folders
|
||||
elif self.path_type == 'Storage':
|
||||
self.noo = u.default_storage_noo
|
||||
self.folders = u.default_storage_folders
|
||||
else:
|
||||
# search so force folders to be false (rather see images, # than series of folders that dont match search themselves)
|
||||
self.noo=u.default_search_noo
|
||||
self.folders=False
|
||||
|
||||
self.cwd=self.root
|
||||
if not hasattr(self, 'orig_ptype'):
|
||||
self.orig_ptype=None
|
||||
if not hasattr(self, 'orig_search_term'):
|
||||
self.orig_search_term=None
|
||||
self.orig_url = self.url
|
||||
|
||||
# the above are defaults, if we are here, then we have current values, use them instead if they are set -- AI: searches dont set them so then we use those in the DB first
|
||||
if request.method=="POST":
|
||||
if self.path_type != "View" and 'noo' in request.form:
|
||||
# we are changing values based on a POST to the form, if we changed the noo option, we need to reset things
|
||||
if 'change_file_opts' in request.path and self.noo != request.form['noo']:
|
||||
self.noo=request.form['noo']
|
||||
self.first_eid=0
|
||||
self.last_eid=0
|
||||
self.offset=0
|
||||
if 'how_many' in request.form:
|
||||
self.how_many=request.form['how_many']
|
||||
if 'offset' in request.form:
|
||||
self.offset=int(request.form['offset'])
|
||||
if 'grouping' in request.form:
|
||||
self.grouping=request.form['grouping']
|
||||
# this can be null if we come from view by details
|
||||
if 'size' in request.form:
|
||||
self.size = request.form['size']
|
||||
# seems html cant do boolean, but uses strings so convert
|
||||
if self.path_type != "View" and 'folders' in request.form:
|
||||
# we are changing values based on a POST to the form, if we are in folder view and we changed the folders option, we need to reset things
|
||||
if 'change_file_opts' in request.path:
|
||||
if self.folders and self.folders != request.form['folders']:
|
||||
self.num_entries=0
|
||||
self.first_eid=0
|
||||
self.last_eid=0
|
||||
if request.form['folders'] == "False":
|
||||
self.folders=False
|
||||
else:
|
||||
self.folders=True
|
||||
# have to force grouping to None if we flick to folders from a flat view with grouping (otherwise we print out
|
||||
# group headings for child content that is not in the CWD)
|
||||
self.grouping=None
|
||||
if 'orig_url' in request.form:
|
||||
self.orig_url = request.form['orig_url']
|
||||
|
||||
# possible to not be set for an AI: search
|
||||
if 'cwd' in request.form:
|
||||
self.cwd = request.form['cwd']
|
||||
if 'prev' in request.form:
|
||||
self.offset -= int(self.how_many)
|
||||
# just in case we hit prev too fast, stop this...
|
||||
if self.offset < 0:
|
||||
self.offset=0
|
||||
if 'next' in request.form:
|
||||
if (self.offset + int(self.how_many)) < self.num_entries:
|
||||
self.offset += int(self.how_many)
|
||||
else:
|
||||
# tripping this still
|
||||
SetFELog( message=f"WARNING: next image requested, but would go past end of list? - ignore this" , level="warning", persistent=True, cant_close=False )
|
||||
if 'current' in request.form:
|
||||
self.current = int(request.form['current'])
|
||||
|
||||
last_used=datetime.now(pytz.utc)
|
||||
|
||||
# set the prefix based on path
|
||||
path=None
|
||||
if self.path_type == 'Storage':
|
||||
path = SettingsSPath()
|
||||
elif self.path_type == 'Import':
|
||||
if 'files_ip' in self.url or 'file_list_ip' in self.url:
|
||||
self.path_type = 'Import'
|
||||
path = SettingsIPath()
|
||||
elif self.path_type == 'Bin':
|
||||
elif 'files_sp' in self.url:
|
||||
self.path_type = 'Storage'
|
||||
path = SettingsSPath()
|
||||
elif 'files_rbp' in self.url:
|
||||
self.path_type = 'Bin'
|
||||
path = SettingsRBPath()
|
||||
elif 'search' in self.url:
|
||||
self.path_type = 'Search'
|
||||
self.search_term = ''
|
||||
else:
|
||||
self.path_type=''
|
||||
|
||||
if path:
|
||||
self.prefix = SymlinkName(self.path_type,path,path+'/')
|
||||
|
||||
# now save pref
|
||||
if not pref:
|
||||
# insert new pref for this combo (might be a new search or view, or first time for a path)
|
||||
pref=PA_UserState( pa_user_dn=current_user.dn, last_used=last_used, path_type=self.path_type, view_eid=self.view_eid,
|
||||
noo=self.noo, grouping=self.grouping, how_many=self.how_many, st_offset=self.offset, size=self.size,
|
||||
folders=self.folders, root=self.root, cwd=self.cwd, orig_ptype=self.orig_ptype, orig_search_term=self.orig_search_term,
|
||||
orig_url=self.orig_url, current=self.current, first_eid=self.first_eid, last_eid=self.last_eid, num_entries=self.num_entries )
|
||||
else:
|
||||
# update this pref with the values calculated above (most likely from POST to form)
|
||||
pref.pa_user_dn=current_user.dn
|
||||
pref.path_type=self.path_type
|
||||
pref.view_eid=self.view_eid
|
||||
pref.noo=self.noo
|
||||
pref.grouping=self.grouping
|
||||
pref.how_many=self.how_many
|
||||
pref.st_offset=self.offset
|
||||
pref.size=self.size
|
||||
pref.folders=self.folders
|
||||
pref.root = self.root
|
||||
pref.cwd = self.cwd
|
||||
pref.orig_ptype = self.orig_ptype
|
||||
pref.orig_search_term = self.orig_search_term
|
||||
pref.orig_url = self.orig_url
|
||||
pref.last_used = last_used
|
||||
pref.first_eid = self.first_eid
|
||||
pref.last_eid = self.last_eid
|
||||
pref.num_entries = self.num_entries
|
||||
# only passed in (at the moment) in view_list
|
||||
pref.current = self.current
|
||||
self.prefix=None
|
||||
|
||||
db.session.add(pref)
|
||||
db.session.commit()
|
||||
# retreive defaults from 'PAUser' where defaults are stored
|
||||
u=PAUser.query.filter(PAUser.dn==current_user.dn).one()
|
||||
self.grouping=u.default_grouping
|
||||
self.how_many=u.default_how_many
|
||||
self.size=u.default_size
|
||||
self.root='static/' + self.path_type
|
||||
if self.path_type == 'Import':
|
||||
self.noo = u.default_import_noo
|
||||
self.folders = u.default_import_folders
|
||||
elif self.path_type == 'Storage':
|
||||
self.noo = u.default_storage_noo
|
||||
self.folders = u.default_storage_folders
|
||||
else:
|
||||
# search so force folders to be false (rather see images, # than series of folders that dont match search themselves)
|
||||
self.noo=u.default_search_noo
|
||||
self.folders=False
|
||||
|
||||
self.default_flat_noo=u.default_import_noo
|
||||
self.default_folder_noo=u.default_storage_noo
|
||||
self.default_search_noo=u.default_search_noo
|
||||
self.cwd=self.root
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -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( ` ${OPT.how_many} files ` )
|
||||
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"> {{OPT.how_many}} files </span>
|
||||
<span class="how_many_text sm-txt my-auto"> {{OPT.how_many}} files </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"> {{OPT.how_many}} files </span>
|
||||
<span class="how_many_text sm-txt my-auto"> {{OPT.how_many}} files </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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user