Compare commits
10 Commits
master
...
be218c5049
| Author | SHA1 | Date | |
|---|---|---|---|
| be218c5049 | |||
| a28f016b8a | |||
| d2db7f6184 | |||
| 9ec8195d0a | |||
| 2325dcd22a | |||
| e0b597c58c | |||
| 0895268df2 | |||
| efceef7e57 | |||
| 21059a6235 | |||
| 4d80fa4e7c |
3
BUGs
3
BUGs
@@ -1,4 +1,5 @@
|
|||||||
### Next: 140
|
### Next: 141
|
||||||
|
BUG-140: When db is restarted underneath PA, it crashes job mgr... It should just accept timeouts, and keep trying to reconnect every 2? mins
|
||||||
BUG-139: using any large entry list and going next a few times, ends say 4 pages of 50 into 4000 matches (entries from DB < 50)...
|
BUG-139: using any large entry list and going next a few times, ends say 4 pages of 50 into 4000 matches (entries from DB < 50)...
|
||||||
- confirmed this is when person has 2 or more refimgs:
|
- confirmed this is when person has 2 or more refimgs:
|
||||||
- on page "2", we get 49 pulled back in the ORM instead of the 50 expected -- b/c I use that to indicate we must be at the end of the list if not 50 found
|
- on page "2", we get 49 pulled back in the ORM instead of the 50 expected -- b/c I use that to indicate we must be at the end of the list if not 50 found
|
||||||
|
|||||||
5
TODO
5
TODO
@@ -8,6 +8,7 @@
|
|||||||
* client side always has query_id. IF DB does not have query_id, then its really old? - just say so...
|
* 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.
|
* 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
|
* 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:
|
* 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.
|
- I ask for details on 1,2,3 and get back details on 1,3 only.
|
||||||
@@ -21,6 +22,10 @@
|
|||||||
- When job that flips, rotates, deletes completes then lets update the query details (e.g. remove eids, or remove the ammendments)
|
- 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.
|
- 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
|
### GENERAL
|
||||||
* jobs for AI should show path name
|
* jobs for AI should show path name
|
||||||
* rm dups job should show progress bar
|
* rm dups job should show progress bar
|
||||||
|
|||||||
126
files.py
126
files.py
@@ -1,11 +1,11 @@
|
|||||||
from wtforms import SubmitField, StringField, HiddenField, validators, Form
|
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask import request, render_template, redirect, send_from_directory, url_for, jsonify, make_response
|
from flask import request, render_template, redirect, send_from_directory, url_for, jsonify, make_response
|
||||||
from main import db, app, ma
|
from main import db, app, ma
|
||||||
from sqlalchemy import Sequence, text
|
from sqlalchemy import Sequence, text, select
|
||||||
from sqlalchemy.exc import SQLAlchemyError
|
from sqlalchemy.exc import SQLAlchemyError
|
||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
|
import json
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pymediainfo import MediaInfo
|
from pymediainfo import MediaInfo
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -20,8 +20,8 @@ import pytz
|
|||||||
import html
|
import html
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from states import States, PA_UserState
|
from states import States, PA_UserState
|
||||||
|
from query import Query
|
||||||
|
|
||||||
################################################################################
|
|
||||||
# Local Class imports
|
# Local Class imports
|
||||||
################################################################################
|
################################################################################
|
||||||
from job import Job, JobExtra, Joblog, NewJob, SetFELog
|
from job import Job, JobExtra, Joblog, NewJob, SetFELog
|
||||||
@@ -119,7 +119,7 @@ class File(db.Model):
|
|||||||
eid = db.Column(db.Integer, db.ForeignKey("entry.id"), primary_key=True )
|
eid = db.Column(db.Integer, db.ForeignKey("entry.id"), primary_key=True )
|
||||||
size_mb = db.Column(db.Integer, unique=False, nullable=False)
|
size_mb = db.Column(db.Integer, unique=False, nullable=False)
|
||||||
thumbnail = db.Column(db.String, unique=False, nullable=True)
|
thumbnail = db.Column(db.String, unique=False, nullable=True)
|
||||||
hash = db.Column(db.Integer)
|
hash = db.Column(db.String)
|
||||||
year = db.Column(db.Integer)
|
year = db.Column(db.Integer)
|
||||||
month = db.Column(db.Integer)
|
month = db.Column(db.Integer)
|
||||||
day = db.Column(db.Integer)
|
day = db.Column(db.Integer)
|
||||||
@@ -141,6 +141,61 @@ class FileType(db.Model):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<id: {self.id}, name={self.name}>"
|
return f"<id: {self.id}, name={self.name}>"
|
||||||
|
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# this is how we order all queries based on value of 'noo' - used with
|
||||||
|
# access *order_map.get(OPT.noo)
|
||||||
|
################################################################################
|
||||||
|
order_map = {
|
||||||
|
"Newest": (File.year.desc(),File.month.desc(),File.day.desc(),Entry.name.desc()),
|
||||||
|
"Oldest": (File.year,File.month,File.day,Entry.name),
|
||||||
|
# careful, these need to be tuples, so with a , at the end
|
||||||
|
"Z to A": (Entry.name.desc(),),
|
||||||
|
"A to Z": (Entry.name.asc(),),
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Schemas for Path, FileType, File, Dir - used in EntrySchema
|
||||||
|
################################################################################
|
||||||
|
class PathType(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta: model = PathType
|
||||||
|
load_instance = True
|
||||||
|
|
||||||
|
class PathSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta: model = Path
|
||||||
|
load_instance = True
|
||||||
|
type = ma.Nested(PathType)
|
||||||
|
|
||||||
|
class FileTypeSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta: model = FileType
|
||||||
|
load_instance = True
|
||||||
|
|
||||||
|
class FileSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta: model = File
|
||||||
|
load_instance = True
|
||||||
|
|
||||||
|
class DirSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta: model = Dir
|
||||||
|
load_instance = True
|
||||||
|
in_path = ma.Nested(PathSchema)
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Schema for Entry so we can json for data to the client
|
||||||
|
################################################################################
|
||||||
|
class EntrySchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
# gives id, name, type_id
|
||||||
|
class Meta: model = Entry
|
||||||
|
load_instance = True
|
||||||
|
|
||||||
|
type = ma.Nested(FileTypeSchema)
|
||||||
|
file_details = ma.Nested(FileSchema)
|
||||||
|
# noting dir_details needs in_path to work
|
||||||
|
dir_details = ma.Nested(DirSchema)
|
||||||
|
# noting in_dir needs in_path and in_path.type to work
|
||||||
|
in_dir = ma.Nested(DirSchema)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# util function to just update the current/first/last positions needed for
|
# util function to just update the current/first/last positions needed for
|
||||||
# viewing / using pa_user_state DB table
|
# viewing / using pa_user_state DB table
|
||||||
@@ -327,9 +382,66 @@ def SetOrderStrings( OPT ):
|
|||||||
OPT.last_order_raw=f"e.name desc"
|
OPT.last_order_raw=f"e.name desc"
|
||||||
return
|
return
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# /get_entries_by_ids -> route where we supply list of entry ids (for next/prev
|
||||||
|
# page of data we want to show). Returns json of all matching entries
|
||||||
|
################################################################################
|
||||||
|
@app.route('/get_entries_by_ids', methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
def process_ids():
|
||||||
|
data = request.get_json() # Parse JSON body
|
||||||
|
ids = data.get('ids', []) # Extract list of ids
|
||||||
|
|
||||||
|
# DDP: debate here, do I get query_id, do I validate whether we are asking
|
||||||
|
# for ids not in the query? OR, dont even make/store/have query?
|
||||||
|
|
||||||
|
# marshmallow will allow us to json the data the way we need for the client
|
||||||
|
entries_schema = EntrySchema(many=True)
|
||||||
|
|
||||||
|
# Query DB for matching entries
|
||||||
|
entries = Entry.query.filter(Entry.id.in_(ids)).all()
|
||||||
|
|
||||||
|
# return entries as json
|
||||||
|
return jsonify(entries_schema.dump(entries))
|
||||||
|
|
||||||
|
###
|
||||||
|
# query_data = { 'entry_lst': entry_lst, 'query_id': query_id, ... }
|
||||||
|
###
|
||||||
|
# Call this ONCE on first menu choice of View files, or search box submission
|
||||||
|
def GetQueryData( OPT ):
|
||||||
|
query_data = {}
|
||||||
|
query_data['query_id']=None
|
||||||
|
query_data['entry_list']=None
|
||||||
|
|
||||||
|
# set up the sql order strings (back in OPT) based on value of noo
|
||||||
|
# FIXME: remove this for all last/first eid usage AND use order_map
|
||||||
|
SetOrderStrings( OPT )
|
||||||
|
|
||||||
|
if OPT.path_type == 'Search':
|
||||||
|
print ("NOT YET")
|
||||||
|
return query_data
|
||||||
|
|
||||||
|
if OPT.folders:
|
||||||
|
entries, tmp_num_ents = GetEntriesInFolderView( OPT, prefix )
|
||||||
|
else:
|
||||||
|
stmt = ( select(Entry.id).join(File).join(EntryDirLink).join(Dir).join(PathDirLink).
|
||||||
|
join(Path).filter(Path.path_prefix == OPT.prefix) )
|
||||||
|
stmt = stmt.order_by(*order_map.get(OPT.noo) )
|
||||||
|
query_data['entry_list']= db.session.execute(stmt).scalars().all()
|
||||||
|
|
||||||
|
# first time we get the data q_offset is 0, current=first one, search never gets here, so search_term=''
|
||||||
|
# FIXME: Doubt we need cwd -- I only need originals to either invalidate this list, or recreate it... need to think about that a lot more
|
||||||
|
query = Query( path_type=OPT.path_type, noo=OPT.noo, q_offset=0, folder=OPT.folders, grouping=OPT.grouping, root=OPT.root, cwd=OPT.cwd, search_term='',
|
||||||
|
entry_list=query_data['entry_list'], current=query_data['entry_list'][0], created=datetime.now(pytz.utc) )
|
||||||
|
db.session.add(query)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
query_data['query_id']=query.id
|
||||||
|
return query_data
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /GetEntries -> helper function that Gets Entries for required files to show
|
# /GetEntries -> helper function that Gets Entries for required files to show
|
||||||
# for several routes (files_ip, files_sp, files_rbp, search, view_list)
|
# for several routes (ifles_ip, files_sp, files_rbp, search, view_list)
|
||||||
################################################################################
|
################################################################################
|
||||||
def GetEntries( OPT ):
|
def GetEntries( OPT ):
|
||||||
entries=[]
|
entries=[]
|
||||||
@@ -374,6 +486,7 @@ def GetEntries( OPT ):
|
|||||||
OPT.num_entries=num_entries
|
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()
|
pref=PA_UserState.query.filter(PA_UserState.pa_user_dn==current_user.dn,PA_UserState.path_type==OPT.path_type).first()
|
||||||
UpdatePref( pref, OPT )
|
UpdatePref( pref, OPT )
|
||||||
|
|
||||||
return entries
|
return entries
|
||||||
|
|
||||||
@app.route("/change_file_opts", methods=["POST"])
|
@app.route("/change_file_opts", methods=["POST"])
|
||||||
@@ -409,7 +522,8 @@ def files_ip():
|
|||||||
entries=GetEntries( OPT )
|
entries=GetEntries( OPT )
|
||||||
people = Person.query.all()
|
people = Person.query.all()
|
||||||
move_paths = MovePathDetails()
|
move_paths = MovePathDetails()
|
||||||
return render_template("files.html", page_title=f"View Files ({OPT.path_type} Path)", entry_data=entries, OPT=OPT, people=people, move_paths=move_paths )
|
query_data = GetQueryData( OPT )
|
||||||
|
return render_template("files.html", page_title=f"View Files ({OPT.path_type} Path)", entry_data=entries, OPT=OPT, people=people, move_paths=move_paths, query_data=query_data )
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# /files -> show thumbnail view of files from storage_path
|
# /files -> show thumbnail view of files from storage_path
|
||||||
|
|||||||
@@ -316,3 +316,90 @@ function NoSel() {
|
|||||||
else
|
else
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePageOfData()
|
||||||
|
{
|
||||||
|
// FIXME: this should get back a json'd array of entries, and I can/should
|
||||||
|
// use this to redraw the figures dynamically on the page
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to get the 'page' of entry ids out of entryList
|
||||||
|
function getPage(pageNumber)
|
||||||
|
{
|
||||||
|
const startIndex = (pageNumber - 1) * howMany;
|
||||||
|
const endIndex = startIndex + howMany;
|
||||||
|
pageList = entryList.slice(startIndex, endIndex);
|
||||||
|
// FIXME: should POST here to get the data for new pl
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quick Function to check if we are on the first page
|
||||||
|
function isFirstPage(pageNumber)
|
||||||
|
{
|
||||||
|
return pageNumber <= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if we are on the last page
|
||||||
|
function isLastPage(pageNumber)
|
||||||
|
{
|
||||||
|
const totalPages = Math.ceil(entryList.length / howMany);
|
||||||
|
return pageNumber >= totalPages;
|
||||||
|
}
|
||||||
|
|
||||||
|
// given an id in the list, return which page we are on (page 1 is first page)
|
||||||
|
function getPageNumberForId(id) {
|
||||||
|
const idx = entryList.indexOf(id);
|
||||||
|
// should be impossible but jic
|
||||||
|
if (idx === -1) {
|
||||||
|
return -1; // or null, if you prefer
|
||||||
|
}
|
||||||
|
return Math.floor(idx / howMany) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are on first page, disable prev, it not ensure next is enabled
|
||||||
|
// if we are on last page, disable next, it not ensure prev is enabled
|
||||||
|
function resetNextPrevButtons()
|
||||||
|
{
|
||||||
|
if ( isFirstPage( getPageNumberForId(pageList[0]) ) )
|
||||||
|
$('.prev').prop('disabled', true).addClass('disabled');
|
||||||
|
else
|
||||||
|
$('.prev').prop('disabled', false).removeClass('disabled');
|
||||||
|
|
||||||
|
if ( isLastPage( getPageNumberForId(pageList[0]) ) )
|
||||||
|
$('.next').prop('disabled', true).addClass('disabled');
|
||||||
|
else
|
||||||
|
$('.next').prop('disabled', false).removeClass('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
// get list of eids for the next page, also make sure next/prev buttons make sense for page we are on
|
||||||
|
function nextPage()
|
||||||
|
{
|
||||||
|
// pageList[0] is the first entry on this page
|
||||||
|
const currentPage=getPageNumberForId( pageList[0] )
|
||||||
|
// should never happen / just return pageList unchanged
|
||||||
|
if ( currentPage === -1 || isLastPage( currentPage ) )
|
||||||
|
{
|
||||||
|
console.log( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + howMany + " gives currentPage=" + currentPage + " and we cant go next page?" )
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getPage( currentPage+1 )
|
||||||
|
resetNextPrevButtons()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// get list of eids for the prev page, also make sure next/prev buttons make sense for page we are on
|
||||||
|
function prevPage()
|
||||||
|
{
|
||||||
|
// pageList[0] is the first entry on this page
|
||||||
|
const currentPage=getPageNumberForId( pageList[0] )
|
||||||
|
// should never happen / just return pageList unchanged
|
||||||
|
if (currentPage === 1 || currentPage === -1 )
|
||||||
|
{
|
||||||
|
console.log( "WARNING: seems first on pg=" + firstEntryOnPage + " of how many=" + howMany + " gives currentPage=" + currentPage + " and we cant go prev page?" )
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getPage( currentPage-1 )
|
||||||
|
resetNextPrevButtons()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
41
query.py
Normal file
41
query.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from flask_login import UserMixin, login_required
|
||||||
|
from main import db
|
||||||
|
#from sqlalchemy import Sequence
|
||||||
|
#from flask import request, redirect, make_response, jsonify
|
||||||
|
#from main import db, app, ma
|
||||||
|
#from sqlalchemy.exc import SQLAlchemyError
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
|
||||||
|
################################################################################
|
||||||
|
# Class describing Person in the database and DB via sqlalchemy
|
||||||
|
# id is unique id in DB
|
||||||
|
# dn is ldap distinguised name
|
||||||
|
# any entry in this DB is effectively a record you already authed successfully
|
||||||
|
# so acts as a session marker. If you fail ldap auth, you dont get a row here
|
||||||
|
################################################################################
|
||||||
|
class Query(UserMixin,db.Model):
|
||||||
|
__tablename__ = "query"
|
||||||
|
id = db.Column(db.Integer, db.Sequence('query_id_seq'), primary_key=True)
|
||||||
|
path_type = db.Column(db.String)
|
||||||
|
noo = db.Column(db.String)
|
||||||
|
grouping = db.Column(db.String)
|
||||||
|
q_offset = db.Column(db.Integer)
|
||||||
|
folder = db.Column(db.Boolean)
|
||||||
|
entry_list = db.Column(db.String)
|
||||||
|
root = db.Column(db.String)
|
||||||
|
cwd = db.Column(db.String)
|
||||||
|
search_term = db.Column(db.String)
|
||||||
|
current = db.Column(db.Integer)
|
||||||
|
created = db.Column(db.DateTime(timezone=True))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
str=f"<{self.__class__.__name__}("
|
||||||
|
for k, v in self.__dict__.items():
|
||||||
|
str += f"{k}={v!r}, "
|
||||||
|
str=str.rstrip(", ") + ")>"
|
||||||
|
return str
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.dn
|
||||||
15
states.py
15
states.py
@@ -1,10 +1,12 @@
|
|||||||
from flask import request, render_template, redirect, url_for
|
from flask import request, render_template, redirect, url_for
|
||||||
|
from settings import Settings, SettingsIPath, SettingsSPath, SettingsRBPath
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from main import db, app, ma
|
from main import db, app, ma
|
||||||
from shared import PA
|
from shared import PA
|
||||||
from user import PAUser
|
from user import PAUser
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from job import SetFELog
|
from job import SetFELog
|
||||||
|
from shared import SymlinkName
|
||||||
import pytz
|
import pytz
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -58,6 +60,7 @@ class States(PA):
|
|||||||
self.first_eid=0
|
self.first_eid=0
|
||||||
self.last_eid=0
|
self.last_eid=0
|
||||||
self.num_entries=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)
|
# 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
|
# because this can happen on a view, or files_up, etc. change this FIRST
|
||||||
@@ -273,6 +276,18 @@ class States(PA):
|
|||||||
self.current = int(request.form['current'])
|
self.current = int(request.form['current'])
|
||||||
|
|
||||||
last_used=datetime.now(pytz.utc)
|
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':
|
||||||
|
path = SettingsIPath()
|
||||||
|
elif self.path_type == 'Bin':
|
||||||
|
path = SettingsRBPath()
|
||||||
|
if path:
|
||||||
|
self.prefix = SymlinkName(self.path_type,path,path+'/')
|
||||||
|
|
||||||
# now save pref
|
# now save pref
|
||||||
if not pref:
|
if not pref:
|
||||||
# insert new pref for this combo (might be a new search or view, or first time for a path)
|
# insert new pref for this combo (might be a new search or view, or first time for a path)
|
||||||
|
|||||||
309
tables.sql
309
tables.sql
@@ -1,189 +1,196 @@
|
|||||||
alter database PA set timezone to 'Australia/Victoria';
|
ALTER DATABASE pa SET TIMEZONE TO 'aUSTRALIA/vICTORIA';
|
||||||
|
|
||||||
create sequence PA_USER_ID_SEQ;
|
CREATE SEQUENCE pa_user_id_seq;
|
||||||
create sequence PA_USER_STATE_ID_SEQ;
|
CREATE SEQUENCE pa_user_state_id_seq;
|
||||||
create sequence FACE_ID_SEQ;
|
CREATE SEQUENCE face_id_seq;
|
||||||
create sequence PATH_ID_SEQ;
|
CREATE SEQUENCE path_id_seq;
|
||||||
create sequence PATH_TYPE_ID_SEQ;
|
CREATE SEQUENCE path_type_id_seq;
|
||||||
create sequence FILE_ID_SEQ;
|
CREATE SEQUENCE file_id_seq;
|
||||||
create sequence FILE_TYPE_ID_SEQ;
|
CREATE SEQUENCE file_type_id_seq;
|
||||||
create sequence JOBEXTRA_ID_SEQ;
|
CREATE SEQUENCE jobextra_id_seq;
|
||||||
create sequence JOBLOG_ID_SEQ;
|
CREATE SEQUENCE joblog_id_seq;
|
||||||
create sequence JOB_ID_SEQ;
|
CREATE SEQUENCE job_id_seq;
|
||||||
create sequence PERSON_ID_SEQ;
|
CREATE SEQUENCE person_id_seq;
|
||||||
create sequence REFIMG_ID_SEQ;
|
CREATE SEQUENCE refimg_id_seq;
|
||||||
create sequence SETTINGS_ID_SEQ;
|
CREATE SEQUENCE settings_id_seq;
|
||||||
create sequence PA_JOB_MANAGER_ID_SEQ;
|
CREATE SEQUENCE pa_job_manager_id_seq;
|
||||||
create sequence PA_JOB_MANAGER_FE_MESSAGE_ID_SEQ;
|
CREATE SEQUENCE pa_job_manager_fe_message_id_seq;
|
||||||
create sequence FACE_OVERRIDE_TYPE_ID_SEQ;
|
CREATE SEQUENCE face_override_type_id_seq;
|
||||||
create sequence FACE_OVERRIDE_ID_SEQ;
|
CREATE SEQUENCE face_override_id_seq;
|
||||||
|
CREATE SEQUENCE query_id_seq;
|
||||||
|
|
||||||
-- these are hard-coded at present, not sure I can reflexively find models from API?
|
-- these are hard-coded at present, not sure I can reflexively find models from API?
|
||||||
create table AI_MODEL ( ID integer, NAME varchar(24), DESCRIPTION varchar(80), constraint PK_AI_MODEL primary key(ID) );
|
CREATE TABLE ai_model ( id INTEGER, name VARCHAR(24), description VARCHAR(80), CONSTRAINT pk_ai_model PRIMARY KEY(id) );
|
||||||
insert into AI_MODEL values ( 1, 'hog', 'normal' );
|
INSERT INTO ai_model VALUES ( 1, 'HOG', 'NORMAL' );
|
||||||
insert into AI_MODEL values ( 2, 'cnn', 'more accurate / much slower' );
|
INSERT INTO ai_model VALUES ( 2, 'CNN', 'MORE ACCURATE / MUCH SLOWER' );
|
||||||
|
|
||||||
create table SETTINGS(
|
CREATE TABLE settings(
|
||||||
ID integer,
|
id INTEGER,
|
||||||
BASE_PATH varchar, IMPORT_PATH varchar, STORAGE_PATH varchar, RECYCLE_BIN_PATH varchar, METADATA_PATH varchar,
|
base_path VARCHAR, import_path VARCHAR, storage_path VARCHAR, recycle_bin_path VARCHAR, metadata_path VARCHAR,
|
||||||
AUTO_ROTATE Boolean,
|
auto_rotate bOOLEAN,
|
||||||
DEFAULT_REFIMG_MODEL integer, DEFAULT_SCAN_MODEL integer, DEFAULT_THRESHOLD float,
|
default_refimg_model INTEGER, default_scan_model INTEGER, default_threshold FLOAT,
|
||||||
FACE_SIZE_LIMIT integer,
|
face_size_limit INTEGER,
|
||||||
SCHEDULED_IMPORT_SCAN integer, SCHEDULED_STORAGE_SCAN integer,
|
scheduled_import_scan INTEGER, scheduled_storage_scan INTEGER,
|
||||||
SCHEDULED_BIN_CLEANUP integer, BIN_CLEANUP_FILE_AGE integer,
|
scheduled_bin_cleanup INTEGER, bin_cleanup_file_age INTEGER,
|
||||||
JOB_ARCHIVE_AGE integer,
|
job_archive_age INTEGER,
|
||||||
constraint PK_SETTINGS_ID primary key(ID),
|
CONSTRAINT pk_settings_id PRIMARY KEY(id),
|
||||||
constraint FK_DEFAULT_REFIMG_MODEL foreign key (DEFAULT_REFIMG_MODEL) references AI_MODEL(ID),
|
CONSTRAINT fk_default_refimg_model FOREIGN KEY (default_refimg_model) REFERENCES ai_model(id),
|
||||||
constraint FK_DEFAULT_SCAN_MODEL foreign key (DEFAULT_SCAN_MODEL) references AI_MODEL(ID) );
|
CONSTRAINT fk_default_scan_model FOREIGN KEY (default_scan_model) REFERENCES ai_model(id) );
|
||||||
|
|
||||||
create table PA_USER(
|
CREATE TABLE pa_user(
|
||||||
ID integer,
|
id INTEGER,
|
||||||
DN varchar unique,
|
dn VARCHAR UNIQUE,
|
||||||
DEFAULT_IMPORT_NOO varchar,
|
default_import_noo VARCHAR,
|
||||||
DEFAULT_STORAGE_NOO varchar,
|
default_storage_noo VARCHAR,
|
||||||
DEFAULT_SEARCH_NOO varchar,
|
default_search_noo VARCHAR,
|
||||||
DEFAULT_GROUPING varchar(16),
|
default_grouping VARCHAR(16),
|
||||||
DEFAULT_HOW_MANY integer,
|
default_how_many INTEGER,
|
||||||
DEFAULT_SIZE integer,
|
default_size INTEGER,
|
||||||
DEFAULT_IMPORT_FOLDERS Boolean,
|
default_import_folders bOOLEAN,
|
||||||
DEFAULT_STORAGE_FOLDERS Boolean,
|
default_storage_folders bOOLEAN,
|
||||||
constraint PK_PA_USER_ID primary key(ID) );
|
CONSTRAINT pk_pa_user_id PRIMARY KEY(id) );
|
||||||
|
|
||||||
-- this is totally not 3rd normal form, but when I made it that, it was so complex, it was stupid
|
-- this is totally not 3rd normal form, but when I made it that, it was so complex, it was stupid
|
||||||
-- so for the little data here, I'm deliberately doing a redundant data structure
|
-- so for the little data here, I'm deliberately doing a redundant data structure
|
||||||
create table PA_USER_STATE ( ID integer, PA_USER_DN varchar(128), PATH_TYPE varchar(16),
|
CREATE TABLE pa_user_state ( id INTEGER, pa_user_dn VARCHAR(128), path_type VARCHAR(16),
|
||||||
NOO varchar(16), GROUPING varchar(16), HOW_MANY integer, ST_OFFSET integer, SIZE integer, FOLDERS Boolean,
|
noo VARCHAR(16), grouping VARCHAR(16), how_many INTEGER, st_offset INTEGER, size INTEGER, folders bOOLEAN,
|
||||||
ROOT varchar, CWD varchar,
|
root VARCHAR, cwd VARCHAR,
|
||||||
ORIG_PTYPE varchar, ORIG_SEARCH_TERM varchar, ORIG_URL varchar,
|
orig_ptype VARCHAR, orig_search_term VARCHAR, orig_url VARCHAR,
|
||||||
VIEW_EID integer, CURRENT integer, FIRST_EID integer, LAST_EID integer, NUM_ENTRIES integer, LAST_USED timestamptz,
|
view_eid INTEGER, current INTEGER, first_eid INTEGER, last_eid INTEGER, num_entries INTEGER, last_used TIMESTAMPTZ,
|
||||||
constraint FK_PA_USER_DN foreign key (PA_USER_DN) references PA_USER(DN),
|
CONSTRAINT fk_pa_user_dn FOREIGN KEY (pa_user_dn) REFERENCES pa_user(dn),
|
||||||
constraint PK_PA_USER_STATES_ID primary key(ID ) );
|
CONSTRAINT pk_pa_user_states_id PRIMARY KEY(id ) );
|
||||||
|
|
||||||
create table FILE_TYPE ( ID integer, NAME varchar(32) unique, constraint PK_FILE_TYPE_ID primary key(ID) );
|
CREATE TABLE query ( id INTEGER, path_type VARCHAR(16), noo VARCHAR(16), grouping VARCHAR(16), q_offset INTEGER,
|
||||||
|
entry_list VARCHAR, folders BOOLEAN, root VARCHAR, cwd VARCHAR, search_term VARCHAR, current INTEGER,
|
||||||
create table PATH_TYPE ( ID integer, NAME varchar(16) unique, constraint PK_PATH_TYPE_ID primary key(ID) );
|
created TIMESTAMPTZ,
|
||||||
|
CONSTRAINT pk_query_id PRIMARY KEY(id ) );
|
||||||
create table PATH ( ID integer, TYPE_ID integer, PATH_PREFIX varchar(1024), NUM_FILES integer,
|
|
||||||
constraint PK_PATH_ID primary key(ID),
|
|
||||||
constraint FK_PATH_TYPE_TYPE_ID foreign key (TYPE_ID) references PATH_TYPE(ID) );
|
|
||||||
|
|
||||||
create table ENTRY( ID integer, NAME varchar(128), TYPE_ID integer, EXISTS_ON_FS boolean,
|
|
||||||
constraint PK_ENTRY_ID primary key(ID),
|
|
||||||
constraint FK_FILE_TYPE_TYPE_ID foreign key (TYPE_ID) references FILE_TYPE(ID) );
|
|
||||||
|
|
||||||
create table FILE ( EID integer, SIZE_MB integer, HASH varchar(34), THUMBNAIL varchar, FACES_CREATED_ON float, LAST_HASH_DATE float, LAST_AI_SCAN float, YEAR integer, MONTH integer, DAY integer, WOY integer,
|
|
||||||
constraint PK_FILE_ID primary key(EID),
|
|
||||||
constraint FK_FILE_ENTRY_ID foreign key (EID) references ENTRY(ID) );
|
|
||||||
|
|
||||||
create table DEL_FILE ( FILE_EID integer, ORIG_PATH_PREFIX varchar(1024), constraint PK_DEL_FILE_FILE_EID primary key (FILE_EID),
|
|
||||||
constraint FK_ENTRY_ID foreign key (FILE_EID) references FILE(EID) );
|
|
||||||
|
|
||||||
create table DIR ( EID integer, REL_PATH varchar(256), NUM_FILES integer, LAST_IMPORT_DATE float,
|
|
||||||
constraint PK_DIR_EID primary key(EID),
|
|
||||||
constraint FK_DIR_ENTRY_ID foreign key (EID) references ENTRY(ID) );
|
|
||||||
|
|
||||||
create table PATH_DIR_LINK ( path_id integer, dir_eid integer,
|
|
||||||
constraint PK_PDL_path_id_dir_eid primary key (path_id, dir_eid),
|
|
||||||
constraint FK_PDL_PATH_ID foreign key (PATH_ID) references PATH(ID),
|
|
||||||
constraint FK_PDL_DIR_EID foreign key (DIR_EID) references DIR(EID) );
|
|
||||||
|
|
||||||
create table ENTRY_DIR_LINK ( entry_id integer, dir_eid integer,
|
|
||||||
constraint PK_EDL_entry_id_dir_eid primary key (entry_id, dir_eid),
|
|
||||||
constraint FK_EDL_ENTRY_ID foreign key (ENTRY_ID) references ENTRY(ID),
|
|
||||||
constraint FK_EDL_DIR_EID foreign key (DIR_EID) references DIR(EID) );
|
|
||||||
|
|
||||||
create table PERSON ( ID integer default nextval('PERSON_ID_SEQ'), TAG varchar(48), FIRSTNAME varchar(48), SURNAME varchar(48),
|
|
||||||
constraint PK_PERSON_ID primary key(ID) );
|
|
||||||
alter sequence PERSON_ID_SEQ owned by PERSON.ID;
|
|
||||||
|
|
||||||
|
|
||||||
create table REFIMG ( ID integer, FNAME varchar(128), FACE bytea, ORIG_W integer, ORIG_H integer,
|
CREATE TABLE file_type ( id INTEGER, name VARCHAR(32) UNIQUE, CONSTRAINT pk_file_type_id PRIMARY KEY(id) );
|
||||||
FACE_TOP integer, FACE_RIGHT integer, FACE_BOTTOM integer, FACE_LEFT integer, CREATED_ON float, THUMBNAIL varchar, MODEL_USED integer,
|
|
||||||
constraint PK_REFIMG_ID primary key(ID),
|
|
||||||
constraint FK_REFIMG_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) );
|
|
||||||
alter sequence REFIMG_ID_SEQ owned by REFIMG.ID;
|
|
||||||
|
|
||||||
create table FACE( ID integer, FACE bytea, FACE_TOP integer, FACE_RIGHT integer, FACE_BOTTOM integer, FACE_LEFT integer,
|
CREATE TABLE path_type ( id INTEGER, name VARCHAR(16) UNIQUE, CONSTRAINT pk_path_type_id PRIMARY KEY(id) );
|
||||||
W integer, H integer, constraint PK_FACE_ID primary key(ID) );
|
|
||||||
|
|
||||||
create table FACE_FILE_LINK( FACE_ID integer, FILE_EID integer, MODEL_USED integer,
|
CREATE TABLE path ( id INTEGER, type_id INTEGER, path_prefix VARCHAR(1024), num_files INTEGER,
|
||||||
constraint PK_FFL_FACE_ID_FILE_ID primary key(FACE_ID, FILE_EID),
|
CONSTRAINT pk_path_id PRIMARY KEY(id),
|
||||||
constraint FK_FFL_FACE_ID foreign key (FACE_ID) references FACE(ID) on delete cascade,
|
CONSTRAINT fk_path_type_type_id FOREIGN KEY (type_id) REFERENCES path_type(id) );
|
||||||
constraint FK_FFL_FILE_EID foreign key (FILE_EID) references FILE(EID),
|
|
||||||
constraint FK_FFL_MODEL_USED foreign key (MODEL_USED) references AI_MODEL(ID) );
|
|
||||||
|
|
||||||
create table FACE_REFIMG_LINK( FACE_ID integer, REFIMG_ID integer, FACE_DISTANCE float,
|
CREATE TABLE entry( id INTEGER, name VARCHAR(128), type_id INTEGER, exists_on_fs BOOLEAN,
|
||||||
constraint PK_FRL_FACE_ID_REFIMG_ID primary key(FACE_ID, REFIMG_ID),
|
CONSTRAINT pk_entry_id PRIMARY KEY(id),
|
||||||
constraint FK_FRL_FACE_ID foreign key (FACE_ID) references FACE(ID) on delete cascade,
|
CONSTRAINT fk_file_type_type_id FOREIGN KEY (type_id) REFERENCES file_type(id) );
|
||||||
constraint FK_FRL_REFIMG_ID foreign key (REFIMG_ID) references REFIMG(ID) );
|
|
||||||
|
|
||||||
create table FACE_OVERRIDE_TYPE ( ID integer, NAME varchar unique, constraint PK_FACE_OVERRIDE_TYPE_ID primary key(ID) );
|
CREATE TABLE file ( eid INTEGER, size_mb INTEGER, hash VARCHAR(34), thumbnail VARCHAR, faces_created_on FLOAT, last_hash_date FLOAT, last_ai_scan FLOAT, year INTEGER, month INTEGER, day INTEGER, woy INTEGER,
|
||||||
insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Manual match to existing person' );
|
CONSTRAINT pk_file_id PRIMARY KEY(eid),
|
||||||
insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Not a face' );
|
CONSTRAINT fk_file_entry_id FOREIGN KEY (eid) REFERENCES entry(id) );
|
||||||
insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Too young' );
|
|
||||||
insert into FACE_OVERRIDE_TYPE values ( (select nextval('FACE_OVERRIDE_TYPE_ID_SEQ')), 'Ignore face' );
|
CREATE TABLE del_file ( file_eid INTEGER, orig_path_prefix VARCHAR(1024), CONSTRAINT pk_del_file_file_eid PRIMARY KEY (file_eid),
|
||||||
|
CONSTRAINT fk_entry_id FOREIGN KEY (file_eid) REFERENCES file(eid) );
|
||||||
|
|
||||||
|
CREATE TABLE dir ( eid INTEGER, rel_path VARCHAR(256), num_files INTEGER, last_import_date FLOAT,
|
||||||
|
CONSTRAINT pk_dir_eid PRIMARY KEY(eid),
|
||||||
|
CONSTRAINT fk_dir_entry_id FOREIGN KEY (eid) REFERENCES entry(id) );
|
||||||
|
|
||||||
|
CREATE TABLE path_dir_link ( PATH_ID INTEGER, DIR_EID INTEGER,
|
||||||
|
CONSTRAINT pk_pdl_PATH_ID_DIR_EID PRIMARY KEY (PATH_ID, DIR_EID),
|
||||||
|
CONSTRAINT fk_pdl_path_id FOREIGN KEY (path_id) REFERENCES path(id),
|
||||||
|
CONSTRAINT fk_pdl_dir_eid FOREIGN KEY (dir_eid) REFERENCES dir(eid) );
|
||||||
|
|
||||||
|
CREATE TABLE entry_dir_link ( ENTRY_ID INTEGER, DIR_EID INTEGER,
|
||||||
|
CONSTRAINT pk_edl_ENTRY_ID_DIR_EID PRIMARY KEY (ENTRY_ID, DIR_EID),
|
||||||
|
CONSTRAINT fk_edl_entry_id FOREIGN KEY (entry_id) REFERENCES entry(id),
|
||||||
|
CONSTRAINT fk_edl_dir_eid FOREIGN KEY (dir_eid) REFERENCES dir(eid) );
|
||||||
|
|
||||||
|
CREATE TABLE person ( id INTEGER DEFAULT NEXTVAL('person_id_seq'), tag VARCHAR(48), firstname VARCHAR(48), surname VARCHAR(48),
|
||||||
|
CONSTRAINT pk_person_id PRIMARY KEY(id) );
|
||||||
|
ALTER SEQUENCE person_id_seq OWNED BY person.id;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE refimg ( id INTEGER, fname VARCHAR(128), face BYTEA, orig_w INTEGER, orig_h INTEGER,
|
||||||
|
face_top INTEGER, face_right INTEGER, face_bottom INTEGER, face_left INTEGER, created_on FLOAT, thumbnail VARCHAR, model_used INTEGER,
|
||||||
|
CONSTRAINT pk_refimg_id PRIMARY KEY(id),
|
||||||
|
CONSTRAINT fk_refimg_model_used FOREIGN KEY (model_used) REFERENCES ai_model(id) );
|
||||||
|
ALTER SEQUENCE refimg_id_seq OWNED BY refimg.id;
|
||||||
|
|
||||||
|
CREATE TABLE face( id INTEGER, face BYTEA, face_top INTEGER, face_right INTEGER, face_bottom INTEGER, face_left INTEGER,
|
||||||
|
w INTEGER, h INTEGER, CONSTRAINT pk_face_id PRIMARY KEY(id) );
|
||||||
|
|
||||||
|
CREATE TABLE face_file_link( face_id INTEGER, file_eid INTEGER, model_used INTEGER,
|
||||||
|
CONSTRAINT pk_ffl_face_id_file_id PRIMARY KEY(face_id, file_eid),
|
||||||
|
CONSTRAINT fk_ffl_face_id FOREIGN KEY (face_id) REFERENCES face(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_ffl_file_eid FOREIGN KEY (file_eid) REFERENCES file(eid),
|
||||||
|
CONSTRAINT fk_ffl_model_used FOREIGN KEY (model_used) REFERENCES ai_model(id) );
|
||||||
|
|
||||||
|
CREATE TABLE face_refimg_link( face_id INTEGER, refimg_id INTEGER, face_distance FLOAT,
|
||||||
|
CONSTRAINT pk_frl_face_id_refimg_id PRIMARY KEY(face_id, refimg_id),
|
||||||
|
CONSTRAINT fk_frl_face_id FOREIGN KEY (face_id) REFERENCES face(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT fk_frl_refimg_id FOREIGN KEY (refimg_id) REFERENCES refimg(id) );
|
||||||
|
|
||||||
|
CREATE TABLE face_override_type ( id INTEGER, name VARCHAR UNIQUE, CONSTRAINT pk_face_override_type_id PRIMARY KEY(id) );
|
||||||
|
INSERT INTO face_override_type VALUES ( (SELECT NEXTVAL('face_override_type_id_seq')), 'mANUAL MATCH TO EXISTING PERSON' );
|
||||||
|
INSERT INTO face_override_type VALUES ( (SELECT NEXTVAL('face_override_type_id_seq')), 'nOT A FACE' );
|
||||||
|
INSERT INTO face_override_type VALUES ( (SELECT NEXTVAL('face_override_type_id_seq')), 'tOO YOUNG' );
|
||||||
|
INSERT INTO face_override_type VALUES ( (SELECT NEXTVAL('face_override_type_id_seq')), 'iGNORE FACE' );
|
||||||
|
|
||||||
-- keep non-redundant FACE because, when we rebuild data we may have a null FACE_ID, but still want to connect to this override
|
-- keep non-redundant FACE because, when we rebuild data we may have a null FACE_ID, but still want to connect to this override
|
||||||
-- from a previous AI pass... (would happen if we delete a file and then reimport/scan it), OR, more likely we change (say) a threshold, etc.
|
-- from a previous AI pass... (would happen if we delete a file and then reimport/scan it), OR, more likely we change (say) a threshold, etc.
|
||||||
-- any reordering of faces, generates new face_ids... (but if the face data was the same, then this override should stand)
|
-- any reordering of faces, generates new face_ids... (but if the face data was the same, then this override should stand)
|
||||||
create table FACE_NO_MATCH_OVERRIDE ( ID integer, FACE_ID integer, TYPE_ID integer,
|
CREATE TABLE face_no_match_override ( id INTEGER, face_id INTEGER, type_id INTEGER,
|
||||||
constraint FK_FNMO_FACE_ID foreign key (FACE_ID) references FACE(ID),
|
CONSTRAINT fk_fnmo_face_id FOREIGN KEY (face_id) REFERENCES face(id),
|
||||||
constraint FK_FNMO_TYPE foreign key (TYPE_ID) references FACE_OVERRIDE_TYPE(ID),
|
CONSTRAINT fk_fnmo_type FOREIGN KEY (type_id) REFERENCES face_override_type(id),
|
||||||
constraint PK_FNMO_ID primary key(ID) );
|
CONSTRAINT pk_fnmo_id PRIMARY KEY(id) );
|
||||||
|
|
||||||
-- manual match goes to person not refimg, so on search, etc. we deal with this anomaly (via sql not ORM)
|
-- manual match goes to person not refimg, so on search, etc. we deal with this anomaly (via sql not ORM)
|
||||||
create table FACE_FORCE_MATCH_OVERRIDE ( ID integer, FACE_ID integer, PERSON_ID integer, constraint PK_FACE_FORCE_MATCH_OVERRIDE_ID primary key(ID) );
|
CREATE TABLE face_force_match_override ( id INTEGER, face_id INTEGER, person_id INTEGER, CONSTRAINT pk_face_force_match_override_id PRIMARY KEY(id) );
|
||||||
|
|
||||||
create table DISCONNECTED_NO_MATCH_OVERRIDE ( FACE bytea, TYPE_ID integer,
|
CREATE TABLE disconnected_no_match_override ( face BYTEA, type_id INTEGER,
|
||||||
constraint FK_DNMO_TYPE_ID foreign key (TYPE_ID) references FACE_OVERRIDE_TYPE(ID),
|
CONSTRAINT fk_dnmo_type_id FOREIGN KEY (type_id) REFERENCES face_override_type(id),
|
||||||
constraint PK_DNMO_FACE primary key (FACE) );
|
CONSTRAINT pk_dnmo_face PRIMARY KEY (face) );
|
||||||
|
|
||||||
create table DISCONNECTED_FORCE_MATCH_OVERRIDE ( FACE bytea, PERSON_ID integer,
|
CREATE TABLE disconnected_force_match_override ( face BYTEA, person_id INTEGER,
|
||||||
constraint FK_DFMO_PERSON_ID foreign key (PERSON_ID) references PERSON(ID),
|
CONSTRAINT fk_dfmo_person_id FOREIGN KEY (person_id) REFERENCES person(id),
|
||||||
constraint PK_DFMO_FACE primary key (FACE) );
|
CONSTRAINT pk_dfmo_face PRIMARY KEY (face) );
|
||||||
|
|
||||||
create table PERSON_REFIMG_LINK ( PERSON_ID integer, REFIMG_ID integer,
|
CREATE TABLE person_refimg_link ( person_id INTEGER, refimg_id INTEGER,
|
||||||
constraint PK_PRL primary key(PERSON_ID, REFIMG_ID),
|
CONSTRAINT pk_prl PRIMARY KEY(person_id, refimg_id),
|
||||||
constraint FK_PRL_PERSON_ID foreign key (PERSON_ID) references PERSON(ID),
|
CONSTRAINT fk_prl_person_id FOREIGN KEY (person_id) REFERENCES person(id),
|
||||||
constraint FK_PRL_REFIMG_ID foreign key (REFIMG_ID) references REFIMG(ID),
|
CONSTRAINT fk_prl_refimg_id FOREIGN KEY (refimg_id) REFERENCES refimg(id),
|
||||||
constraint U_PRL_REFIMG_ID unique(REFIMG_ID) );
|
CONSTRAINT u_prl_refimg_id UNIQUE(refimg_id) );
|
||||||
|
|
||||||
create table JOB (
|
CREATE TABLE job (
|
||||||
ID integer, START_TIME timestamptz, LAST_UPDATE timestamptz, NAME varchar(64), STATE varchar(128),
|
id INTEGER, start_time TIMESTAMPTZ, last_update TIMESTAMPTZ, name VARCHAR(64), state VARCHAR(128),
|
||||||
NUM_FILES integer, CURRENT_FILE_NUM integer, CURRENT_FILE varchar(256), WAIT_FOR integer, PA_JOB_STATE varchar(48),
|
num_files INTEGER, current_file_num INTEGER, current_file VARCHAR(256), wait_for INTEGER, pa_job_state VARCHAR(48),
|
||||||
constraint PK_JOB_ID primary key(ID) );
|
CONSTRAINT pk_job_id PRIMARY KEY(id) );
|
||||||
|
|
||||||
-- used to pass / keep extra values, e.g. num_files for jobs that have sets of files, or out* for adding output from jobs that you want to pass to next job in the chain
|
-- used to pass / keep extra values, e.g. num_files for jobs that have sets of files, or out* for adding output from jobs that you want to pass to next job in the chain
|
||||||
create table JOBEXTRA ( ID integer, JOB_ID integer, NAME varchar(32), VALUE varchar,
|
CREATE TABLE jobextra ( id INTEGER, job_id INTEGER, name VARCHAR(32), value VARCHAR,
|
||||||
constraint PK_JOBEXTRA_ID primary key(ID), constraint FK_JOBEXTRA_JOB_ID foreign key(JOB_ID) references JOB(ID) );
|
CONSTRAINT pk_jobextra_id PRIMARY KEY(id), CONSTRAINT fk_jobextra_job_id FOREIGN KEY(job_id) REFERENCES job(id) );
|
||||||
|
|
||||||
create table JOBLOG ( ID integer, JOB_ID integer, LOG_DATE timestamptz, LOG varchar,
|
CREATE TABLE joblog ( id INTEGER, job_id INTEGER, log_date TIMESTAMPTZ, log VARCHAR,
|
||||||
constraint PK_JL_ID primary key(ID), constraint FK_JL_JOB_ID foreign key(JOB_ID) references JOB(ID) );
|
CONSTRAINT pk_jl_id PRIMARY KEY(id), CONSTRAINT fk_jl_job_id FOREIGN KEY(job_id) REFERENCES job(id) );
|
||||||
|
|
||||||
create table PA_JOB_MANAGER_FE_MESSAGE ( ID integer, JOB_ID integer, LEVEL varchar(16), MESSAGE varchar(8192), PERSISTENT boolean, CANT_CLOSE boolean,
|
CREATE TABLE pa_job_manager_fe_message ( id INTEGER, job_id INTEGER, level VARCHAR(16), message VARCHAR(8192), persistent BOOLEAN, cant_close BOOLEAN,
|
||||||
constraint PA_JOB_MANAGER_FE_ACKS_ID primary key(ID),
|
CONSTRAINT pa_job_manager_fe_acks_id PRIMARY KEY(id),
|
||||||
constraint FK_PA_JOB_MANAGER_FE_MESSAGE_JOB_ID foreign key(JOB_ID) references JOB(ID) );
|
CONSTRAINT fk_pa_job_manager_fe_message_job_id FOREIGN KEY(job_id) REFERENCES job(id) );
|
||||||
|
|
||||||
-- default data for types of paths
|
-- default data for types of paths
|
||||||
insert into PATH_TYPE values ( (select nextval('PATH_TYPE_ID_SEQ')), 'Import' );
|
INSERT INTO path_type VALUES ( (SELECT NEXTVAL('path_type_id_seq')), 'iMPORT' );
|
||||||
insert into PATH_TYPE values ( (select nextval('PATH_TYPE_ID_SEQ')), 'Storage' );
|
INSERT INTO path_type VALUES ( (SELECT NEXTVAL('path_type_id_seq')), 'sTORAGE' );
|
||||||
insert into PATH_TYPE values ( (select nextval('PATH_TYPE_ID_SEQ')), 'Bin' );
|
INSERT INTO path_type VALUES ( (SELECT NEXTVAL('path_type_id_seq')), 'bIN' );
|
||||||
insert into PATH_TYPE values ( (select nextval('PATH_TYPE_ID_SEQ')), 'Metadata' );
|
INSERT INTO path_type VALUES ( (SELECT NEXTVAL('path_type_id_seq')), 'mETADATA' );
|
||||||
|
|
||||||
-- default data for types of files
|
-- default data for types of files
|
||||||
insert into FILE_TYPE values ( (select nextval('FILE_TYPE_ID_SEQ')), 'Image' );
|
INSERT INTO file_type VALUES ( (SELECT NEXTVAL('file_type_id_seq')), 'iMAGE' );
|
||||||
insert into FILE_TYPE values ( (select nextval('FILE_TYPE_ID_SEQ')), 'Video' );
|
INSERT INTO file_type VALUES ( (SELECT NEXTVAL('file_type_id_seq')), 'vIDEO' );
|
||||||
insert into FILE_TYPE values ( (select nextval('FILE_TYPE_ID_SEQ')), 'Directory' );
|
INSERT INTO file_type VALUES ( (SELECT NEXTVAL('file_type_id_seq')), 'dIRECTORY' );
|
||||||
insert into FILE_TYPE values ( (select nextval('FILE_TYPE_ID_SEQ')), 'Unknown' );
|
INSERT INTO file_type VALUES ( (SELECT NEXTVAL('file_type_id_seq')), 'uNKNOWN' );
|
||||||
|
|
||||||
-- fake data only for making testing easier
|
-- fake data only for making testing easier
|
||||||
--insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'dad', 'Damien', 'De Paoli' );
|
--INSERT INTO person VALUES ( (SELECT NEXTVAL('person_id_seq')), 'dad', 'Damien', 'De Paoli' );
|
||||||
--insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'mum', 'Mandy', 'De Paoli' );
|
--INSERT INTO person VALUES ( (SELECT NEXTVAL('person_id_seq')), 'mum', 'Mandy', 'De Paoli' );
|
||||||
--insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'cam', 'Cameron', 'De Paoli' );
|
--INSERT INTO person VALUES ( (SELECT NEXTVAL('person_id_seq')), 'cam', 'Cameron', 'De Paoli' );
|
||||||
--insert into PERSON values ( (select nextval('PERSON_ID_SEQ')), 'mich', 'Michelle', 'De Paoli' );
|
--INSERT INTO person VALUES ( (SELECT NEXTVAL('person_id_seq')), 'mich', 'Michelle', 'De Paoli' );
|
||||||
-- DEV(ddp):
|
-- DEV(ddp):
|
||||||
insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, metadata_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), '/home/ddp/src/photoassistant/', 'images_to_process/', 'photos/', '.pa_bin/', '.pa_metadata/', true, 1, 1, '0.55', 43, 1, 1, 7, 30, 3 );
|
INSERT INTO settings ( ID, BASE_PATH, IMPORT_PATH, STORAGE_PATH, RECYCLE_BIN_PATH, METADATA_PATH, AUTO_ROTATE, DEFAULT_REFIMG_MODEL, DEFAULT_SCAN_MODEL, DEFAULT_THRESHOLD, FACE_SIZE_LIMIT, SCHEDULED_IMPORT_SCAN, SCHEDULED_STORAGE_SCAN, SCHEDULED_BIN_CLEANUP, BIN_CLEANUP_FILE_AGE, JOB_ARCHIVE_AGE ) VALUES ( (SELECT NEXTVAL('settings_id_seq')), '/HOME/DDP/SRC/PHOTOASSISTANT/', 'IMAGES_TO_PROCESS/', 'PHOTOS/', '.PA_BIN/', '.PA_METADATA/', TRUE, 1, 1, '0.55', 43, 1, 1, 7, 30, 3 );
|
||||||
-- DEV(cam):
|
-- DEV(cam):
|
||||||
--insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, metadata_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), 'c:/Users/cam/Desktop/code/python/photoassistant/', 'c:\images_to_process', 'photos/', '.pa_bin/', '.pa_metadata/', true, 1, 1, '0.55', 43, 1, 1, 7, 30, 3 );
|
--INSERT INTO settings ( id, base_path, import_path, storage_path, recycle_bin_path, metadata_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) VALUES ( (select nextval('SETTINGS_ID_SEQ')), 'c:/Users/cam/Desktop/code/python/photoassistant/', 'c:\images_to_process', 'photos/', '.pa_bin/', '.pa_metadata/', TRUE, 1, 1, '0.55', 43, 1, 1, 7, 30, 3 );
|
||||||
-- PROD:
|
-- PROD:
|
||||||
--insert into SETTINGS ( id, base_path, import_path, storage_path, recycle_bin_path, metadata_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) values ( (select nextval('SETTINGS_ID_SEQ')), '/export/docker/storage/', 'Camera_uploads/', 'photos/', '.pa_bin/', '.pa_metadata/', true, 1, 1, '0.55', 43, 1, 1, 7, 30, 4 );
|
--INSERT INTO settings ( id, base_path, import_path, storage_path, recycle_bin_path, metadata_path, auto_rotate, default_refimg_model, default_scan_model, default_threshold, face_size_limit, scheduled_import_scan, scheduled_storage_scan, scheduled_bin_cleanup, bin_cleanup_file_age, job_archive_age ) VALUES ( (SELECT NEXTVAL('settings_id_seq')), '/export/docker/storage/', 'Camera_uploads/', 'photos/', '.pa_bin/', '.pa_metadata/', TRUE, 1, 1, '0.55', 43, 1, 1, 7, 30, 4 );
|
||||||
|
|||||||
@@ -16,14 +16,20 @@
|
|||||||
move_paths.push(p)
|
move_paths.push(p)
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
document.OPT = '{{OPT}}'
|
// GLOBALS
|
||||||
document.entries = '{{entry_data}}'
|
// how many on this page, we can change this and redraw the page to suit (also used heavily in pagination code related to entry and page list)
|
||||||
document.how_many = '{{OPT.how_many}}'
|
var howMany = {{OPT.how_many}}
|
||||||
document.entries_len = '{{entry_data|length}}'
|
|
||||||
|
// this is the list of entry ids for the images for ALL matches for this query
|
||||||
|
var entryList={{query_data.entry_list}}
|
||||||
|
|
||||||
|
// pageList is just those entries shown on this page from the full entryList
|
||||||
|
var pageList=[]
|
||||||
|
// force pageList to the first page
|
||||||
|
getPage(1)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<form id="main_form" method="POST" action="/change_file_opts">
|
|
||||||
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
||||||
{% if search_term is defined %}
|
{% if search_term is defined %}
|
||||||
<input type="hidden" name="search_term" id="view_term" value="{{search_term}}">
|
<input type="hidden" name="search_term" id="view_term" value="{{search_term}}">
|
||||||
@@ -68,15 +74,11 @@
|
|||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="col flex-grow-1 my-auto d-flex justify-content-center w-100">
|
<div class="col flex-grow-1 my-auto d-flex justify-content-center w-100">
|
||||||
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary">
|
<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>
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<span class="sm-txt my-auto"> {{OPT.how_many}} files </span>
|
<span class="sm-txt my-auto"> {{OPT.how_many}} files </span>
|
||||||
{% set nxt_disabled="" %}
|
<button aria-label="next" id="next" name="next" class="next sm-txt btn btn-outline-secondary" onClick="nextPage()">
|
||||||
{% if not entry_data or entry_data|length < OPT.how_many|int %}
|
|
||||||
{% set nxt_disabled="disabled" %}
|
|
||||||
{% endif %}
|
|
||||||
<button aria-label="next" id="next" {{nxt_disabled}} name="next" class="next sm-txt btn btn-outline-secondary">
|
|
||||||
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<button aria-label="move" id="move" disabled name="move" class="sm-txt btn btn-outline-primary ms-4" onClick="MoveDBox(move_paths,'{{url_for('internal', filename='icons.svg')}}'); return false;">
|
<button aria-label="move" id="move" disabled name="move" class="sm-txt btn btn-outline-primary ms-4" onClick="MoveDBox(move_paths,'{{url_for('internal', filename='icons.svg')}}'); return false;">
|
||||||
@@ -138,10 +140,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input name="eids" id="eids" type="hidden" value="{{eids.str}}">
|
<input name="eids" id="eids" type="hidden" value="{{eids.str}}">
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{% set ecnt=namespace( val=0 ) %}
|
{% set ecnt=namespace( val=0 ) %}
|
||||||
<div class="row ms-2">
|
<div id="figures" class="row ms-2">
|
||||||
|
<!--
|
||||||
{% set last = namespace(printed=0) %}
|
{% set last = namespace(printed=0) %}
|
||||||
{# rare event of empty folder, still need to show back button #}
|
{# rare event of empty folder, still need to show back button #}
|
||||||
{% if OPT.folders and entry_data|length == 0 %}
|
{% if OPT.folders and entry_data|length == 0 %}
|
||||||
@@ -263,22 +265,22 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<form id="nav_form" method="POST" action="/change_file_opts">
|
|
||||||
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
<input type="hidden" name="cwd" id="cwd" value="{{OPT.cwd}}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col my-auto d-flex justify-content-center">
|
<div class="col my-auto d-flex justify-content-center">
|
||||||
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary">
|
<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>
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
|
||||||
</button>
|
</button>
|
||||||
<span class="sm-txt my-auto"> {{OPT.how_many}} files </span>
|
<span class="sm-txt my-auto"> {{OPT.how_many}} files </span>
|
||||||
<button aria-label="next" id="next" {{nxt_disabled}} name="next" class="next sm-txt btn btn-outline-secondary">
|
<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>
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
</div class="container">
|
</div class="container">
|
||||||
{% endblock main_content %}
|
{% endblock main_content %}
|
||||||
{% block script_content %}
|
{% block script_content %}
|
||||||
|
|||||||
Reference in New Issue
Block a user