diff --git a/etiquette/constants.py b/etiquette/constants.py index 8e8cc6e..b8156c2 100644 --- a/etiquette/constants.py +++ b/etiquette/constants.py @@ -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' diff --git a/etiquette/etiquette.py b/etiquette/etiquette.py index d79797a..56430a8 100644 --- a/etiquette/etiquette.py +++ b/etiquette/etiquette.py @@ -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/') 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 diff --git a/etiquette/etiquette_upgrader.py b/etiquette/etiquette_upgrader.py new file mode 100644 index 0000000..7545c8d --- /dev/null +++ b/etiquette/etiquette_upgrader.py @@ -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:]) diff --git a/etiquette/phototagger.py b/etiquette/phototagger.py index 29b06a8..7df3e13 100644 --- a/etiquette/phototagger.py +++ b/etiquette/phototagger.py @@ -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() diff --git a/etiquette/templates/bookmarks.html b/etiquette/templates/bookmarks.html new file mode 100644 index 0000000..97216fe --- /dev/null +++ b/etiquette/templates/bookmarks.html @@ -0,0 +1,25 @@ + + + + {% import "header.html" as header %} + Bookmarks + + + + + + + + + + {{header.make_header()}} +
+ Needs tagging +
+ + + + + diff --git a/etiquette/templates/root.html b/etiquette/templates/root.html index 119eebd..52c6ce5 100644 --- a/etiquette/templates/root.html +++ b/etiquette/templates/root.html @@ -33,6 +33,7 @@ a:hover Search Browse tags Browse albums + Bookmarks diff --git a/etiquette/templates/search.html b/etiquette/templates/search.html index 52a03a3..9396f04 100644 --- a/etiquette/templates/search.html +++ b/etiquette/templates/search.html @@ -98,14 +98,15 @@ form {% macro create_orderby_li(selected_column, selected_sorter) %}