Make associated_directory its own table so albums can have multiple
Also renamed a bunch of indices
This commit is contained in:
		
							parent
							
								
									440941077f
								
							
						
					
					
						commit
						efa930f4a8
					
				
					 4 changed files with 137 additions and 73 deletions
				
			
		|  | @ -31,11 +31,14 @@ SQL_LASTID_COLUMNS = [ | ||||||
|     'table', |     'table', | ||||||
|     'last_id', |     'last_id', | ||||||
| ] | ] | ||||||
|  | SQL_ALBUM_DIRECTORY_COLUMNS = [ | ||||||
|  |     'albumid', | ||||||
|  |     'directory', | ||||||
|  | ] | ||||||
| SQL_ALBUM_COLUMNS = [ | SQL_ALBUM_COLUMNS = [ | ||||||
|     'id', |     'id', | ||||||
|     'title', |     'title', | ||||||
|     'description', |     'description', | ||||||
|     'associated_directory', |  | ||||||
| ] | ] | ||||||
| SQL_BOOKMARK_COLUMNS = [ | SQL_BOOKMARK_COLUMNS = [ | ||||||
|     'id', |     'id', | ||||||
|  | @ -92,6 +95,7 @@ SQL_USER_COLUMNS = [ | ||||||
| 
 | 
 | ||||||
| _sql_dictify = lambda columns: {key:index for (index, key) in enumerate(columns)} | _sql_dictify = lambda columns: {key:index for (index, key) in enumerate(columns)} | ||||||
| SQL_ALBUM = _sql_dictify(SQL_ALBUM_COLUMNS) | SQL_ALBUM = _sql_dictify(SQL_ALBUM_COLUMNS) | ||||||
|  | SQL_ALBUM_DIRECTORY = _sql_dictify(SQL_ALBUM_DIRECTORY_COLUMNS) | ||||||
| SQL_ALBUMGROUP = _sql_dictify(SQL_ALBUMGROUP_COLUMNS) | SQL_ALBUMGROUP = _sql_dictify(SQL_ALBUMGROUP_COLUMNS) | ||||||
| SQL_BOOKMARK = _sql_dictify(SQL_BOOKMARK_COLUMNS) | SQL_BOOKMARK = _sql_dictify(SQL_BOOKMARK_COLUMNS) | ||||||
| SQL_ALBUMPHOTO = _sql_dictify(SQL_ALBUMPHOTO_COLUMNS) | SQL_ALBUMPHOTO = _sql_dictify(SQL_ALBUMPHOTO_COLUMNS) | ||||||
|  |  | ||||||
|  | @ -54,6 +54,13 @@ class NoSuchUser(NoSuch): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # EXISTS | # EXISTS | ||||||
|  | @with_error_type | ||||||
|  | class AlbumExists(WithFormat): | ||||||
|  |     error_message = 'Album "{}" already exists.' | ||||||
|  |     def __init__(self, album): | ||||||
|  |         self.album = album | ||||||
|  |         WithFormat.__init__(self, album.id) | ||||||
|  | 
 | ||||||
| @with_error_type | @with_error_type | ||||||
| class GroupExists(WithFormat): | class GroupExists(WithFormat): | ||||||
|     error_message = '{member} already in group {group}' |     error_message = '{member} already in group {group}' | ||||||
|  |  | ||||||
|  | @ -29,23 +29,64 @@ logging.getLogger('PIL.PngImagePlugin').setLevel(logging.WARNING) | ||||||
| # Note: Setting user_version pragma in init sequence is safe because it only | # Note: Setting user_version pragma in init sequence is safe because it only | ||||||
| # happens after the out-of-date check occurs, so no chance of accidentally | # happens after the out-of-date check occurs, so no chance of accidentally | ||||||
| # overwriting it. | # overwriting it. | ||||||
| DATABASE_VERSION = 6 | DATABASE_VERSION = 7 | ||||||
| DB_INIT = ''' | DB_INIT = ''' | ||||||
| PRAGMA count_changes = OFF; | PRAGMA count_changes = OFF; | ||||||
| PRAGMA cache_size = 10000; | PRAGMA cache_size = 10000; | ||||||
| PRAGMA user_version = {user_version}; | PRAGMA user_version = {user_version}; | ||||||
|  | 
 | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
|  | CREATE TABLE IF NOT EXISTS album_associated_directories( | ||||||
|  |     albumid TEXT, | ||||||
|  |     directory TEXT COLLATE NOCASE | ||||||
|  | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_associated_directories_albumid on | ||||||
|  |     album_associated_directories(albumid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_associated_directories_directory on | ||||||
|  |     album_associated_directories(directory); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
|  | CREATE TABLE IF NOT EXISTS album_group_rel( | ||||||
|  |     parentid TEXT, | ||||||
|  |     memberid TEXT | ||||||
|  | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_group_rel_parentid on album_group_rel(parentid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_group_rel_memberid on album_group_rel(memberid); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
|  | CREATE TABLE IF NOT EXISTS album_photo_rel( | ||||||
|  |     albumid TEXT, | ||||||
|  |     photoid TEXT | ||||||
|  | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_photo_rel_albumid on album_photo_rel(albumid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_album_photo_rel_photoid on album_photo_rel(photoid); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS albums( | CREATE TABLE IF NOT EXISTS albums( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|     title TEXT, |     title TEXT, | ||||||
|     description TEXT, |     description TEXT | ||||||
|     associated_directory TEXT COLLATE NOCASE |  | ||||||
| ); | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_albums_id on albums(id); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS bookmarks( | CREATE TABLE IF NOT EXISTS bookmarks( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|     title TEXT, |     title TEXT, | ||||||
|     url TEXT, |     url TEXT, | ||||||
|     author_id TEXT |     author_id TEXT | ||||||
| ); | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_bookmarks_id on bookmarks(id); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_bookmarks_author on bookmarks(author_id); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
|  | CREATE TABLE IF NOT EXISTS id_numbers( | ||||||
|  |     tab TEXT, | ||||||
|  |     last_id TEXT | ||||||
|  | ); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
|  | CREATE TABLE IF NOT EXISTS photo_tag_rel( | ||||||
|  |     photoid TEXT, | ||||||
|  |     tagid TEXT | ||||||
|  | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_photo_tag_rel_photoid on photo_tag_rel(photoid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_photo_tag_rel_tagid on photo_tag_rel(tagid); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS photos( | CREATE TABLE IF NOT EXISTS photos( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|     filepath TEXT COLLATE NOCASE, |     filepath TEXT COLLATE NOCASE, | ||||||
|  | @ -62,82 +103,42 @@ CREATE TABLE IF NOT EXISTS photos( | ||||||
|     tagged_at INT, |     tagged_at INT, | ||||||
|     author_id TEXT |     author_id TEXT | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS tags( | CREATE INDEX IF NOT EXISTS index_photos_id on photos(id); | ||||||
|     id TEXT, | CREATE INDEX IF NOT EXISTS index_photos_filepath on photos(filepath COLLATE NOCASE); | ||||||
|     name TEXT | CREATE INDEX IF NOT EXISTS index_photos_override_filename on | ||||||
| ); |     photos(override_filename COLLATE NOCASE); | ||||||
| CREATE TABLE IF NOT EXISTS album_photo_rel( | CREATE INDEX IF NOT EXISTS index_photos_created on photos(created); | ||||||
|     albumid TEXT, | CREATE INDEX IF NOT EXISTS index_photos_extension on photos(extension); | ||||||
|     photoid TEXT | CREATE INDEX IF NOT EXISTS index_photos_author_id on photos(author_id); | ||||||
| ); | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS album_group_rel( |  | ||||||
|     parentid TEXT, |  | ||||||
|     memberid TEXT |  | ||||||
| ); |  | ||||||
| CREATE TABLE IF NOT EXISTS photo_tag_rel( |  | ||||||
|     photoid TEXT, |  | ||||||
|     tagid TEXT |  | ||||||
| ); |  | ||||||
| CREATE TABLE IF NOT EXISTS tag_group_rel( | CREATE TABLE IF NOT EXISTS tag_group_rel( | ||||||
|     parentid TEXT, |     parentid TEXT, | ||||||
|     memberid TEXT |     memberid TEXT | ||||||
| ); | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_tag_group_rel_parentid on tag_group_rel(parentid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_tag_group_rel_memberid on tag_group_rel(memberid); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS tag_synonyms( | CREATE TABLE IF NOT EXISTS tag_synonyms( | ||||||
|     name TEXT, |     name TEXT, | ||||||
|     mastername TEXT |     mastername TEXT | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS id_numbers( | CREATE INDEX IF NOT EXISTS index_tag_synonyms_name on tag_synonyms(name); | ||||||
|     tab TEXT, | ---------------------------------------------------------------------------------------------------- | ||||||
|     last_id TEXT | CREATE TABLE IF NOT EXISTS tags( | ||||||
|  |     id TEXT, | ||||||
|  |     name TEXT | ||||||
| ); | ); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_tags_id on tags(id); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_tags_name on tags(name); | ||||||
|  | ---------------------------------------------------------------------------------------------------- | ||||||
| CREATE TABLE IF NOT EXISTS users( | CREATE TABLE IF NOT EXISTS users( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|     username TEXT COLLATE NOCASE, |     username TEXT COLLATE NOCASE, | ||||||
|     password BLOB, |     password BLOB, | ||||||
|     created INT |     created INT | ||||||
| ); | ); | ||||||
| 
 | CREATE INDEX IF NOT EXISTS index_users_id on users(id); | ||||||
| -- Album | CREATE INDEX IF NOT EXISTS index_users_username on users(username COLLATE NOCASE); | ||||||
| CREATE INDEX IF NOT EXISTS index_album_id on albums(id); |  | ||||||
| 
 |  | ||||||
| -- Album-photo relation |  | ||||||
| CREATE INDEX IF NOT EXISTS index_albumrel_albumid on album_photo_rel(albumid); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_albumrel_photoid on album_photo_rel(photoid); |  | ||||||
| 
 |  | ||||||
| -- Album-group relation |  | ||||||
| CREATE INDEX IF NOT EXISTS index_albumgroup_parentid on album_group_rel(parentid); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_albumgroup_memberid on album_group_rel(memberid); |  | ||||||
| 
 |  | ||||||
| -- Bookmark |  | ||||||
| CREATE INDEX IF NOT EXISTS index_bookmark_id on bookmarks(id); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_bookmark_author on bookmarks(author_id); |  | ||||||
| 
 |  | ||||||
| -- Photo |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_id on photos(id); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_path on photos(filepath COLLATE NOCASE); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_fakepath on photos(override_filename COLLATE NOCASE); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_created on photos(created); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_extension on photos(extension); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_author on photos(author_id); |  | ||||||
| 
 |  | ||||||
| -- Tag |  | ||||||
| CREATE INDEX IF NOT EXISTS index_tag_id on tags(id); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_tag_name on tags(name); |  | ||||||
| 
 |  | ||||||
| -- Photo-tag relation |  | ||||||
| CREATE INDEX IF NOT EXISTS index_tagrel_photoid on photo_tag_rel(photoid); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_tagrel_tagid on photo_tag_rel(tagid); |  | ||||||
| 
 |  | ||||||
| -- Tag-synonym relation |  | ||||||
| CREATE INDEX IF NOT EXISTS index_tagsyn_name on tag_synonyms(name); |  | ||||||
| 
 |  | ||||||
| -- Tag-group relation |  | ||||||
| CREATE INDEX IF NOT EXISTS index_taggroup_parentid on tag_group_rel(parentid); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_taggroup_memberid on tag_group_rel(memberid); |  | ||||||
| 
 |  | ||||||
| -- User |  | ||||||
| CREATE INDEX IF NOT EXISTS index_user_id on users(id); |  | ||||||
| CREATE INDEX IF NOT EXISTS index_user_username on users(username COLLATE NOCASE); |  | ||||||
| '''.format(user_version=DATABASE_VERSION) | '''.format(user_version=DATABASE_VERSION) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -260,11 +261,12 @@ class PDBAlbumMixin: | ||||||
|         ''' |         ''' | ||||||
|         filepath = os.path.abspath(filepath) |         filepath = os.path.abspath(filepath) | ||||||
|         cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|         cur.execute('SELECT * FROM albums WHERE associated_directory == ?', [filepath]) |         cur.execute('SELECT * FROM album_associated_directories WHERE directory == ?', [filepath]) | ||||||
|         fetch = cur.fetchone() |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             raise exceptions.NoSuchAlbum(filepath) |             raise exceptions.NoSuchAlbum(filepath) | ||||||
|         return self.get_album(fetch[constants.SQL_ALBUM['id']]) |         album_id = fetch[constants.SQL_ALBUM_DIRECTORY['albumid']] | ||||||
|  |         return self.get_album(album_id) | ||||||
| 
 | 
 | ||||||
|     def get_albums(self): |     def get_albums(self): | ||||||
|         yield from self.get_things(thing_type='album') |         yield from self.get_things(thing_type='album') | ||||||
|  | @ -287,29 +289,45 @@ class PDBAlbumMixin: | ||||||
|         albumid = self.generate_id('albums') |         albumid = self.generate_id('albums') | ||||||
|         title = title or '' |         title = title or '' | ||||||
|         description = description or '' |         description = description or '' | ||||||
|         if associated_directory is not None: |  | ||||||
|             associated_directory = os.path.abspath(associated_directory) |  | ||||||
| 
 |  | ||||||
|         if not isinstance(title, str): |         if not isinstance(title, str): | ||||||
|             raise TypeError('Title must be string, not %s' % type(title)) |             raise TypeError('Title must be string, not %s' % type(title)) | ||||||
| 
 | 
 | ||||||
|         if not isinstance(description, str): |         if not isinstance(description, str): | ||||||
|             raise TypeError('Description must be string, not %s' % type(description)) |             raise TypeError('Description must be string, not %s' % type(description)) | ||||||
| 
 | 
 | ||||||
|  |         cur = self.sql.cursor() | ||||||
|  |         if associated_directory is not None: | ||||||
|  |             associated_directory = os.path.abspath(associated_directory) | ||||||
|  |             cur.execute( | ||||||
|  |                 'SELECT * FROM album_associated_directories WHERE directory == ?', | ||||||
|  |                 [associated_directory] | ||||||
|  |             ) | ||||||
|  |             fetch = cur.fetchone() | ||||||
|  |             if fetch is not None: | ||||||
|  |                 album = self.get_album(fetch[constants.SQL_ALBUM_DIRECTORY['albumid']]) | ||||||
|  |                 raise exceptions.AlbumExists(album) | ||||||
|  | 
 | ||||||
|         self.log.debug('New Album: %s' % title) |         self.log.debug('New Album: %s' % title) | ||||||
|         data = { |         data = { | ||||||
|             'id': albumid, |             'id': albumid, | ||||||
|             'title': title, |             'title': title, | ||||||
|             'description': description, |             'description': description, | ||||||
|             'associated_directory': associated_directory, |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         (qmarks, bindings) = helpers.binding_filler(constants.SQL_ALBUM_COLUMNS, data) |         (qmarks, bindings) = helpers.binding_filler(constants.SQL_ALBUM_COLUMNS, data) | ||||||
|         query = 'INSERT INTO albums VALUES(%s)' % qmarks |         query = 'INSERT INTO albums VALUES(%s)' % qmarks | ||||||
|         cur = self.sql.cursor() |         cur.execute(query, bindings) | ||||||
|  |         album = objects.Album(self, data) | ||||||
|  | 
 | ||||||
|  |         if associated_directory is not None: | ||||||
|  |             data = { | ||||||
|  |                 'albumid': albumid, | ||||||
|  |                 'directory': associated_directory, | ||||||
|  |             } | ||||||
|  |             (qmarks, bindings) = helpers.binding_filler(constants.SQL_ALBUM_DIRECTORY_COLUMNS, data) | ||||||
|  |             query = 'INSERT INTO album_associated_directories VALUES(%s)' % qmarks | ||||||
|             cur.execute(query, bindings) |             cur.execute(query, bindings) | ||||||
| 
 | 
 | ||||||
|         album = objects.Album(self, data) |  | ||||||
|         if photos: |         if photos: | ||||||
|             for photo in photos: |             for photo in photos: | ||||||
|                 photo = self.get_photo(photo) |                 photo = self.get_photo(photo) | ||||||
|  |  | ||||||
|  | @ -103,6 +103,41 @@ def upgrade_5_to_6(sql): | ||||||
|             [album_id, album_id] |             [album_id, album_id] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  | def upgrade_6_to_7(sql): | ||||||
|  |     ''' | ||||||
|  |     Most of the indices were renamed, so delete them and let them regenerate | ||||||
|  |     next time. | ||||||
|  | 
 | ||||||
|  |     Albums lost their `associated_directory` column, and it has been moved to a | ||||||
|  |     separate table `album_associated_directories`, so that we can have albums | ||||||
|  |     which load from multiple directories. | ||||||
|  |     ''' | ||||||
|  |     cur = sql.cursor() | ||||||
|  |     cur.execute('SELECT name FROM sqlite_master WHERE type == "index"') | ||||||
|  |     indices = [x[0] for x in cur.fetchall()] | ||||||
|  |     for index in indices: | ||||||
|  |         cur.execute('DROP INDEX %s' % index) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     cur.execute(''' | ||||||
|  |     CREATE TABLE album_associated_directories( | ||||||
|  |         albumid TEXT, | ||||||
|  |         directory TEXT COLLATE NOCASE | ||||||
|  |     )''') | ||||||
|  |     cur.execute('ALTER TABLE albums RENAME TO deleting_albums') | ||||||
|  |     cur.execute(''' | ||||||
|  |     CREATE TABLE albums( | ||||||
|  |         id TEXT, | ||||||
|  |         title TEXT, | ||||||
|  |         description TEXT | ||||||
|  |     )''') | ||||||
|  |     cur.execute('INSERT INTO albums SELECT id, title, description FROM deleting_albums') | ||||||
|  |     cur.execute(''' | ||||||
|  |     INSERT INTO album_associated_directories SELECT id, associated_directory FROM deleting_albums | ||||||
|  |     ''') | ||||||
|  |     cur.execute('DROP TABLE deleting_albums') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def upgrade_all(database_filename): | def upgrade_all(database_filename): | ||||||
|     ''' |     ''' | ||||||
|     Given the filename of a phototagger database, apply all of the needed |     Given the filename of a phototagger database, apply all of the needed | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue