Add database upgrader script
This commit is contained in:
parent
45a8a8ccc5
commit
2d4b07c10f
7 changed files with 127 additions and 26 deletions
|
@ -1,7 +1,7 @@
|
|||
import string
|
||||
|
||||
# Errors and warnings
|
||||
ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}'
|
||||
ERROR_DATABASE_OUTOFDATE = 'Database is out-of-date. {current} should be {new}. Please use etiquette_upgrader.py'
|
||||
ERROR_INVALID_ACTION = 'Invalid action'
|
||||
ERROR_NO_SUCH_TAG = 'Doesn\'t exist'
|
||||
ERROR_NO_TAG_GIVEN = 'No tag name supplied'
|
||||
|
|
|
@ -235,6 +235,11 @@ def get_albums_json():
|
|||
return make_json_response(albums)
|
||||
|
||||
|
||||
@site.route('/bookmarks')
|
||||
def get_bookmarks():
|
||||
return flask.render_template('bookmarks.html')
|
||||
|
||||
|
||||
@site.route('/file/<photoid>')
|
||||
def get_file(photoid):
|
||||
requested_photoid = photoid
|
||||
|
@ -327,7 +332,6 @@ def get_search_core():
|
|||
orderby = request.args.get('orderby', None)
|
||||
if orderby:
|
||||
orderby = orderby.replace('-', ' ')
|
||||
orderby = orderby.replace('_', ' ')
|
||||
orderby = orderby.split(',')
|
||||
else:
|
||||
orderby = None
|
||||
|
|
60
etiquette/etiquette_upgrader.py
Normal file
60
etiquette/etiquette_upgrader.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import argparse
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
|
||||
import phototagger
|
||||
|
||||
def upgrade_1_to_2(sql):
|
||||
'''
|
||||
In this version, a column `tagged_at` was added to the Photos table, to keep
|
||||
track of the last time the photo's tags were edited (added or removed).
|
||||
'''
|
||||
cur = sql.cursor()
|
||||
cur.execute('ALTER TABLE photos ADD COLUMN tagged_at INT')
|
||||
|
||||
|
||||
def upgrade_all(database_filename):
|
||||
'''
|
||||
Given the filename of a phototagger database, apply all of the needed
|
||||
upgrade_x_to_y functions in order.
|
||||
'''
|
||||
if not os.path.isfile(database_filename):
|
||||
raise FileNotFoundError(database_filename)
|
||||
|
||||
sql = sqlite3.connect(database_filename)
|
||||
cur = sql.cursor()
|
||||
|
||||
cur.execute('PRAGMA user_version')
|
||||
current_version = cur.fetchone()[0]
|
||||
needed_version = phototagger.DATABASE_VERSION
|
||||
|
||||
if current_version == needed_version:
|
||||
print('Already up-to-date.')
|
||||
return
|
||||
|
||||
for version_number in range(current_version + 1, needed_version + 1):
|
||||
print('Upgrading from %d to %d' % (current_version, version_number))
|
||||
upgrade_function = 'upgrade_%d_to_%d' % (current_version, version_number)
|
||||
upgrade_function = eval(upgrade_function)
|
||||
upgrade_function(sql)
|
||||
sql.cursor().execute('PRAGMA user_version = 2')
|
||||
sql.commit()
|
||||
current_version = version_number
|
||||
print('Upgrades finished.')
|
||||
|
||||
|
||||
def upgrade_all_argparse(args):
|
||||
return upgrade_all(database_filename=args.database_filename)
|
||||
|
||||
def main(argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('database_filename')
|
||||
parser.set_defaults(func=upgrade_all_argparse)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
args.func(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
|
@ -70,6 +70,7 @@ SQL_PHOTO_COLUMNS = [
|
|||
'bytes',
|
||||
'created',
|
||||
'thumbnail',
|
||||
'tagged_at',
|
||||
]
|
||||
SQL_TAG_COLUMNS = [
|
||||
'id',
|
||||
|
@ -101,7 +102,10 @@ SQL_SYN = {key:index for (index, key) in enumerate(SQL_SYN_COLUMNS)}
|
|||
SQL_TAG = {key:index for (index, key) in enumerate(SQL_TAG_COLUMNS)}
|
||||
SQL_TAGGROUP = {key:index for (index, key) in enumerate(SQL_TAGGROUP_COLUMNS)}
|
||||
|
||||
DATABASE_VERSION = 1
|
||||
# Note: Setting user_version pragma in init sequence is safe because it only
|
||||
# happens after the out-of-date check occurs, so no chance of accidentally
|
||||
# overwriting it.
|
||||
DATABASE_VERSION = 2
|
||||
DB_INIT = '''
|
||||
PRAGMA count_changes = OFF;
|
||||
PRAGMA cache_size = 10000;
|
||||
|
@ -124,7 +128,8 @@ CREATE TABLE IF NOT EXISTS photos(
|
|||
duration INT,
|
||||
bytes INT,
|
||||
created INT,
|
||||
thumbnail TEXT
|
||||
thumbnail TEXT,
|
||||
tagged_at INT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS tags(
|
||||
id TEXT,
|
||||
|
@ -180,6 +185,18 @@ CREATE INDEX IF NOT EXISTS index_grouprel_parentid on tag_group_rel(parentid);
|
|||
CREATE INDEX IF NOT EXISTS index_grouprel_memberid on tag_group_rel(memberid);
|
||||
'''.format(user_version=DATABASE_VERSION)
|
||||
|
||||
ALLOWED_ORDERBY_COLUMNS = [
|
||||
'extension',
|
||||
'width',
|
||||
'height',
|
||||
'ratio',
|
||||
'area',
|
||||
'duration',
|
||||
'bytes',
|
||||
'created',
|
||||
'tagged_at',
|
||||
'random',
|
||||
]
|
||||
|
||||
def _helper_extension(ext):
|
||||
'''
|
||||
|
@ -240,18 +257,7 @@ def _helper_orderby(orderby):
|
|||
return None
|
||||
|
||||
#print(column, sorter)
|
||||
sortable = column in [
|
||||
'extension',
|
||||
'width',
|
||||
'height',
|
||||
'ratio',
|
||||
'area',
|
||||
'duration',
|
||||
'bytes',
|
||||
'created',
|
||||
'random',
|
||||
]
|
||||
if not sortable:
|
||||
if column not in ALLOWED_ORDERBY_COLUMNS:
|
||||
warnings.warn(constants.WARNING_ORDERBY_BADCOL.format(column=column))
|
||||
return None
|
||||
if column == 'random':
|
||||
|
@ -1666,6 +1672,7 @@ class Photo(ObjectBase):
|
|||
self.id = row_tuple[SQL_PHOTO['id']]
|
||||
self.real_filepath = row_tuple[SQL_PHOTO['filepath']]
|
||||
self.real_filepath = normalize_filepath(self.real_filepath)
|
||||
self.real_path = pathclass.Path(self.real_filepath)
|
||||
self.filepath = row_tuple[SQL_PHOTO['override_filename']] or self.real_filepath
|
||||
self.basename = row_tuple[SQL_PHOTO['override_filename']] or os.path.basename(self.real_filepath)
|
||||
self.extension = row_tuple[SQL_PHOTO['extension']]
|
||||
|
@ -1677,7 +1684,7 @@ class Photo(ObjectBase):
|
|||
self.duration = row_tuple[SQL_PHOTO['duration']]
|
||||
self.created = row_tuple[SQL_PHOTO['created']]
|
||||
self.thumbnail = row_tuple[SQL_PHOTO['thumbnail']]
|
||||
self.real_path = pathclass.Path(self.real_filepath)
|
||||
self.tagged_at = row_tuple[SQL_PHOTO['tagged_at']]
|
||||
|
||||
def __reinit__(self):
|
||||
'''
|
||||
|
@ -1709,9 +1716,10 @@ class Photo(ObjectBase):
|
|||
log.debug('Preferring new {tag:s} over {par:s}'.format(tag=tag, par=parent))
|
||||
self.remove_tag(parent)
|
||||
|
||||
|
||||
log.debug('Applying tag {tag:s} to photo {pho:s}'.format(tag=tag, pho=self))
|
||||
now = int(getnow())
|
||||
self.photodb.cur.execute('INSERT INTO photo_tag_rel VALUES(?, ?)', [self.id, tag.id])
|
||||
self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id])
|
||||
if commit:
|
||||
log.debug('Committing - add photo tag')
|
||||
self.photodb.commit()
|
||||
|
@ -1913,6 +1921,8 @@ class Photo(ObjectBase):
|
|||
'DELETE FROM photo_tag_rel WHERE photoid == ? AND tagid == ?',
|
||||
[self.id, tag.id]
|
||||
)
|
||||
now = int(getnow())
|
||||
self.photodb.cur.execute('UPDATE photos SET tagged_at = ? WHERE id == ?', [now, self.id])
|
||||
if commit:
|
||||
log.debug('Committing - remove photo tag')
|
||||
self.photodb.commit()
|
||||
|
|
25
etiquette/templates/bookmarks.html
Normal file
25
etiquette/templates/bookmarks.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html5>
|
||||
<html>
|
||||
<head>
|
||||
{% import "header.html" as header %}
|
||||
<title>Bookmarks</title>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="/static/common.css">
|
||||
<script src="/static/common.js"></script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
{{header.make_header()}}
|
||||
<div id="content_body">
|
||||
<a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
</html>
|
|
@ -33,6 +33,7 @@ a:hover
|
|||
<a href="/search">Search</a>
|
||||
<a href="/tags">Browse tags</a>
|
||||
<a href="/albums">Browse albums</a>
|
||||
<a href="/bookmarks">Bookmarks</a>
|
||||
</body>
|
||||
|
||||
|
||||
|
|
|
@ -98,14 +98,15 @@ form
|
|||
{% macro create_orderby_li(selected_column, selected_sorter) %}
|
||||
<li class="search_builder_orderby_li">
|
||||
<select>
|
||||
<option value="created" {%if selected_column=="created"%}selected{%endif%} >Creation date</option>
|
||||
<option value="area" {%if selected_column=="area"%}selected{%endif%} >Area</option>
|
||||
<option value="width" {%if selected_column=="width"%}selected{%endif%} >Width</option>
|
||||
<option value="height" {%if selected_column=="height"%}selected{%endif%} >Height</option>
|
||||
<option value="ratio" {%if selected_column=="ratio"%}selected{%endif%} >Aspect Ratio</option>
|
||||
<option value="bytes" {%if selected_column=="bytes"%}selected{%endif%} >File size</option>
|
||||
<option value="duration" {%if selected_column=="duration"%}selected{%endif%} >Duration</option>
|
||||
<option value="random" {%if selected_column=="random"%}selected{%endif%} >Random</option>
|
||||
<option value="created" {%if selected_column=="created"%}selected{%endif%} >Creation date</option>
|
||||
<option value="area" {%if selected_column=="area"%}selected{%endif%} >Area</option>
|
||||
<option value="width" {%if selected_column=="width"%}selected{%endif%} >Width</option>
|
||||
<option value="height" {%if selected_column=="height"%}selected{%endif%} >Height</option>
|
||||
<option value="ratio" {%if selected_column=="ratio"%}selected{%endif%} >Aspect Ratio</option>
|
||||
<option value="bytes" {%if selected_column=="bytes"%}selected{%endif%} >File size</option>
|
||||
<option value="duration" {%if selected_column=="duration"%}selected{%endif%} >Duration</option>
|
||||
<option value="tagged_at" {%if selected_column=="tagged_at"%}selected{%endif%}>Recently tagged</option>
|
||||
<option value="random" {%if selected_column=="random"%}selected{%endif%} >Random</option>
|
||||
</select>
|
||||
<select>
|
||||
<option value="desc" {%if selected_sorter=="desc"%}selected{%endif%} >Descending</option>
|
||||
|
|
Loading…
Reference in a new issue