Use new worms version.
This commit is contained in:
parent
a436dafa9c
commit
0e00a1e981
10 changed files with 388 additions and 361 deletions
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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({})
|
||||
|
|
|
@ -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({})
|
||||
|
|
|
@ -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({})
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Reference in a new issue