Give Albums their own ID counter, own group rel table
This commit is contained in:
		
							parent
							
								
									415d858e20
								
							
						
					
					
						commit
						83408aca4a
					
				
					 4 changed files with 114 additions and 20 deletions
				
			
		|  | @ -67,6 +67,10 @@ SQL_SYN_COLUMNS = [ | ||||||
|     'name', |     'name', | ||||||
|     'master', |     'master', | ||||||
| ] | ] | ||||||
|  | SQL_ALBUMGROUP_COLUMNS = [ | ||||||
|  |     'parentid', | ||||||
|  |     'memberid', | ||||||
|  | ] | ||||||
| SQL_ALBUMPHOTO_COLUMNS = [ | SQL_ALBUMPHOTO_COLUMNS = [ | ||||||
|     'albumid', |     'albumid', | ||||||
|     'photoid', |     'photoid', | ||||||
|  | @ -88,6 +92,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_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) | ||||||
| SQL_LASTID = _sql_dictify(SQL_LASTID_COLUMNS) | SQL_LASTID = _sql_dictify(SQL_LASTID_COLUMNS) | ||||||
|  |  | ||||||
|  | @ -31,6 +31,10 @@ class ObjectBase: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class GroupableMixin: | class GroupableMixin: | ||||||
|  |     group_getter = None | ||||||
|  |     group_sql_index = None | ||||||
|  |     group_table = None | ||||||
|  | 
 | ||||||
|     def add(self, member, *, commit=True): |     def add(self, member, *, commit=True): | ||||||
|         ''' |         ''' | ||||||
|         Add a child object to this group. |         Add a child object to this group. | ||||||
|  | @ -46,33 +50,43 @@ class GroupableMixin: | ||||||
|         # Groupables are only allowed to have 1 parent. |         # Groupables are only allowed to have 1 parent. | ||||||
|         # Unlike photos which can exist in multiple albums. |         # Unlike photos which can exist in multiple albums. | ||||||
|         cur = self.photodb.sql.cursor() |         cur = self.photodb.sql.cursor() | ||||||
|         cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [member.id]) |         cur.execute( | ||||||
|  |             'SELECT * FROM %s WHERE memberid == ?' % self.group_table, | ||||||
|  |             [member.id] | ||||||
|  |         ) | ||||||
|         fetch = cur.fetchone() |         fetch = cur.fetchone() | ||||||
|         if fetch is not None: |         if fetch is not None: | ||||||
|             parent_id = fetch[constants.SQL_TAGGROUP['parentid']] |             parent_id = fetch[self.group_sql_index['parentid']] | ||||||
|             if parent_id == self.id: |             if parent_id == self.id: | ||||||
|                 that_group = self |                 that_group = self | ||||||
|             else: |             else: | ||||||
|                 that_group = self.group_getter(id=parent_id) |                 that_group = self.group_getter(id=parent_id) | ||||||
|             raise exceptions.GroupExists('%s already in group %s' % (member.name, that_group.name)) |             raise exceptions.GroupExists('%s already in group %s' % (member, that_group)) | ||||||
| 
 | 
 | ||||||
|         for parent in self.walk_parents(): |         for parent in self.walk_parents(): | ||||||
|             if parent.id == member.id: |             if parent == member: | ||||||
|                 raise exceptions.RecursiveGrouping('%s is an ancestor of %s' % (member.name, self.name)) |                 raise exceptions.RecursiveGrouping('%s is an ancestor of %s' % (member, self)) | ||||||
| 
 | 
 | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         cur.execute('INSERT INTO tag_group_rel VALUES(?, ?)', [self.id, member.id]) |         cur.execute( | ||||||
|  |             'INSERT INTO %s VALUES(?, ?)' % self.group_table, | ||||||
|  |             [self.id, member.id] | ||||||
|  |         ) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - add to group') |             self.photodb.log.debug('Committing - add to group') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
| 
 | 
 | ||||||
