From 6522bda09c4af86fd6d0f66aceb7434e96496389 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Wed, 30 Dec 2020 15:28:52 -0800 Subject: [PATCH] Add basename in separate column so we can orderby it. For a while I've wanted to be able to sort search results by the file's basename. This is especially important for the cli. SQLite doesn't have an easy way to split the filepath column by the slash, so the only choice is to store basename as a separate column. I put it off for a while but I think it's the right move. However we must not forget to update it every time we update filepath, which is a bummer. --- etiquette/constants.py | 6 +- etiquette/objects.py | 7 ++- etiquette/photodb.py | 1 + etiquette/searchhelpers.py | 3 + frontends/etiquette_cli.py | 3 +- .../database_upgrader/database_upgrader.py | 61 +++++++++++++++++++ 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/etiquette/constants.py b/etiquette/constants.py index c09cab0..d0fc1f1 100644 --- a/etiquette/constants.py +++ b/etiquette/constants.py @@ -41,7 +41,7 @@ ffmpeg = _load_ffmpeg() # Database ######################################################################################### -DATABASE_VERSION = 15 +DATABASE_VERSION = 16 DB_VERSION_PRAGMA = f''' PRAGMA user_version = {DATABASE_VERSION}; ''' @@ -91,8 +91,9 @@ CREATE TABLE IF NOT EXISTS photos( id TEXT PRIMARY KEY NOT NULL, filepath TEXT COLLATE NOCASE, dev_ino TEXT, + basename TEXT COLLATE NOCASE, override_filename TEXT COLLATE NOCASE, - extension TEXT, + extension TEXT COLLATE NOCASE, width INT, height INT, ratio REAL, @@ -195,6 +196,7 @@ SQL_INDEX = sqlhelpers.reverse_table_column_map(SQL_COLUMNS) ALLOWED_ORDERBY_COLUMNS = { 'area', + 'basename', 'bitrate', 'bytes', 'created', diff --git a/etiquette/objects.py b/etiquette/objects.py index 1fdd9a5..303df69 100644 --- a/etiquette/objects.py +++ b/etiquette/objects.py @@ -1097,6 +1097,7 @@ class Photo(ObjectBase): data = { 'id': self.id, 'filepath': new_filepath.absolute_path, + 'basename': new_filepath.basename, } self.photodb.sql_update(table='photos', pairs=data, where_key='id') self.real_path = new_filepath @@ -1180,9 +1181,11 @@ class Photo(ObjectBase): spinal.copy_file(old_path, new_path) data = { - 'filepath': (old_path.absolute_path, new_path.absolute_path), + 'id': self.id, + 'filepath': new_path.absolute_path, + 'basename': new_path.basename, } - self.photodb.sql_update(table='photos', pairs=data, where_key='filepath') + self.photodb.sql_update(table='photos', pairs=data, where_key='id') self.real_path = new_path if new_path.normcase == old_path.normcase: diff --git a/etiquette/photodb.py b/etiquette/photodb.py index 6f15c09..d25ec97 100644 --- a/etiquette/photodb.py +++ b/etiquette/photodb.py @@ -507,6 +507,7 @@ class PDBPhotoMixin: data = { 'id': photo_id, 'filepath': filepath.absolute_path, + 'basename': filepath.basename, 'override_filename': None, 'extension': filepath.extension.no_dot, 'created': helpers.now(), diff --git a/etiquette/searchhelpers.py b/etiquette/searchhelpers.py index 3af01ce..83d8cb6 100644 --- a/etiquette/searchhelpers.py +++ b/etiquette/searchhelpers.py @@ -338,6 +338,9 @@ def normalize_orderby(orderby, warning_bag=None): elif column == 'area': column = '(width * height)' + elif column == 'basename': + column = 'COALESCE(override_filename, basename)' + elif column == 'bitrate': column = '((bytes / 128) / duration)' diff --git a/frontends/etiquette_cli.py b/frontends/etiquette_cli.py index e9d808a..d76093f 100644 --- a/frontends/etiquette_cli.py +++ b/frontends/etiquette_cli.py @@ -192,7 +192,6 @@ def purge_empty_albums_argparse(args): def search_argparse(args): photos = search_by_argparse(args, yield_photos=True) - photos = sorted(photos, key=lambda p: p.real_path) for photo in photos: print(photo.real_path.absolute_path) @@ -357,7 +356,7 @@ def main(argv): p_search.add_argument('--tag_expression', '--tag-expression', dest='tag_expression', default=None) p_search.add_argument('--limit', dest='limit', default=None) p_search.add_argument('--offset', dest='offset', default=None) - p_search.add_argument('--orderby', dest='orderby', default=None) + p_search.add_argument('--orderby', dest='orderby', default='basename-ASC') # p_search.add_argument('--yield_albums', '--yield-albums', dest='yield_albums', default=None) p_search.set_defaults(func=search_argparse) diff --git a/utilities/database_upgrader/database_upgrader.py b/utilities/database_upgrader/database_upgrader.py index 9a2f49b..54083d8 100644 --- a/utilities/database_upgrader/database_upgrader.py +++ b/utilities/database_upgrader/database_upgrader.py @@ -1,4 +1,5 @@ import argparse +import os import sys from voussoirkit import sqlhelpers @@ -454,6 +455,66 @@ def upgrade_14_to_15(photodb): dev_ino = f'{dev},{ino}' photodb.sql_execute('UPDATE photos SET dev_ino = ? WHERE id == ?', [dev_ino, photo.id]) +def upgrade_15_to_16(photodb): + ''' + Added the basename column to photos. Added collate nocase to extension. + ''' + with Regenerator(photodb, except_tables='photos'): + photodb.sql_executescript(''' + PRAGMA foreign_keys = OFF; + + BEGIN; + + ALTER TABLE photos RENAME TO photos_old; + + CREATE TABLE photos( + id TEXT PRIMARY KEY NOT NULL, + filepath TEXT COLLATE NOCASE, + dev_ino TEXT, + basename TEXT COLLATE NOCASE, + override_filename TEXT COLLATE NOCASE, + extension TEXT COLLATE NOCASE, + width INT, + height INT, + ratio REAL, + area INT, + duration INT, + bytes INT, + created INT, + thumbnail TEXT, + tagged_at INT, + author_id TEXT, + searchhidden INT, + FOREIGN KEY(author_id) REFERENCES users(id) + ); + + INSERT INTO photos SELECT + id, + filepath, + dev_ino, + NULL, + override_filename, + extension, + width, + height, + ratio, + area, + duration, + bytes, + created, + thumbnail, + tagged_at, + author_id, + searchhidden + FROM photos_old; + + DROP TABLE photos_old; + ''') + + for (id, filepath) in photodb.sql_select('SELECT id, filepath FROM photos'): + basename = os.path.basename(filepath) + photodb.sql_execute('UPDATE photos SET basename = ? WHERE id == ?', [basename, id]) + def upgrade_all(data_directory): ''' Given the directory containing a phototagger database, apply all of the