Use new worms version.

This commit is contained in:
voussoir 2022-07-15 23:00:07 -07:00
parent a436dafa9c
commit 0e00a1e981
No known key found for this signature in database
GPG key ID: 5F7554F8C26DACCB
10 changed files with 388 additions and 361 deletions

View file

@ -42,19 +42,8 @@ ffmpeg = _load_ffmpeg()
# Database #########################################################################################
DATABASE_VERSION = 20
DB_VERSION_PRAGMA = f'''
PRAGMA user_version = {DATABASE_VERSION};
'''
DB_PRAGMAS = f'''
PRAGMA cache_size = 10000;
PRAGMA foreign_keys = ON;
'''
DB_INIT = f'''
BEGIN;
{DB_PRAGMAS}
{DB_VERSION_PRAGMA}
----------------------------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS albums(
id TEXT PRIMARY KEY NOT NULL,
@ -191,8 +180,6 @@ CREATE TABLE IF NOT EXISTS tag_synonyms(
mastername TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS index_tag_synonyms_name on tag_synonyms(name);
----------------------------------------------------------------------------------------------------
COMMIT;
'''
SQL_COLUMNS = sqlhelpers.extract_table_column_map(DB_INIT)

View file

@ -329,7 +329,7 @@ class Album(ObjectBase, GroupableMixin):
self.photodb.insert(table='album_associated_directories', data=data)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_associated_directory(self, path) -> None:
'''
Add a directory from which this album will pull files during rescans.
@ -341,7 +341,7 @@ class Album(ObjectBase, GroupableMixin):
self._add_associated_directory(path)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_associated_directories(self, paths) -> None:
'''
Add multiple associated directories.
@ -352,7 +352,7 @@ class Album(ObjectBase, GroupableMixin):
self._add_associated_directory(path)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_child(self, member):
'''
Raises exceptions.CantGroupSelf if member is self.
@ -362,7 +362,7 @@ class Album(ObjectBase, GroupableMixin):
return super().add_child(member)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_children(self, *args, **kwargs):
return super().add_children(*args, **kwargs)
@ -372,7 +372,7 @@ class Album(ObjectBase, GroupableMixin):
self.photodb.insert(table='album_photo_rel', data=data)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_photo(self, photo) -> None:
if self.has_photo(photo):
return
@ -380,7 +380,7 @@ class Album(ObjectBase, GroupableMixin):
self._add_photo(photo)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def add_photos(self, photos) -> None:
existing_photos = set(self.get_photos())
photos = set(photos)
@ -393,7 +393,7 @@ class Album(ObjectBase, GroupableMixin):
self._add_photo(photo)
# Photo.add_tag already has @required_feature
@worms.transaction
@worms.atomic
def add_tag_to_all(self, tag, *, nested_children=True) -> None:
'''
Add this tag to every photo in the album. Saves you from having to
@ -413,7 +413,7 @@ class Album(ObjectBase, GroupableMixin):
photo.add_tag(tag)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def delete(self, *, delete_children=False) -> None:
log.info('Deleting %s.', self)
GroupableMixin.delete(self, delete_children=delete_children)
@ -431,7 +431,7 @@ class Album(ObjectBase, GroupableMixin):
return self.id
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def edit(self, title=None, description=None) -> None:
'''
Change the title or description. Leave None to keep current value.
@ -546,12 +546,12 @@ class Album(ObjectBase, GroupableMixin):
return j
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def remove_child(self, *args, **kwargs):
return super().remove_child(*args, **kwargs)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def remove_children(self, *args, **kwargs):
return super().remove_children(*args, **kwargs)
@ -561,12 +561,12 @@ class Album(ObjectBase, GroupableMixin):
self.photodb.delete(table='album_photo_rel', pairs=pairs)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def remove_photo(self, photo) -> None:
self._remove_photo(photo)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def remove_photos(self, photos) -> None:
existing_photos = set(self.get_photos())
photos = set(photos)
@ -579,7 +579,7 @@ class Album(ObjectBase, GroupableMixin):
self._remove_photo(photo)
@decorators.required_feature('album.edit')
@worms.transaction
@worms.atomic
def set_thumbnail_photo(self, photo) -> None:
'''
Raises TypeError if photo is not a Photo.
@ -747,7 +747,7 @@ class Bookmark(ObjectBase):
self.photodb.caches[Bookmark].remove(self.id)
@decorators.required_feature('bookmark.edit')
@worms.transaction
@worms.atomic
def delete(self) -> None:
self.photodb.delete(table=Bookmark, pairs={'id': self.id})
self._uncache()
@ -761,7 +761,7 @@ class Bookmark(ObjectBase):
return self.id
@decorators.required_feature('bookmark.edit')
@worms.transaction
@worms.atomic
def edit(self, title=None, url=None) -> None:
'''
Change the title or URL. Leave None to keep current.
@ -894,7 +894,7 @@ class Photo(ObjectBase):
# Will add -> Tag when forward references are supported by Python.
@decorators.required_feature('photo.add_remove_tag')
@worms.transaction
@worms.atomic
def add_tag(self, tag):
tag = self.photodb.get_tag(name=tag)
@ -948,7 +948,7 @@ class Photo(ObjectBase):
return '??? b'
# Photo.add_tag already has @required_feature add_remove_tag
@worms.transaction
@worms.atomic
def copy_tags(self, other_photo) -> None:
'''
Take all of the tags owned by other_photo and apply them to this photo.
@ -957,7 +957,7 @@ class Photo(ObjectBase):
self.add_tag(tag)
@decorators.required_feature('photo.edit')
@worms.transaction
@worms.atomic
def delete(self, *, delete_file=False) -> None:
'''
Delete the Photo and its relation to any tags and albums.
@ -995,7 +995,7 @@ class Photo(ObjectBase):
return hms.seconds_to_hms(self.duration)
@decorators.required_feature('photo.generate_thumbnail')
@worms.transaction
@worms.atomic
def generate_thumbnail(self, **special) -> pathclass.Path:
'''
special:
@ -1144,7 +1144,7 @@ class Photo(ObjectBase):
return hopeful_filepath
# Photo.rename_file already has @required_feature
@worms.transaction
@worms.atomic
def move_file(self, directory) -> None:
directory = pathclass.Path(directory)
directory.assert_is_directory()
@ -1195,7 +1195,7 @@ class Photo(ObjectBase):
self.duration = probe.audio.duration
@decorators.required_feature('photo.reload_metadata')
@worms.transaction
@worms.atomic
def reload_metadata(self, hash_kwargs=None) -> None:
'''
Load the file's height, width, etc as appropriate for this type of file.
@ -1252,7 +1252,7 @@ class Photo(ObjectBase):
self._uncache()
@decorators.required_feature('photo.edit')
@worms.transaction
@worms.atomic
def relocate(self, new_filepath) -> None:
'''
Point the Photo object to a different filepath.
@ -1287,7 +1287,7 @@ class Photo(ObjectBase):
self._uncache()
@decorators.required_feature('photo.add_remove_tag')
@worms.transaction
@worms.atomic
def remove_tag(self, tag) -> None:
tag = self.photodb.get_tag(name=tag)
@ -1305,7 +1305,7 @@ class Photo(ObjectBase):
self.photodb.update(table=Photo, pairs=data, where_key='id')
@decorators.required_feature('photo.add_remove_tag')
@worms.transaction
@worms.atomic
def remove_tags(self, tags) -> None:
tags = [self.photodb.get_tag(name=tag) for tag in tags]
@ -1324,7 +1324,7 @@ class Photo(ObjectBase):
self.photodb.update(table=Photo, pairs=data, where_key='id')
@decorators.required_feature('photo.edit')
@worms.transaction
@worms.atomic
def rename_file(self, new_filename, *, move=False) -> None:
'''
Rename the file on the disk as well as in the database.
@ -1411,7 +1411,7 @@ class Photo(ObjectBase):
self.__reinit__()
@decorators.required_feature('photo.edit')
@worms.transaction
@worms.atomic
def set_override_filename(self, new_filename) -> None:
new_filename = self.normalize_override_filename(new_filename)
@ -1425,7 +1425,7 @@ class Photo(ObjectBase):
self.__reinit__()
@decorators.required_feature('photo.edit')
@worms.transaction
@worms.atomic
def set_searchhidden(self, searchhidden) -> None:
data = {
'id': self.id,
@ -1537,7 +1537,7 @@ class Tag(ObjectBase, GroupableMixin):
photo.remove_tags(ancestors)
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def add_child(self, member):
'''
Raises exceptions.CantGroupSelf if member is self.
@ -1552,7 +1552,7 @@ class Tag(ObjectBase, GroupableMixin):
return ret
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def add_children(self, members):
ret = super().add_children(members)
if ret is BAIL:
@ -1562,7 +1562,7 @@ class Tag(ObjectBase, GroupableMixin):
return ret
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def add_synonym(self, synname) -> str:
'''
Raises any exceptions from photodb.normalize_tagname.
@ -1594,7 +1594,7 @@ class Tag(ObjectBase, GroupableMixin):
return synname
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def convert_to_synonym(self, mastertag) -> None:
'''
Convert this tag into a synonym for a different tag.
@ -1656,7 +1656,7 @@ class Tag(ObjectBase, GroupableMixin):
mastertag.add_synonym(self.name)
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def delete(self, *, delete_children=False) -> None:
log.info('Deleting %s.', self)
super().delete(delete_children=delete_children)
@ -1668,7 +1668,7 @@ class Tag(ObjectBase, GroupableMixin):
self.deleted = True
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def edit(self, description=None) -> None:
'''
Change the description. Leave None to keep current value.
@ -1718,7 +1718,7 @@ class Tag(ObjectBase, GroupableMixin):
return j
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def remove_child(self, *args, **kwargs):
ret = super().remove_child(*args, **kwargs)
if ret is BAIL:
@ -1728,7 +1728,7 @@ class Tag(ObjectBase, GroupableMixin):
return ret
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def remove_children(self, *args, **kwargs):
ret = super().remove_children(*args, **kwargs)
if ret is BAIL:
@ -1738,7 +1738,7 @@ class Tag(ObjectBase, GroupableMixin):
return ret
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def remove_synonym(self, synname) -> str:
'''
Delete a synonym.
@ -1770,7 +1770,7 @@ class Tag(ObjectBase, GroupableMixin):
return synname
@decorators.required_feature('tag.edit')
@worms.transaction
@worms.atomic
def rename(self, new_name, *, apply_to_synonyms=True) -> None:
'''
Rename the tag. Does not affect its relation to Photos or tag groups.
@ -1860,7 +1860,7 @@ class User(ObjectBase):
self.photodb.caches[User].remove(self.id)
@decorators.required_feature('user.edit')
@worms.transaction
@worms.atomic
def delete(self, *, disown_authored_things) -> None:
'''
If disown_authored_things is True then all of this user's albums,
@ -1978,7 +1978,7 @@ class User(ObjectBase):
return j
@decorators.required_feature('user.edit')
@worms.transaction
@worms.atomic
def set_display_name(self, display_name) -> None:
display_name = self.normalize_display_name(
display_name,
@ -1993,7 +1993,7 @@ class User(ObjectBase):
self._display_name = display_name
@decorators.required_feature('user.edit')
@worms.transaction
@worms.atomic
def set_password(self, password) -> None:
if not isinstance(password, bytes):
password = password.encode('utf-8')

View file

@ -9,6 +9,14 @@ import time
import types
import typing
from . import constants
from . import decorators
from . import exceptions
from . import helpers
from . import objects
from . import searchhelpers
from . import tag_export
from voussoirkit import cacheclass
from voussoirkit import configlayers
from voussoirkit import expressionmatch
@ -22,13 +30,6 @@ from voussoirkit import worms
log = vlogging.getLogger(__name__)
from . import constants
from . import decorators
from . import exceptions
from . import helpers
from . import objects
from . import searchhelpers
from . import tag_export
####################################################################################################
@ -85,7 +86,7 @@ class PDBAlbumMixin:
return self.get_root_objects(objects.Album)
@decorators.required_feature('album.new')
@worms.transaction
@worms.atomic
def new_album(
self,
title=None,
@ -130,7 +131,7 @@ class PDBAlbumMixin:
return album
@worms.transaction
@worms.atomic
def purge_deleted_associated_directories(self, albums=None) -> typing.Iterable[pathclass.Path]:
query = 'SELECT DISTINCT directory FROM album_associated_directories'
directories = self.select_column(query)
@ -148,7 +149,7 @@ class PDBAlbumMixin:
self.execute(query)
yield from directories
@worms.transaction
@worms.atomic
def purge_empty_albums(self, albums=None) -> typing.Iterable[objects.Album]:
if albums is None:
to_check = set(self.get_albums())
@ -188,7 +189,7 @@ class PDBBookmarkMixin:
return self.get_objects_by_sql(objects.Bookmark, query, bindings)
@decorators.required_feature('bookmark.new')
@worms.transaction
@worms.atomic
def new_bookmark(self, url, title=None, *, author=None) -> objects.Bookmark:
# These might raise exceptions.
title = objects.Bookmark.normalize_title(title)
@ -305,7 +306,7 @@ class PDBPhotoMixin:
return self.get_objects_by_sql(objects.Photo, query, bindings)
@decorators.required_feature('photo.new')
@worms.transaction
@worms.atomic
def new_photo(
self,
filepath,
@ -390,7 +391,7 @@ class PDBPhotoMixin:
return photo
@worms.transaction
@worms.atomic
def purge_deleted_files(self, photos=None) -> typing.Iterable[objects.Photo]:
'''
Delete Photos whose corresponding file on disk is missing.
@ -956,7 +957,7 @@ class PDBTagMixin:
return self.get_objects_by_sql(objects.Tag, query, bindings)
@decorators.required_feature('tag.new')
@worms.transaction
@worms.atomic
def new_tag(self, tagname, description=None, *, author=None) -> objects.Tag:
'''
Register a new tag and return the Tag object.
@ -1138,7 +1139,7 @@ class PDBUserMixin:
return user
@decorators.required_feature('user.new')
@worms.transaction
@worms.atomic
def new_user(self, username, password, *, display_name=None) -> objects.User:
# These might raise exceptions.
self.assert_valid_username(username)
@ -1177,7 +1178,7 @@ class PDBUtilMixin:
def __init__(self):
super().__init__()
@worms.transaction
@worms.atomic
def digest_directory(
self,
directory,
@ -1436,7 +1437,7 @@ class PDBUtilMixin:
if yield_albums:
yield from current_albums
@worms.transaction
@worms.atomic
def easybake(self, ebstring, author=None):
'''
Easily create tags, groups, and synonyms with a string like
@ -1570,7 +1571,7 @@ class PhotoDB(
Compare database's user_version against constants.DATABASE_VERSION,
raising exceptions.DatabaseOutOfDate if not correct.
'''
existing = self.execute('PRAGMA user_version').fetchone()[0]
existing = self.pragma_read('user_version')
if existing != constants.DATABASE_VERSION:
raise exceptions.DatabaseOutOfDate(
existing=existing,
@ -1580,8 +1581,10 @@ class PhotoDB(
def _first_time_setup(self):
log.info('Running first-time database setup.')
self.executescript(constants.DB_INIT)
self.commit()
with self.transaction:
self._load_pragmas()
self.pragma_write('user_version', constants.DATABASE_VERSION)
self.executescript(constants.DB_INIT)
def _init_caches(self):
self.caches = {
@ -1600,8 +1603,8 @@ class PhotoDB(
def _init_sql(self, create, skip_version_check):
if self.ephemeral:
existing_database = False
self.sql = sqlite3.connect(':memory:')
self.sql.row_factory = sqlite3.Row
self.sql_read = self._make_sqlite_read_connection(':memory:')
self.sql_write = self._make_sqlite_write_connection(':memory:')
self._first_time_setup()
return
@ -1613,21 +1616,21 @@ class PhotoDB(
raise FileNotFoundError(msg)
self.data_directory.makedirs(exist_ok=True)
log.debug('Connecting to sqlite file "%s".', self.database_filepath.absolute_path)
self.sql = sqlite3.connect(self.database_filepath.absolute_path)
self.sql.row_factory = sqlite3.Row
self.sql_read = self._make_sqlite_read_connection(self.database_filepath)
self.sql_write = self._make_sqlite_write_connection(self.database_filepath)
if existing_database:
if not skip_version_check:
self._check_version()
self._load_pragmas()
with self.transaction:
self._load_pragmas()
else:
self._first_time_setup()
def _load_pragmas(self):
log.debug('Reloading pragmas.')
self.executescript(constants.DB_PRAGMAS)
self.commit()
self.pragma_write('cache_size', 10000)
self.pragma_write('foreign_keys', 'on')
# Will add -> PhotoDB when forward references are supported
@classmethod

View file

@ -65,7 +65,14 @@ def get_photos_by_glob(pattern):
if pattern == '**':
return search_in_cwd(yield_photos=True, yield_albums=False)
for file in pathclass.glob_files(pattern):
as_path = pathclass.Path(pattern)
if as_path.is_directory:
files = as_path.listdir_files()
else:
files = pathclass.glob_files(pattern)
for file in files:
try:
photo = photodb.get_photo_by_path(file)
yield photo
@ -76,7 +83,7 @@ def get_photos_by_globs(patterns):
for pattern in patterns:
yield from get_photos_by_glob(pattern)
def get_photos_from_args(args):
def get_photos_from_args(args, fallback_search_in_cwd=False):
load_photodb()
photos = []
@ -92,6 +99,9 @@ def get_photos_from_args(args):
if args.photo_search_args:
photos.extend(search_by_argparse(args.photo_search_args, yield_photos=True))
if (not photos) and fallback_search_in_cwd:
photos.extend(search_in_cwd(yield_photos=True, yield_albums=False))
return photos
def get_albums_from_args(args):
@ -158,18 +168,20 @@ def add_remove_tag_argparse(args, action):
need_commit = False
for photo in photos:
if action == 'add':
photo.add_tag(tag)
elif action == 'remove':
photo.remove_tag(tag)
need_commit = True
with photodb.transaction:
for photo in photos:
if action == 'add':
photo.add_tag(tag)
elif action == 'remove':
photo.remove_tag(tag)
need_commit = True
if not need_commit:
return 0
if not need_commit:
return 0
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -178,15 +190,18 @@ def delete_albums_argparse(args):
need_commit = False
albums = get_albums_from_args(args)
for album in albums:
album.delete()
need_commit = True
if not need_commit:
return 0
with photodb.transaction:
for album in albums:
album.delete()
need_commit = True
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not need_commit:
return 0
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -195,15 +210,18 @@ def delete_photos_argparse(args):
need_commit = False
photos = get_photos_from_args(args)
for photo in photos:
photo.delete(delete_file=args.delete_file)
need_commit = True
if not need_commit:
return 0
with photodb.transaction:
for photo in photos:
photo.delete(delete_file=args.delete_file)
need_commit = True
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not need_commit:
return 0
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -216,41 +234,46 @@ def digest_directory_argparse(args):
load_photodb()
need_commit = False
for directory in directories:
digest = photodb.digest_directory(
directory,
exclude_directories=args.exclude_directories,
exclude_filenames=args.exclude_filenames,
glob_directories=args.glob_directories,
glob_filenames=args.glob_filenames,
hash_kwargs={'bytes_per_second': args.hash_bytes_per_second},
make_albums=args.make_albums,
new_photo_ratelimit=args.ratelimit,
recurse=args.recurse,
yield_albums=True,
yield_photos=True,
)
for result in digest:
# print(result)
need_commit = True
with photodb.transaction:
for directory in directories:
digest = photodb.digest_directory(
directory,
exclude_directories=args.exclude_directories,
exclude_filenames=args.exclude_filenames,
glob_directories=args.glob_directories,
glob_filenames=args.glob_filenames,
hash_kwargs={'bytes_per_second': args.hash_bytes_per_second},
make_albums=args.make_albums,
new_photo_ratelimit=args.ratelimit,
recurse=args.recurse,
yield_albums=True,
yield_photos=True,
)
for result in digest:
# print(result)
need_commit = True
if not need_commit:
return 0
if not need_commit:
return 0
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
def easybake_argparse(args):
load_photodb()
for eb_string in args.eb_strings:
notes = photodb.easybake(eb_string)
for (action, tagname) in notes:
print(action, tagname)
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
with photodb.transaction:
for eb_string in args.eb_strings:
notes = photodb.easybake(eb_string)
for (action, tagname) in notes:
print(action, tagname)
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -310,18 +333,21 @@ def generate_thumbnail_argparse(args):
photos = search_in_cwd(yield_photos=True, yield_albums=False)
need_commit = False
try:
for photo in photos:
photo.generate_thumbnail()
need_commit = True
except KeyboardInterrupt:
pass
if not need_commit:
return 0
with photodb.transaction:
try:
for photo in photos:
photo.generate_thumbnail()
need_commit = True
except KeyboardInterrupt:
pass
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not need_commit:
return 0
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -335,7 +361,6 @@ def init_argparse(args):
pipeable.stderr(f'PhotoDB {photodb} already exists.')
return 0
photodb = etiquette.photodb.PhotoDB(create=True)
photodb.commit()
return 0
def purge_deleted_files_argparse(args):
@ -348,15 +373,17 @@ def purge_deleted_files_argparse(args):
need_commit = False
for deleted in photodb.purge_deleted_files(photos):
need_commit = True
print(deleted)
with photodb.transaction:
for deleted in photodb.purge_deleted_files(photos):
need_commit = True
print(deleted)
if not need_commit:
return 0
if not need_commit:
return 0
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -373,25 +400,24 @@ def purge_empty_albums_argparse(args):
need_commit = False
for deleted in photodb.purge_empty_albums(albums):
need_commit = True
print(deleted)
with photodb.transaction:
for deleted in photodb.purge_empty_albums(albums):
need_commit = True
print(deleted)
if not need_commit:
return 0
if not need_commit:
return 0
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
def reload_metadata_argparse(args):
load_photodb()
if args.any_photo_args:
photos = get_photos_from_args(args)
else:
photos = search_in_cwd(yield_photos=True, yield_albums=False)
photos = get_photos_from_args(args)
hash_kwargs = {
'bytes_per_second': args.hash_bytes_per_second,
@ -399,40 +425,45 @@ def reload_metadata_argparse(args):
}
need_commit = False
try:
for photo in photos:
if not photo.real_path.is_file:
continue
need_reload = (
args.force or
photo.mtime != photo.real_path.stat.st_mtime or
photo.bytes != photo.real_path.stat.st_size
)
with photodb.transaction:
try:
for photo in photos:
if not photo.real_path.is_file:
continue
if not need_reload:
continue
photo.reload_metadata(hash_kwargs=hash_kwargs)
need_commit = True
except KeyboardInterrupt:
pass
need_reload = (
args.force or
photo.mtime != photo.real_path.stat.st_mtime or
photo.bytes != photo.real_path.stat.st_size
)
if not need_commit:
return 0
if not need_reload:
continue
photo.reload_metadata(hash_kwargs=hash_kwargs)
need_commit = True
except KeyboardInterrupt:
pass
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not need_commit:
return 0
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
def relocate_argparse(args):
load_photodb()
photo = photodb.get_photo(args.photo_id)
photo.relocate(args.filepath)
with photodb.transaction:
photo = photodb.get_photo(args.photo_id)
photo.relocate(args.filepath)
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -477,12 +508,14 @@ def set_unset_searchhidden_argparse(args, searchhidden):
else:
photos = search_in_cwd(yield_photos=True, yield_albums=False)
for photo in photos:
print(photo)
photo.set_searchhidden(searchhidden)
with photodb.transaction:
for photo in photos:
print(photo)
photo.set_searchhidden(searchhidden)
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -514,17 +547,19 @@ def tag_breplace_argparse(args):
for (tag_name, new_name, printline) in renames:
print(printline)
if not interactive.getpermission('Ok?', must_pick=True):
return 0
return 1
for (tag_name, new_name, printline) in renames:
print(printline)
tag = photodb.get_tag(tag_name)
tag.rename(new_name)
if args.set_synonym:
tag.add_synonym(tag_name)
with photodb.transaction:
for (tag_name, new_name, printline) in renames:
print(printline)
tag = photodb.get_tag(tag_name)
tag.rename(new_name)
if args.set_synonym:
tag.add_synonym(tag_name)
if args.autoyes or interactive.getpermission('Commit?'):
photodb.commit()
if not (args.autoyes or interactive.getpermission('Commit?')):
photodb.rollback()
return 1
return 0
@ -1577,17 +1612,17 @@ def main(argv):
##
def postprocessor(args):
if hasattr(args, 'photo_search_args'):
if getattr(args, 'photo_search_args', None) is not None:
args.photo_search_args = p_search.parse_args(args.photo_search_args)
else:
args.photo_search_args = None
if hasattr(args, 'album_search_args'):
if getattr(args, 'album_search_args', None) is not None:
args.album_search_args = p_search.parse_args(args.album_search_args)
else:
args.album_search_args = None
if hasattr(args, 'photo_id_args'):
if getattr(args, 'photo_id_args', None) is not None:
args.photo_id_args = [
photo_id
for arg in args.photo_id_args
@ -1596,7 +1631,7 @@ def main(argv):
else:
args.photo_id_args = None
if hasattr(args, 'album_id_args'):
if getattr(args, 'album_id_args', None) is not None:
args.album_id_args = [
album_id
for arg in args.album_id_args
@ -1605,11 +1640,10 @@ def main(argv):
else:
args.album_id_args = None
if not hasattr(args, 'globs'):
if not getattr(args, 'globs', None) is not None:
args.globs = None
if not hasattr(args, 'glob'):
if not getattr(args, 'glob', None) is not None:
args.glob = None
args.any_photo_args = bool(
@ -1625,7 +1659,7 @@ def main(argv):
return args
try:
return betterhelp.go(parser, argv)
return betterhelp.go(parser, argv, args_postprocessor=postprocessor)
except etiquette.exceptions.NoClosestPhotoDB as exc:
pipeable.stderr(exc.error_message)
pipeable.stderr('Try `etiquette_cli.py init` to create the database.')

View file

@ -58,51 +58,52 @@ def get_album_zip(album_id):
@site.route('/album/<album_id>/add_child', methods=['POST'])
@flasktools.required_fields(['child_id'], forbid_whitespace=True)
def post_album_add_child(album_id):
album = common.P_album(album_id, response_type='json')
child_ids = stringtools.comma_space_split(request.form['child_id'])
children = list(common.P_albums(child_ids, response_type='json'))
print(children)
album.add_children(children, commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
children = list(common.P_albums(child_ids, response_type='json'))
print(children)
album.add_children(children)
response = album.jsonify()
return flasktools.json_response(response)
@site.route('/album/<album_id>/remove_child', methods=['POST'])
@flasktools.required_fields(['child_id'], forbid_whitespace=True)
def post_album_remove_child(album_id):
album = common.P_album(album_id, response_type='json')
child_ids = stringtools.comma_space_split(request.form['child_id'])
children = list(common.P_albums(child_ids, response_type='json'))
album.remove_children(children, commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
children = list(common.P_albums(child_ids, response_type='json'))
album.remove_children(children)
response = album.jsonify()
return flasktools.json_response(response)
@site.route('/album/<album_id>/remove_thumbnail_photo', methods=['POST'])
def post_album_remove_thumbnail_photo(album_id):
album = common.P_album(album_id, response_type='json')
album.set_thumbnail_photo(None)
common.P.commit(message='album remove thumbnail photo endpoint')
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
album.set_thumbnail_photo(None)
return flasktools.json_response(album.jsonify())
@site.route('/album/<album_id>/refresh_directories', methods=['POST'])
def post_album_refresh_directories(album_id):
album = common.P_album(album_id, response_type='json')
for directory in album.get_associated_directories():
if not directory.is_dir:
continue
digest = common.P.digest_directory(directory, new_photo_ratelimit=0.1)
gentools.run(digest)
common.P.commit(message='refresh album directories endpoint')
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
for directory in album.get_associated_directories():
if not directory.is_dir:
continue
digest = common.P.digest_directory(directory, new_photo_ratelimit=0.1)
gentools.run(digest)
return flasktools.json_response({})
@site.route('/album/<album_id>/set_thumbnail_photo', methods=['POST'])
@flasktools.required_fields(['photo_id'], forbid_whitespace=True)
def post_album_set_thumbnail_photo(album_id):
album = common.P_album(album_id, response_type='json')
photo = common.P_photo(request.form['photo_id'], response_type='json')
album.set_thumbnail_photo(photo)
common.P.commit(message='album set thumbnail photo endpoint')
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
photo = common.P_photo(request.form['photo_id'], response_type='json')
album.set_thumbnail_photo(photo)
return flasktools.json_response(album.jsonify())
# Album photo operations ###########################################################################
@ -113,11 +114,12 @@ def post_album_add_photo(album_id):
'''
Add a photo or photos to this album.
'''
album = common.P_album(album_id, response_type='json')
photo_ids = stringtools.comma_space_split(request.form['photo_id'])
photos = list(common.P_photos(photo_ids, response_type='json'))
album.add_photos(photos, commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
photos = list(common.P_photos(photo_ids, response_type='json'))
album.add_photos(photos)
response = album.jsonify()
return flasktools.json_response(response)
@ -127,11 +129,11 @@ def post_album_remove_photo(album_id):
'''
Remove a photo or photos from this album.
'''
album = common.P_album(album_id, response_type='json')
photo_ids = stringtools.comma_space_split(request.form['photo_id'])
photos = list(common.P_photos(photo_ids, response_type='json'))
album.remove_photos(photos, commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
photos = list(common.P_photos(photo_ids, response_type='json'))
album.remove_photos(photos)
response = album.jsonify()
return flasktools.json_response(response)
@ -143,17 +145,18 @@ def post_album_add_tag(album_id):
Apply a tag to every photo in the album.
'''
response = {}
album = common.P_album(album_id, response_type='json')
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
tag = request.form['tagname'].strip()
try:
tag = common.P_tag(tag, response_type='json')
except etiquette.exceptions.NoSuchTag as exc:
response = exc.jsonify()
return flasktools.json_response(response, status=404)
recursive = request.form.get('recursive', False)
recursive = stringtools.truthystring(recursive)
album.add_tag_to_all(tag, nested_children=recursive, commit=True)
tag = request.form['tagname'].strip()
try:
tag = common.P_tag(tag, response_type='json')
except etiquette.exceptions.NoSuchTag as exc:
response = exc.jsonify()
return flasktools.json_response(response, status=404)
recursive = request.form.get('recursive', False)
recursive = stringtools.truthystring(recursive)
album.add_tag_to_all(tag, nested_children=recursive)
response['action'] = 'add_tag'
response['tagname'] = tag.name
return flasktools.json_response(response)
@ -165,11 +168,13 @@ def post_album_edit(album_id):
'''
Edit the title / description.
'''
album = common.P_album(album_id, response_type='json')
title = request.form.get('title', None)
description = request.form.get('description', None)
album.edit(title=title, description=description, commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
album.edit(title=title, description=description)
response = album.jsonify(minimal=True)
return flasktools.json_response(response)
@ -234,16 +239,17 @@ def post_albums_create():
user = session_manager.get(request).user
album = common.P.new_album(title=title, description=description, author=user)
if parent_id is not None:
parent.add_child(album)
common.P.commit('create album endpoint')
with common.P.transaction:
album = common.P.new_album(title=title, description=description, author=user)
if parent_id is not None:
parent.add_child(album)
response = album.jsonify(minimal=False)
return flasktools.json_response(response)
@site.route('/album/<album_id>/delete', methods=['POST'])
def post_album_delete(album_id):
album = common.P_album(album_id, response_type='json')
album.delete(commit=True)
with common.P.transaction:
album = common.P_album(album_id, response_type='json')
album.delete()
return flasktools.json_response({})

View file

@ -19,11 +19,12 @@ def get_bookmark_json(bookmark_id):
@site.route('/bookmark/<bookmark_id>/edit', methods=['POST'])
def post_bookmark_edit(bookmark_id):
bookmark = common.P_bookmark(bookmark_id, response_type='json')
# Emptystring is okay for titles, but not for URL.
title = request.form.get('title', None)
url = request.form.get('url', None) or None
bookmark.edit(title=title, url=url, commit=True)
with common.P.transaction:
bookmark = common.P_bookmark(bookmark_id, response_type='json')
# Emptystring is okay for titles, but not for URL.
title = request.form.get('title', None)
url = request.form.get('url', None) or None
bookmark.edit(title=title, url=url)
response = bookmark.jsonify()
response = flasktools.json_response(response)
@ -49,13 +50,15 @@ def post_bookmark_create():
url = request.form['url']
title = request.form.get('title', None)
user = session_manager.get(request).user
bookmark = common.P.new_bookmark(url=url, title=title, author=user, commit=True)
with common.P.transaction:
bookmark = common.P.new_bookmark(url=url, title=title, author=user)
response = bookmark.jsonify()
response = flasktools.json_response(response)
return response
@site.route('/bookmark/<bookmark_id>/delete', methods=['POST'])
def post_bookmark_delete(bookmark_id):
bookmark = common.P_bookmark(bookmark_id, response_type='json')
bookmark.delete(commit=True)
with common.P.transaction:
bookmark = common.P_bookmark(bookmark_id, response_type='json')
bookmark.delete()
return flasktools.json_response({})

View file

@ -75,11 +75,11 @@ def get_thumbnail(photo_id):
@site.route('/photo/<photo_id>/delete', methods=['POST'])
def post_photo_delete(photo_id):
print(photo_id)
photo = common.P_photo(photo_id, response_type='json')
delete_file = request.form.get('delete_file', False)
delete_file = stringtools.truthystring(delete_file)
photo.delete(delete_file=delete_file, commit=True)
with common.P.transaction:
photo = common.P_photo(photo_id, response_type='json')
photo.delete(delete_file=delete_file)
return flasktools.json_response({})
# Photo tag operations #############################################################################
@ -91,12 +91,12 @@ def post_photo_add_remove_tag_core(photo_ids, tagname, add_or_remove):
photos = list(common.P_photos(photo_ids, response_type='json'))
tag = common.P_tag(tagname, response_type='json')
for photo in photos:
if add_or_remove == 'add':
photo.add_tag(tag)
elif add_or_remove == 'remove':
photo.remove_tag(tag)
common.P.commit('photo add remove tag core')
with common.P.transaction:
for photo in photos:
if add_or_remove == 'add':
photo.add_tag(tag)
elif add_or_remove == 'remove':
photo.remove_tag(tag)
response = {'action': add_or_remove, 'tagname': tag.name}
return flasktools.json_response(response)
@ -120,10 +120,10 @@ def post_photo_copy_tags(photo_id):
'''
Copy the tags from another photo.
'''
photo = common.P_photo(photo_id, response_type='json')
other = common.P_photo(request.form['other_photo'], response_type='json')
photo.copy_tags(other)
common.P.commit('photo copy tags')
with common.P.transaction:
photo = common.P_photo(photo_id, response_type='json')
other = common.P_photo(request.form['other_photo'], response_type='json')
photo.copy_tags(other)
return flasktools.json_response([tag.jsonify(minimal=True) for tag in photo.get_tags()])
@site.route('/photo/<photo_id>/remove_tag', methods=['POST'])
@ -164,10 +164,10 @@ def post_batch_photos_remove_tag():
@site.route('/photo/<photo_id>/generate_thumbnail', methods=['POST'])
def post_photo_generate_thumbnail(photo_id):
special = request.form.to_dict()
special.pop('commit', None)
photo = common.P_photo(photo_id, response_type='json')
photo.generate_thumbnail(commit=True, **special)
with common.P.transaction:
photo = common.P_photo(photo_id, response_type='json')
photo.generate_thumbnail(**special)
response = flasktools.json_response({})
return response
@ -176,22 +176,21 @@ def post_photo_refresh_metadata_core(photo_ids):
if isinstance(photo_ids, str):
photo_ids = stringtools.comma_space_split(photo_ids)
photos = list(common.P_photos(photo_ids, response_type='json'))
with common.P.transaction:
photos = list(common.P_photos(photo_ids, response_type='json'))
for photo in photos:
photo._uncache()
photo = common.P_photo(photo.id, response_type='json')
try:
photo.reload_metadata()
except pathclass.NotFile:
flask.abort(404)
if photo.thumbnail is None:
for photo in photos:
photo._uncache()
photo = common.P_photo(photo.id, response_type='json')
try:
photo.generate_thumbnail()
except Exception:
log.warning(traceback.format_exc())
common.P.commit('photo refresh metadata core')
photo.reload_metadata()
except pathclass.NotFile:
flask.abort(404)
if photo.thumbnail is None:
try:
photo.generate_thumbnail()
except Exception:
log.warning(traceback.format_exc())
return flasktools.json_response({})
@ -208,26 +207,27 @@ def post_batch_photos_refresh_metadata():
@site.route('/photo/<photo_id>/set_searchhidden', methods=['POST'])
def post_photo_set_searchhidden(photo_id):
photo = common.P_photo(photo_id, response_type='json')
photo.set_searchhidden(True)
with common.P.transaction:
photo = common.P_photo(photo_id, response_type='json')
photo.set_searchhidden(True)
return flasktools.json_response({})
@site.route('/photo/<photo_id>/unset_searchhidden', methods=['POST'])
def post_photo_unset_searchhidden(photo_id):
photo = common.P_photo(photo_id, response_type='json')
photo.set_searchhidden(False)
with common.P.transaction:
photo = common.P_photo(photo_id, response_type='json')
photo.set_searchhidden(False)
return flasktools.json_response({})
def post_batch_photos_searchhidden_core(photo_ids, searchhidden):
if isinstance(photo_ids, str):
photo_ids = stringtools.comma_space_split(photo_ids)
photos = list(common.P_photos(photo_ids, response_type='json'))
with common.P.transaction:
photos = list(common.P_photos(photo_ids, response_type='json'))
for photo in photos:
photo.set_searchhidden(searchhidden)
common.P.commit('photo set searchhidden core')
for photo in photos:
photo.set_searchhidden(searchhidden)
return flasktools.json_response({})

View file

@ -44,24 +44,25 @@ def get_tag_json(specific_tag_name):
@site.route('/tag/<tagname>/edit', methods=['POST'])
def post_tag_edit(tagname):
tag = common.P_tag(tagname, response_type='json')
name = request.form.get('name', '').strip()
if name:
tag.rename(name)
with common.P.transaction:
tag = common.P_tag(tagname, response_type='json')
name = request.form.get('name', '').strip()
if name:
tag.rename(name)
description = request.form.get('description', None)
tag.edit(description=description, commit=True)
description = request.form.get('description', None)
tag.edit(description=description)
response = tag.jsonify()
response = flasktools.json_response(response)
response = flasktools.json_response(tag.jsonify())
return response
@site.route('/tag/<tagname>/add_child', methods=['POST'])
@flasktools.required_fields(['child_name'], forbid_whitespace=True)
def post_tag_add_child(tagname):
parent = common.P_tag(tagname, response_type='json')
child = common.P_tag(request.form['child_name'], response_type='json')
parent.add_child(child, commit=True)
with common.P.transaction:
parent = common.P_tag(tagname, response_type='json')
child = common.P_tag(request.form['child_name'], response_type='json')
parent.add_child(child)
response = {'action': 'add_child', 'tagname': f'{parent.name}.{child.name}'}
return flasktools.json_response(response)
@ -70,8 +71,9 @@ def post_tag_add_child(tagname):
def post_tag_add_synonym(tagname):
syn_name = request.form['syn_name']
master_tag = common.P_tag(tagname, response_type='json')
syn_name = master_tag.add_synonym(syn_name, commit=True)
with common.P.transaction:
master_tag = common.P_tag(tagname, response_type='json')
syn_name = master_tag.add_synonym(syn_name)
response = {'action': 'add_synonym', 'synonym': syn_name}
return flasktools.json_response(response)
@ -79,9 +81,10 @@ def post_tag_add_synonym(tagname):
@site.route('/tag/<tagname>/remove_child', methods=['POST'])
@flasktools.required_fields(['child_name'], forbid_whitespace=True)
def post_tag_remove_child(tagname):
parent = common.P_tag(tagname, response_type='json')
child = common.P_tag(request.form['child_name'], response_type='json')
parent.remove_child(child, commit=True)
with common.P.transaction:
parent = common.P_tag(tagname, response_type='json')
child = common.P_tag(request.form['child_name'], response_type='json')
parent.remove_child(child)
response = {'action': 'remove_child', 'tagname': f'{parent.name}.{child.name}'}
return flasktools.json_response(response)
@ -90,8 +93,9 @@ def post_tag_remove_child(tagname):
def post_tag_remove_synonym(tagname):
syn_name = request.form['syn_name']
master_tag = common.P_tag(tagname, response_type='json')
syn_name = master_tag.remove_synonym(syn_name, commit=True)
with common.P.transaction:
master_tag = common.P_tag(tagname, response_type='json')
syn_name = master_tag.remove_synonym(syn_name)
response = {'action': 'delete_synonym', 'synonym': syn_name}
return flasktools.json_response(response)
@ -163,7 +167,8 @@ def post_tag_create():
name = request.form['name']
description = request.form.get('description', None)
tag = common.P.new_tag(name, description, author=session_manager.get(request).user, commit=True)
with common.P.transaction:
tag = common.P.new_tag(name, description, author=session_manager.get(request).user)
response = tag.jsonify()
return flasktools.json_response(response)
@ -172,13 +177,15 @@ def post_tag_create():
def post_tag_easybake():
easybake_string = request.form['easybake_string']
notes = common.P.easybake(easybake_string, author=session_manager.get(request).user, commit=True)
with common.P.transaction:
notes = common.P.easybake(easybake_string, author=session_manager.get(request).user)
notes = [{'action': action, 'tagname': tagname} for (action, tagname) in notes]
return flasktools.json_response(notes)
@site.route('/tag/<tagname>/delete', methods=['POST'])
def post_tag_delete(tagname):
tag = common.P_tag(tagname, response_type='json')
tag.delete(commit=True)
with common.P.transaction:
tag = common.P_tag(tagname, response_type='json')
tag.delete()
response = {'action': 'delete_tag', 'tagname': tag.name}
return flasktools.json_response(response)

View file

@ -47,9 +47,8 @@ def post_user_edit(username):
display_name = request.form.get('display_name')
if display_name is not None:
user.set_display_name(display_name)
common.P.commit()
with common.P.transaction:
user.set_display_name(display_name)
return flasktools.json_response(user.jsonify())
@ -127,7 +126,8 @@ def post_register():
}
return flasktools.json_response(response, status=422)
user = common.P.new_user(username, password_1, display_name=display_name, commit=True)
with common.P.transaction:
user = common.P.new_user(username, password_1, display_name=display_name)
session = sessions.Session(request, user)
session_manager.add(session)

View file

@ -2,8 +2,12 @@ import argparse
import os
import sys
from voussoirkit import vlogging
import etiquette
log = vlogging.get_logger(__name__, 'database_upgrader')
class Migrator:
'''
Many of the upgraders involve adding columns. ALTER TABLE ADD COLUMN only
@ -45,8 +49,7 @@ class Migrator:
# be pointing to the version of B which has not been reconstructed yet,
# which is about to get renamed to B_old and then A's reference will be
# broken.
self.photodb.execute('PRAGMA foreign_keys = OFF')
self.photodb.execute('BEGIN')
self.photodb.pragma_write('foreign_keys', 'OFF')
for (name, table) in self.tables.items():
if name not in self.existing_tables:
continue
@ -65,16 +68,14 @@ class Migrator:
for (name, query) in self.indices:
self.photodb.execute(query)
self.photodb.pragma_write('foreign_keys', 'ON')
def upgrade_1_to_2(photodb):
'''
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).
'''
photodb.executescript('''
BEGIN;
ALTER TABLE photos ADD COLUMN tagged_at INT;
''')
photodb.execute('ALTER TABLE photos ADD COLUMN tagged_at INT')
def upgrade_2_to_3(photodb):
'''
@ -83,8 +84,6 @@ def upgrade_2_to_3(photodb):
Plus some indices.
'''
photodb.executescript('''
BEGIN;
CREATE TABLE users(
id TEXT,
username TEXT COLLATE NOCASE,
@ -102,8 +101,6 @@ def upgrade_3_to_4(photodb):
Add an `author_id` column to Photos.
'''
photodb.executescript('''
BEGIN;
ALTER TABLE photos ADD COLUMN author_id TEXT;
CREATE INDEX IF NOT EXISTS index_photo_author ON photos(author_id);
@ -114,8 +111,6 @@ def upgrade_4_to_5(photodb):
Add table `bookmarks` and its indices.
'''
photodb.executescript('''
BEGIN;
CREATE TABLE bookmarks(
id TEXT,
title TEXT,
@ -259,18 +254,13 @@ def upgrade_7_to_8(photodb):
'''
Give the Tags table a description field.
'''
photodb.executescript('''
BEGIN;
ALTER TABLE tags ADD COLUMN description TEXT;
''')
photodb.executescript('ALTER TABLE tags ADD COLUMN description TEXT')
def upgrade_8_to_9(photodb):
'''
Give the Photos table a searchhidden field.
'''
photodb.executescript('''
BEGIN;
ALTER TABLE photos ADD COLUMN searchhidden INT;
UPDATE photos SET searchhidden = 0;
@ -351,9 +341,7 @@ def upgrade_11_to_12(photodb):
improve the speed of individual relation searching, important for the
new intersection-based search.
'''
photodb.executescript('''
BEGIN;
photodb.execute('''
CREATE INDEX IF NOT EXISTS index_photo_tag_rel_photoid_tagid on photo_tag_rel(photoid, tagid);
''')
@ -679,11 +667,12 @@ def upgrade_all(data_directory):
'''
photodb = etiquette.photodb.PhotoDB(data_directory, create=False, skip_version_check=True)
current_version = photodb.execute('PRAGMA user_version').fetchone()[0]
current_version = photodb.pragma_read('user_version')
needed_version = etiquette.constants.DATABASE_VERSION
if current_version == needed_version:
print('Already up to date with version %d.' % needed_version)
photodb.close()
return
for version_number in range(current_version + 1, needed_version + 1):
@ -691,22 +680,20 @@ def upgrade_all(data_directory):
upgrade_function = 'upgrade_%d_to_%d' % (current_version, version_number)
upgrade_function = eval(upgrade_function)
try:
photodb.execute('PRAGMA foreign_keys = ON')
photodb.pragma_write('journal_mode', 'wal')
with photodb.transaction:
photodb.pragma_write('foreign_keys', 'ON')
upgrade_function(photodb)
except Exception as exc:
photodb.rollback()
raise
else:
photodb.sql.cursor().execute('PRAGMA user_version = %d' % version_number)
photodb.commit()
photodb.pragma_write('user_version', version_number)
current_version = version_number
photodb.close()
print('Upgrades finished.')
def upgrade_all_argparse(args):
return upgrade_all(data_directory=args.data_directory)
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser()