from flask import request, render_template, redirect, url_for from flask_login import login_required, current_user from main import db, app, ma from shared import PA from user import PAUser from datetime import datetime from job import SetFELog import pytz import re ################################################################################ # PA_UserState: preference data for a given user / path_type combo, so a given user # and their prefs for say the import path(s) and storage path(s) etc, each # path_type has different defaults, and keeping those works better ################################################################################ class PA_UserState(db.Model): __tablename__ = "pa_user_state" id = db.Column(db.Integer, db.Sequence('pa_user_state_id_seq'), primary_key=True ) pa_user_dn = db.Column(db.String, db.ForeignKey('pa_user.dn'), primary_key=True ) last_used = db.Column(db.DateTime(timezone=True)) path_type = db.Column(db.String, primary_key=True, unique=False, nullable=False ) noo = db.Column(db.String, unique=False, nullable=False ) grouping = db.Column(db.String, unique=False, nullable=False ) how_many = db.Column(db.Integer, unique=False, nullable=False ) st_offset = db.Column(db.Integer, unique=False, nullable=False ) size = db.Column(db.Integer, unique=False, nullable=False ) folders = db.Column(db.Boolean, unique=False, nullable=False ) root = db.Column(db.String, unique=False, nullable=False ) cwd = db.Column(db.String, unique=False, nullable=False ) ## for now being lazy and not doing a separate table until I settle on needed fields and when # only used if ptype == View view_eid = db.Column(db.Integer, unique=False, nullable=False ) orig_ptype = db.Column(db.String, unique=False, nullable=False ) # only used if view and orig_ptype was search orig_search_term = db.Column(db.String, unique=False, nullable=False ) orig_url = db.Column(db.String, unique=False, nullable=False ) current = db.Column(db.Integer) first_eid = db.Column(db.Integer) last_eid = db.Column(db.Integer) num_entries = db.Column(db.Integer) def __repr__(self): return f"" ################################################################################ # States: class to store set of default values for viewing (order/size, etc.) # and if a request object (from a POST) is passed in, it returns those instead # it also handles the cwd appropriately, paths, search, etc. ################################################################################ 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 # 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/ 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=True ) 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) # 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 db.session.add(pref) db.session.commit() return ################################################################################ # /states -> GET only -> prints out list of all prefs (simple for now) ################################################################################ @app.route("/states", methods=["GET"]) @login_required def states(): user = PAUser.query.filter( PAUser.dn==current_user.dn ).one() states = PA_UserState.query.filter( PA_UserState.pa_user_dn==current_user.dn ).all() return render_template("states.html", user=user, states=states )