|     def children(self): |     def children(self): | ||||||
|         cur = self.photodb.sql.cursor() |         cur = self.photodb.sql.cursor() | ||||||
|         cur.execute('SELECT * FROM tag_group_rel WHERE parentid == ?', [self.id]) |          | ||||||
|  |         cur.execute( | ||||||
|  |             'SELECT * FROM %s WHERE parentid == ?' % self.group_table, | ||||||
|  |             [self.id] | ||||||
|  |         ) | ||||||
|         fetch = cur.fetchall() |         fetch = cur.fetchall() | ||||||
|         results = [] |         results = [] | ||||||
|         for f in fetch: |         for f in fetch: | ||||||
|             memberid = f[constants.SQL_TAGGROUP['memberid']] |             memberid = f[self.group_sql_index['memberid']] | ||||||
|             child = self.group_getter(id=memberid) |             child = self.group_getter(id=memberid) | ||||||
|             results.append(child) |             results.append(child) | ||||||
|         if isinstance(self, Tag): |         if isinstance(self, Tag): | ||||||
|  | @ -105,16 +119,22 @@ class GroupableMixin: | ||||||
|             if parent is None: |             if parent is None: | ||||||
|                 # Since this group was a root, children become roots by removing |                 # Since this group was a root, children become roots by removing | ||||||
|                 # the row. |                 # the row. | ||||||
|                 cur.execute('DELETE FROM tag_group_rel WHERE parentid == ?', [self.id]) |                 cur.execute( | ||||||
|  |                     'DELETE FROM %s WHERE parentid == ?' % self.group_table, | ||||||
|  |                     [self.id] | ||||||
|  |                 ) | ||||||
|             else: |             else: | ||||||
|                 # Since this group was a child, its parent adopts all its children. |                 # Since this group was a child, its parent adopts all its children. | ||||||
|                 cur.execute( |                 cur.execute( | ||||||
|                     'UPDATE tag_group_rel SET parentid == ? WHERE parentid == ?', |                     'UPDATE %s SET parentid == ? WHERE parentid == ?' % self.group_table, | ||||||
|                     [parent.id, self.id] |                     [parent.id, self.id] | ||||||
|                 ) |                 ) | ||||||
|         # Note that this part comes after the deletion of children to prevent |         # Note that this part comes after the deletion of children to prevent | ||||||
|         # issues of recursion. |         # issues of recursion. | ||||||
|         cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur.execute( | ||||||
|  |             'DELETE FROM %s WHERE memberid == ?' % self.group_table, | ||||||
|  |             [self.id] | ||||||
|  |         ) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - delete tag') |             self.photodb.log.debug('Committing - delete tag') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -125,12 +145,15 @@ class GroupableMixin: | ||||||
|         Returned object will be of the same type as calling object. |         Returned object will be of the same type as calling object. | ||||||
|         ''' |         ''' | ||||||
|         cur = self.photodb.sql.cursor() |         cur = self.photodb.sql.cursor() | ||||||
|         cur.execute('SELECT * FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur.execute( | ||||||
|  |             'SELECT * FROM %s WHERE memberid == ?' % self.group_table, | ||||||
|  |             [self.id] | ||||||
|  |         ) | ||||||
|         fetch = cur.fetchone() |         fetch = cur.fetchone() | ||||||
|         if fetch is None: |         if fetch is None: | ||||||
|             return None |             return None | ||||||
| 
 | 
 | ||||||
|         parentid = fetch[constants.SQL_TAGGROUP['parentid']] |         parentid = fetch[self.group_sql_index['parentid']] | ||||||
|         return self.group_getter(id=parentid) |         return self.group_getter(id=parentid) | ||||||
| 
 | 
 | ||||||
|     def join_group(self, group, *, commit=True): |     def join_group(self, group, *, commit=True): | ||||||
|  | @ -154,7 +177,10 @@ class GroupableMixin: | ||||||
|         ''' |         ''' | ||||||
|         cur = self.photodb.sql.cursor() |         cur = self.photodb.sql.cursor() | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         cur.execute('DELETE FROM tag_group_rel WHERE memberid == ?', [self.id]) |         cur.execute( | ||||||
|  |             'DELETE FROM %s WHERE memberid == ?' % self.group_table, | ||||||
|  |             [self.id] | ||||||
|  |         ) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Committing - leave group') |             self.photodb.log.debug('Committing - leave group') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
|  | @ -172,6 +198,9 @@ class GroupableMixin: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Album(ObjectBase, GroupableMixin): | class Album(ObjectBase, GroupableMixin): | ||||||
|  |     group_sql_index = constants.SQL_ALBUMGROUP | ||||||
|  |     group_table = 'album_group_rel' | ||||||
|  | 
 | ||||||
|     def __init__(self, photodb, db_row): |     def __init__(self, photodb, db_row): | ||||||
|         self.photodb = photodb |         self.photodb = photodb | ||||||
|         if isinstance(db_row, (list, tuple)): |         if isinstance(db_row, (list, tuple)): | ||||||
|  | @ -737,6 +766,9 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|     ''' |     ''' | ||||||
|     A Tag, which can be applied to Photos for organization. |     A Tag, which can be applied to Photos for organization. | ||||||
|     ''' |     ''' | ||||||
|  |     group_sql_index = constants.SQL_TAGGROUP | ||||||
|  |     group_table = 'tag_group_rel' | ||||||
|  | 
 | ||||||
|     def __init__(self, photodb, db_row): |     def __init__(self, photodb, db_row): | ||||||
|         self.photodb = photodb |         self.photodb = photodb | ||||||
|         if isinstance(db_row, (list, tuple)): |         if isinstance(db_row, (list, tuple)): | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ 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 = 5 | DATABASE_VERSION = 6 | ||||||
| DB_INIT = ''' | DB_INIT = ''' | ||||||
| PRAGMA count_changes = OFF; | PRAGMA count_changes = OFF; | ||||||
| PRAGMA cache_size = 10000; | PRAGMA cache_size = 10000; | ||||||
|  | @ -70,6 +70,10 @@ CREATE TABLE IF NOT EXISTS album_photo_rel( | ||||||
|     albumid TEXT, |     albumid TEXT, | ||||||
|     photoid TEXT |     photoid TEXT | ||||||
| ); | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS album_group_rel( | ||||||
|  |     parentid TEXT, | ||||||
|  |     memberid TEXT | ||||||
|  | ); | ||||||
| CREATE TABLE IF NOT EXISTS photo_tag_rel( | CREATE TABLE IF NOT EXISTS photo_tag_rel( | ||||||
|     photoid TEXT, |     photoid TEXT, | ||||||
|     tagid TEXT |     tagid TEXT | ||||||
|  | @ -95,9 +99,15 @@ CREATE TABLE IF NOT EXISTS users( | ||||||
| 
 | 
 | ||||||
| -- Album | -- Album | ||||||
| CREATE INDEX IF NOT EXISTS index_album_id on albums(id); | 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_albumid on album_photo_rel(albumid); | ||||||
| CREATE INDEX IF NOT EXISTS index_albumrel_photoid on album_photo_rel(photoid); | 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 tag_group_rel(parentid); | ||||||
|  | CREATE INDEX IF NOT EXISTS index_albumgroup_memberid on tag_group_rel(memberid); | ||||||
|  | 
 | ||||||
| -- Bookmark | -- Bookmark | ||||||
| CREATE INDEX IF NOT EXISTS index_bookmark_id on bookmarks(id); | CREATE INDEX IF NOT EXISTS index_bookmark_id on bookmarks(id); | ||||||
| CREATE INDEX IF NOT EXISTS index_bookmark_author on bookmarks(author_id); | CREATE INDEX IF NOT EXISTS index_bookmark_author on bookmarks(author_id); | ||||||
|  | @ -122,8 +132,8 @@ CREATE INDEX IF NOT EXISTS index_tagrel_tagid on photo_tag_rel(tagid); | ||||||
| CREATE INDEX IF NOT EXISTS index_tagsyn_name on tag_synonyms(name); | CREATE INDEX IF NOT EXISTS index_tagsyn_name on tag_synonyms(name); | ||||||
| 
 | 
 | ||||||
| -- Tag-group relation | -- Tag-group relation | ||||||
| CREATE INDEX IF NOT EXISTS index_grouprel_parentid on tag_group_rel(parentid); | CREATE INDEX IF NOT EXISTS index_taggroup_parentid on tag_group_rel(parentid); | ||||||
| CREATE INDEX IF NOT EXISTS index_grouprel_memberid on tag_group_rel(memberid); | CREATE INDEX IF NOT EXISTS index_taggroup_memberid on tag_group_rel(memberid); | ||||||
| 
 | 
 | ||||||
| -- User | -- User | ||||||
| CREATE INDEX IF NOT EXISTS index_user_id on users(id); | CREATE INDEX IF NOT EXISTS index_user_id on users(id); | ||||||
|  | @ -283,8 +293,7 @@ class PDBAlbumMixin: | ||||||
|         ''' |         ''' | ||||||
|         Create a new album. Photos can be added now or later. |         Create a new album. Photos can be added now or later. | ||||||
|         ''' |         ''' | ||||||
|         # Albums share the tag table's ID counter |         albumid = self.generate_id('albums') | ||||||
|         albumid = self.generate_id('tags') |  | ||||||
|         title = title or '' |         title = title or '' | ||||||
|         description = description or '' |         description = description or '' | ||||||
|         if associated_directory is not None: |         if associated_directory is not None: | ||||||
|  | @ -1340,7 +1349,7 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs | ||||||
|         ID is actually used. |         ID is actually used. | ||||||
|         ''' |         ''' | ||||||
|         table = table.lower() |         table = table.lower() | ||||||
|         if table not in ['photos', 'tags', 'groups', 'bookmarks']: |         if table not in ['photos', 'tags', 'albums', 'bookmarks']: | ||||||
|             raise ValueError('Invalid table requested: %s.', table) |             raise ValueError('Invalid table requested: %s.', table) | ||||||
| 
 | 
 | ||||||
|         cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|  |  | ||||||
|  | @ -55,6 +55,54 @@ def upgrade_4_to_5(sql): | ||||||
|     cur.execute('CREATE INDEX IF NOT EXISTS index_bookmark_id ON bookmarks(id)') |     cur.execute('CREATE INDEX IF NOT EXISTS index_bookmark_id ON bookmarks(id)') | ||||||
|     cur.execute('CREATE INDEX IF NOT EXISTS index_bookmark_author ON bookmarks(author_id)') |     cur.execute('CREATE INDEX IF NOT EXISTS index_bookmark_author ON bookmarks(author_id)') | ||||||
| 
 | 
 | ||||||
|  | def upgrade_5_to_6(sql): | ||||||
|  |     ''' | ||||||
|  |     When Albums were first introduced, they shared the ID counter and | ||||||
|  |     relationship table with tags, because they were mostly identical at the time. | ||||||
|  |     However this is very ugly and confusing and it's time to finally change it. | ||||||
|  |     - Renames old indices `index_grouprel_*` to `index_taggroup_*` | ||||||
|  |     - Creates new indices `index_albumgroup_*` | ||||||
|  |     - Creates new table `album_group_rel` | ||||||
|  |     - Moves all album group relationships out of `tag_group_rel` and | ||||||
|  |       into `album_group_rel` | ||||||
|  |     - Gives albums their own last_id value, starting with the current tag value. | ||||||
|  |     ''' | ||||||
|  |     # 1. Start the id_numbers.albums value at the tags value so that the number | ||||||
|  |     # can continue to increment safely and separately, instead of starting at | ||||||
|  |     # zero and bumping into existing albums. | ||||||
|  |     cur = sql.cursor() | ||||||
|  |     cur.execute('SELECT * FROM id_numbers WHERE tab == "tags"') | ||||||
|  |     last_id = cur.fetchone()[1] | ||||||
|  |     cur.execute('INSERT INTO id_numbers VALUES("albums", ?)', [last_id]) | ||||||
|  | 
 | ||||||
|  |     # 2. Now's a good chance to rename 'index_grouprel' to 'index_taggroup'. | ||||||
|  |     cur.execute('DROP INDEX index_grouprel_parentid') | ||||||
|  |     cur.execute('DROP INDEX index_grouprel_memberid') | ||||||
|  |     cur.execute('CREATE INDEX index_taggroup_parentid ON tag_group_rel(parentid)') | ||||||
|  |     cur.execute('CREATE INDEX index_taggroup_memberid ON tag_group_rel(memberid)') | ||||||
|  | 
 | ||||||
|  |     # 3. All of the album group relationships need to be moved into their | ||||||
|  |     # own table, out of tag_group_rel | ||||||
|  |     cur.execute('CREATE TABLE album_group_rel(parentid TEXT, memberid TEXT)') | ||||||
|  |     cur.execute('CREATE INDEX index_albumgroup_parentid ON tag_group_rel(parentid)') | ||||||
|  |     cur.execute('CREATE INDEX index_albumgroup_memberid ON tag_group_rel(memberid)') | ||||||
|  |     cur.execute('SELECT id FROM albums') | ||||||
|  |     album_ids = [f[0] for f in cur.fetchall()] | ||||||
|  |     for album_id in album_ids: | ||||||
|  |         cur.execute( | ||||||
|  |             'SELECT * FROM tag_group_rel WHERE parentid == ? OR memberid == ?', | ||||||
|  |             [album_id, album_id] | ||||||
|  |         ) | ||||||
|  |         f = cur.fetchall() | ||||||
|  |         if f == []: | ||||||
|  |             continue | ||||||
|  |         for grouprel in f: | ||||||
|  |             cur.execute('INSERT INTO album_group_rel VALUES(?, ?)', grouprel) | ||||||
|  |         cur.execute( | ||||||
|  |             'DELETE FROM tag_group_rel WHERE parentid == ? OR memberid == ?', | ||||||
|  |             [album_id, album_id] | ||||||
|  |         ) | ||||||
|  | 
 | ||||||
| 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