checkpoint
Add Bookmark class; Add user.html; Add more commit loggers; Fix warning_bag attributeerror when it was None
This commit is contained in:
		
							parent
							
								
									109d5feef1
								
							
						
					
					
						commit
						8b05a26ff7
					
				
					 14 changed files with 300 additions and 40 deletions
				
			
		|  | @ -37,6 +37,12 @@ SQL_ALBUM_COLUMNS = [ | ||||||
|     'description', |     'description', | ||||||
|     'associated_directory', |     'associated_directory', | ||||||
| ] | ] | ||||||
|  | SQL_BOOKMARK_COLUMNS = [ | ||||||
|  |     'id', | ||||||
|  |     'title', | ||||||
|  |     'url', | ||||||
|  |     'author_id', | ||||||
|  | ] | ||||||
| SQL_PHOTO_COLUMNS = [ | SQL_PHOTO_COLUMNS = [ | ||||||
|     'id', |     'id', | ||||||
|     'filepath', |     'filepath', | ||||||
|  | @ -82,6 +88,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_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) | ||||||
| SQL_PHOTO = _sql_dictify(SQL_PHOTO_COLUMNS) | SQL_PHOTO = _sql_dictify(SQL_PHOTO_COLUMNS) | ||||||
|  |  | ||||||
							
								
								
									
										33
									
								
								etiquette.py
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								etiquette.py
									
									
									
									
									
								
							|  | @ -24,6 +24,8 @@ site.config.update( | ||||||
|     TEMPLATES_AUTO_RELOAD=True, |     TEMPLATES_AUTO_RELOAD=True, | ||||||
| ) | ) | ||||||
| site.jinja_env.add_extension('jinja2.ext.do') | site.jinja_env.add_extension('jinja2.ext.do') | ||||||
|  | site.jinja_env.trim_blocks = True | ||||||
|  | site.jinja_env.lstrip_blocks = True | ||||||
| site.debug = True | site.debug = True | ||||||
| 
 | 
 | ||||||
| P = phototagger.PhotoDB() | P = phototagger.PhotoDB() | ||||||
|  | @ -83,6 +85,12 @@ def P_tag(tagname): | ||||||
|     except exceptions.NoSuchTag as e: |     except exceptions.NoSuchTag as e: | ||||||
|         flask.abort(404, 'That tag doesnt exist: %s' % e) |         flask.abort(404, 'That tag doesnt exist: %s' % e) | ||||||
| 
 | 
 | ||||||
|  | def P_user(username): | ||||||
|  |     try: | ||||||
|  |         return P.get_user(username=username) | ||||||
|  |     except exceptions.NoSuchUser as e: | ||||||
|  |         flask.abort(404, 'That user doesnt exist: %s' % e) | ||||||
|  | 
 | ||||||
| def send_file(filepath): | def send_file(filepath): | ||||||
|     ''' |     ''' | ||||||
|     Range-enabled file sending. |     Range-enabled file sending. | ||||||
|  | @ -279,12 +287,14 @@ def get_album_zip(albumid): | ||||||
| 
 | 
 | ||||||
|     recursive = request.args.get('recursive', True) |     recursive = request.args.get('recursive', True) | ||||||
|     recursive = helpers.truthystring(recursive) |     recursive = helpers.truthystring(recursive) | ||||||
|  | 
 | ||||||
|     arcnames = helpers.album_zip_filenames(album, recursive=recursive) |     arcnames = helpers.album_zip_filenames(album, recursive=recursive) | ||||||
| 
 | 
 | ||||||
|     streamed_zip = zipstream.ZipFile() |     streamed_zip = zipstream.ZipFile() | ||||||
|     for (real_filepath, arcname) in arcnames.items(): |     for (real_filepath, arcname) in arcnames.items(): | ||||||
|         streamed_zip.write(real_filepath, arcname=arcname) |         streamed_zip.write(real_filepath, arcname=arcname) | ||||||
| 
 | 
 | ||||||
|  |     # Add the album metadata as an {id}.txt file within each directory. | ||||||
|     directories = helpers.album_zip_directories(album, recursive=recursive) |     directories = helpers.album_zip_directories(album, recursive=recursive) | ||||||
|     for (inner_album, directory) in directories.items(): |     for (inner_album, directory) in directories.items(): | ||||||
|         text = [] |         text = [] | ||||||
|  | @ -337,7 +347,8 @@ def get_albums_json(): | ||||||
| @session_manager.give_token | @session_manager.give_token | ||||||
| def get_bookmarks(): | def get_bookmarks(): | ||||||
|     session = session_manager.get(request) |     session = session_manager.get(request) | ||||||
|     return flask.render_template('bookmarks.html', session=session) |     bookmarks = list(P.get_bookmarks()) | ||||||
|  |     return flask.render_template('bookmarks.html', bookmarks=bookmarks, session=session) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @site.route('/file/<photoid>') | @site.route('/file/<photoid>') | ||||||
|  | @ -584,6 +595,26 @@ def get_thumbnail(photoid): | ||||||
|     return send_file(path) |     return send_file(path) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def get_user_core(username): | ||||||
|  |     user = P_user(username) | ||||||
|  |     return user | ||||||
|  | 
 | ||||||
|  | @site.route('/user/<username>', methods=['GET']) | ||||||
|  | @session_manager.give_token | ||||||
|  | def get_user_html(username): | ||||||
|  |     user = get_user_core(username) | ||||||
|  |     session = session_manager.get(request) | ||||||
|  |     return flask.render_template('user.html', user=user, session=session) | ||||||
|  | 
 | ||||||
|  | @site.route('/user/<username>.json', methods=['GET']) | ||||||
|  | @session_manager.give_token | ||||||
|  | def get_user_json(username): | ||||||
|  |     user = get_user_core(username) | ||||||
|  |     user = jsonify.user(user) | ||||||
|  |     user = jsonify.make_json_response(user) | ||||||
|  |     return user | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @site.route('/album/<albumid>', methods=['POST']) | @site.route('/album/<albumid>', methods=['POST']) | ||||||
| @site.route('/album/<albumid>.json', methods=['POST']) | @site.route('/album/<albumid>.json', methods=['POST']) | ||||||
| @session_manager.give_token | @session_manager.give_token | ||||||
|  |  | ||||||
|  | @ -39,6 +39,22 @@ def upgrade_3_to_4(sql): | ||||||
|     cur.execute('ALTER TABLE photos ADD COLUMN author_id TEXT') |     cur.execute('ALTER TABLE photos ADD COLUMN author_id TEXT') | ||||||
|     cur.execute('CREATE INDEX IF NOT EXISTS index_photo_author on photos(author_id)') |     cur.execute('CREATE INDEX IF NOT EXISTS index_photo_author on photos(author_id)') | ||||||
| 
 | 
 | ||||||
|  | def upgrade_4_to_5(sql): | ||||||
|  |     ''' | ||||||
|  |     Add table `bookmarks` and its indices. | ||||||
|  |     ''' | ||||||
|  |     cur = sql.cursor() | ||||||
|  |     cur.execute(''' | ||||||
|  |     CREATE TABLE IF NOT EXISTS bookmarks( | ||||||
|  |         id TEXT, | ||||||
|  |         title TEXT, | ||||||
|  |         url TEXT, | ||||||
|  |         author_id TEXT | ||||||
|  |     ) | ||||||
|  |     ''') | ||||||
|  |     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)') | ||||||
|  | 
 | ||||||
| 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 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,9 @@ | ||||||
| class NoSuchAlbum(Exception): | class NoSuchAlbum(Exception): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | class NoSuchBookmark(Exception): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
| class NoSuchGroup(Exception): | class NoSuchGroup(Exception): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -55,3 +55,11 @@ def tag(t): | ||||||
|         'qualified_name': t.qualified_name(), |         'qualified_name': t.qualified_name(), | ||||||
|     } |     } | ||||||
|     return j |     return j | ||||||
|  | 
 | ||||||
|  | def user(u): | ||||||
|  |     j = { | ||||||
|  |         'id': u.id, | ||||||
|  |         'username': u.username, | ||||||
|  |         'created': u.created, | ||||||
|  |     } | ||||||
|  |     return j | ||||||
|  |  | ||||||
							
								
								
									
										43
									
								
								objects.py
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								objects.py
									
									
									
									
									
								
							|  | @ -61,7 +61,7 @@ class GroupableMixin: | ||||||
|         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 tag_group_rel VALUES(?, ?)', [self.id, member.id]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.photodb.log.debug('Commiting - add to group') |             self.photodb.log.debug('Committing - add to group') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
| 
 | 
 | ||||||
|     def children(self): |     def children(self): | ||||||
|  | @ -266,6 +266,7 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|             [self.id, photo.id] |             [self.id, photo.id] | ||||||
|         ) |         ) | ||||||
|         if commit: |         if commit: | ||||||
|  |             self.photodb.log.debug('Committing - remove photo from album') | ||||||
|             self.photodb.commit() |             self.photodb.commit() | ||||||
| 
 | 
 | ||||||
|     def walk_photos(self): |     def walk_photos(self): | ||||||
|  | @ -277,6 +278,44 @@ class Album(ObjectBase, GroupableMixin): | ||||||
|             print(child) |             print(child) | ||||||
|             yield from child.walk_photos() |             yield from child.walk_photos() | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class Bookmark(ObjectBase): | ||||||
|  |     def __init__(self, photodb, row_tuple): | ||||||
|  |         self.photodb = photodb | ||||||
|  |         if isinstance(row_tuple, (list, tuple)): | ||||||
|  |             row_tuple = {constants.SQL_BOOKMARK_COLUMNS[index]: value for (index, value) in enumerate(row_tuple)} | ||||||
|  | 
 | ||||||
|  |         self.id = row_tuple['id'] | ||||||
|  |         self.title = row_tuple['title'] | ||||||
|  |         self.url = row_tuple['url'] | ||||||
|  |         self.author_id = row_tuple['author_id'] | ||||||
|  | 
 | ||||||
|  |     def __repr__(self): | ||||||
|  |         return 'Bookmark:{id}'.format(id=self.id) | ||||||
|  | 
 | ||||||
|  |     def delete(self, *, commit=True): | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute('DELETE FROM bookmarks WHERE id == ?', [self.id]) | ||||||
|  |         if commit: | ||||||
|  |             self.photodb.sql.commit() | ||||||
|  | 
 | ||||||
|  |     def edit(self, title=None, url=None, *, commit=True): | ||||||
|  |         if title is None and url is None: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         if title is not None: | ||||||
|  |             self.title = title | ||||||
|  | 
 | ||||||
|  |         if url is not None: | ||||||
|  |             self.url = url | ||||||
|  | 
 | ||||||
|  |         cur = self.photodb.sql.cursor() | ||||||
|  |         cur.execute('UPDATE bookmarks SET title = ?, url = ? WHERE id == ?', [self.title, self.url, self.id]) | ||||||
|  |         if commit: | ||||||
|  |             self.photodb.log.debug('Committing - edit bookmark') | ||||||
|  |             self.photodb.sql.commit() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class Photo(ObjectBase): | class Photo(ObjectBase): | ||||||
|     ''' |     ''' | ||||||
|     A PhotoDB entry containing information about an image file. |     A PhotoDB entry containing information about an image file. | ||||||
|  | @ -528,6 +567,8 @@ class Photo(ObjectBase): | ||||||
|         self.ratio = None |         self.ratio = None | ||||||
|         self.duration = None |         self.duration = None | ||||||
| 
 | 
 | ||||||
|  |         self.photodb.log.debug('Reloading metadata for {photo:r}'.format(photo=self)) | ||||||
|  | 
 | ||||||
|         if self.mimetype == 'image': |         if self.mimetype == 'image': | ||||||
|             try: |             try: | ||||||
|                 image = PIL.Image.open(self.real_filepath) |                 image = PIL.Image.open(self.real_filepath) | ||||||
|  |  | ||||||
							
								
								
									
										115
									
								
								phototagger.py
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								phototagger.py
									
									
									
									
									
								
							|  | @ -28,7 +28,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 = 4 | DATABASE_VERSION = 5 | ||||||
| DB_INIT = ''' | DB_INIT = ''' | ||||||
| PRAGMA count_changes = OFF; | PRAGMA count_changes = OFF; | ||||||
| PRAGMA cache_size = 10000; | PRAGMA cache_size = 10000; | ||||||
|  | @ -39,6 +39,12 @@ CREATE TABLE IF NOT EXISTS albums( | ||||||
|     description TEXT, |     description TEXT, | ||||||
|     associated_directory TEXT COLLATE NOCASE |     associated_directory TEXT COLLATE NOCASE | ||||||
| ); | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS bookmarks( | ||||||
|  |     id TEXT, | ||||||
|  |     title TEXT, | ||||||
|  |     url TEXT, | ||||||
|  |     author_id TEXT | ||||||
|  | ); | ||||||
| CREATE TABLE IF NOT EXISTS photos( | CREATE TABLE IF NOT EXISTS photos( | ||||||
|     id TEXT, |     id TEXT, | ||||||
|     filepath TEXT COLLATE NOCASE, |     filepath TEXT COLLATE NOCASE, | ||||||
|  | @ -91,6 +97,10 @@ CREATE INDEX IF NOT EXISTS index_album_id on albums(id); | ||||||
| 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); | ||||||
| 
 | 
 | ||||||
|  | -- 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 | -- Photo | ||||||
| CREATE INDEX IF NOT EXISTS index_photo_id on photos(id); | 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_path on photos(filepath COLLATE NOCASE); | ||||||
|  | @ -379,6 +389,48 @@ class PDBAlbumMixin: | ||||||
|         return album |         return album | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class PDBBookmarkMixin: | ||||||
|  |     def get_bookmark(self, id): | ||||||
|  |         cur = self.sql.cursor() | ||||||
|  |         cur.execute('SELECT * FROM bookmarks WHERE id == ?', [id]) | ||||||
|  |         fetch = cur.fetchone() | ||||||
|  |         if fetch is None: | ||||||
|  |             raise exceptions.NoSuchBookmark(id) | ||||||
|  |         bookmark = objects.Bookmark(self, fetch) | ||||||
|  |         return bookmark | ||||||
|  | 
 | ||||||
|  |     def get_bookmarks(self): | ||||||
|  |         yield from self.get_things(thing_type='bookmark') | ||||||
|  | 
 | ||||||
|  |     def new_bookmark(self, url, title=None, *, author=None, commit=True): | ||||||
|  |         if not url: | ||||||
|  |             raise ValueError('Must provide a URL') | ||||||
|  | 
 | ||||||
|  |         bookmark_id = self.generate_id('bookmarks') | ||||||
|  |         title = title or None | ||||||
|  |         author_id = self.get_user_id_or_none(author) | ||||||
|  | 
 | ||||||
|  |         # To do: NORMALIZATION AND VALIDATION | ||||||
|  | 
 | ||||||
|  |         data = { | ||||||
|  |             'author_id': author_id, | ||||||
|  |             'id': bookmark_id, | ||||||
|  |             'title': title, | ||||||
|  |             'url': url, | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         (qmarks, bindings) = helpers.binding_filler(constants.SQL_BOOKMARK_COLUMNS, data) | ||||||
|  |         query = 'INSERT INTO bookmarks VALUES(%s)' % qmarks | ||||||
|  |         cur = self.sql.cursor() | ||||||
|  |         cur.execute(query, bindings) | ||||||
|  | 
 | ||||||
|  |         bookmark = objects.Bookmark(self, data) | ||||||
|  |         if commit: | ||||||
|  |             self.log.debug('Committing - new Bookmark') | ||||||
|  |             self.sql.commit() | ||||||
|  |         return bookmark | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class PDBPhotoMixin: | class PDBPhotoMixin: | ||||||
|     def get_photo(self, photoid): |     def get_photo(self, photoid): | ||||||
|         return self.get_thing_by_id('photo', photoid) |         return self.get_thing_by_id('photo', photoid) | ||||||
|  | @ -452,15 +504,7 @@ class PDBPhotoMixin: | ||||||
|                 exc.photo = existing |                 exc.photo = existing | ||||||
|                 raise exc |                 raise exc | ||||||
| 
 | 
 | ||||||
|         if isinstance(author, objects.User): |         author_id = self.get_user_id_or_none(author) | ||||||
|             if author.photodb != self: |  | ||||||
|                 raise ValueError('That user does not belong to this photodb') |  | ||||||
|             author_id = author.id |  | ||||||
|         elif author is not None: |  | ||||||
|             # Just to confirm |  | ||||||
|             author_id = self.get_user(id=author).id |  | ||||||
|         else: |  | ||||||
|             author_id = None |  | ||||||
| 
 | 
 | ||||||
|         extension = os.path.splitext(filename)[1] |         extension = os.path.splitext(filename)[1] | ||||||
|         extension = extension.replace('.', '') |         extension = extension.replace('.', '') | ||||||
|  | @ -503,11 +547,11 @@ class PDBPhotoMixin: | ||||||
|             photo.add_tag(tag, commit=False) |             photo.add_tag(tag, commit=False) | ||||||
| 
 | 
 | ||||||
|         if commit: |         if commit: | ||||||
|             self.log.debug('Commiting - new_photo') |             self.log.debug('Committing - new_photo') | ||||||
|             self.commit() |             self.commit() | ||||||
|         return photo |         return photo | ||||||
| 
 | 
 | ||||||
|     def purge_deleted_files(self): |     def purge_deleted_files(self, *, commit=True): | ||||||
|         ''' |         ''' | ||||||
|         Remove Photo entries if their corresponding file is no longer found. |         Remove Photo entries if their corresponding file is no longer found. | ||||||
|         ''' |         ''' | ||||||
|  | @ -515,14 +559,20 @@ class PDBPhotoMixin: | ||||||
|         for photo in photos: |         for photo in photos: | ||||||
|             if os.path.exists(photo.real_filepath): |             if os.path.exists(photo.real_filepath): | ||||||
|                 continue |                 continue | ||||||
|             photo.delete() |             photo.delete(commit=False) | ||||||
|  |         if commit: | ||||||
|  |             self.log.debug('Committing - purge deleted photos') | ||||||
|  |             self.sql.commit() | ||||||
| 
 | 
 | ||||||
|     def purge_empty_albums(self): |     def purge_empty_albums(self, *, commit=True): | ||||||
|         albums = self.get_albums() |         albums = self.get_albums() | ||||||
|         for album in albums: |         for album in albums: | ||||||
|             if album.children() or album.photos(): |             if album.children() or album.photos(): | ||||||
|                 continue |                 continue | ||||||
|             album.delete() |             album.delete(commit=False) | ||||||
|  |         if commit: | ||||||
|  |             self.log.debug('Committing - purge empty albums') | ||||||
|  |             self.sql.commit() | ||||||
| 
 | 
 | ||||||
|     def search( |     def search( | ||||||
|             self, |             self, | ||||||
|  | @ -559,7 +609,7 @@ class PDBPhotoMixin: | ||||||
| 
 | 
 | ||||||
|         TAGS AND FILTERS |         TAGS AND FILTERS | ||||||
|         authors: |         authors: | ||||||
|             A list of User object or users IDs. |             A list of User objects, or usernames, or user ids. | ||||||
| 
 | 
 | ||||||
|         created: |         created: | ||||||
|             A hyphen_range string respresenting min and max. Or just a number for lower bound. |             A hyphen_range string respresenting min and max. Or just a number for lower bound. | ||||||
|  | @ -813,7 +863,7 @@ class PDBPhotoMixin: | ||||||
|             photos_received += 1 |             photos_received += 1 | ||||||
|             yield photo |             yield photo | ||||||
| 
 | 
 | ||||||
|         if warning_bag.warnings: |         if warning_bag and warning_bag.warnings: | ||||||
|             yield warning_bag |             yield warning_bag | ||||||
| 
 | 
 | ||||||
|         end_time = time.time() |         end_time = time.time() | ||||||
|  | @ -894,7 +944,7 @@ class PDBTagMixin: | ||||||
|         cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|         cur.execute('INSERT INTO tags VALUES(?, ?)', [tagid, tagname]) |         cur.execute('INSERT INTO tags VALUES(?, ?)', [tagid, tagname]) | ||||||
|         if commit: |         if commit: | ||||||
|             self.log.debug('Commiting - new_tag') |             self.log.debug('Committing - new_tag') | ||||||
|             self.commit() |             self.commit() | ||||||
|         tag = objects.Tag(self, [tagid, tagname]) |         tag = objects.Tag(self, [tagid, tagname]) | ||||||
|         return tag |         return tag | ||||||
|  | @ -955,6 +1005,23 @@ class PDBUserMixin: | ||||||
|         else: |         else: | ||||||
|             raise exceptions.NoSuchUser(username) |             raise exceptions.NoSuchUser(username) | ||||||
| 
 | 
 | ||||||
|  |     def get_user_id_or_none(self, user): | ||||||
|  |         ''' | ||||||
|  |         For methods that create photos, albums, etc., we sometimes associate | ||||||
|  |         them with an author but sometimes not. This method hides validation | ||||||
|  |         that those methods would otherwise have to duplicate. | ||||||
|  |         ''' | ||||||
|  |         if isinstance(user, objects.User): | ||||||
|  |             if user.photodb != self: | ||||||
|  |                 raise ValueError('That user does not belong to this photodb') | ||||||
|  |             author_id = user.id | ||||||
|  |         elif user is not None: | ||||||
|  |             # Confirm that this string is an ID and not junk. | ||||||
|  |             author_id = self.get_user(id=user).id | ||||||
|  |         else: | ||||||
|  |             author_id = None | ||||||
|  |         return author_id | ||||||
|  | 
 | ||||||
|     def login(self, user_id, password): |     def login(self, user_id, password): | ||||||
|         cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|         cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) |         cur.execute('SELECT * FROM users WHERE id == ?', [user_id]) | ||||||
|  | @ -1018,7 +1085,7 @@ class PDBUserMixin: | ||||||
|         return objects.User(self, data) |         return objects.User(self, data) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|     ''' |     ''' | ||||||
|     This class represents an SQLite3 database containing the following tables: |     This class represents an SQLite3 database containing the following tables: | ||||||
| 
 | 
 | ||||||
|  | @ -1189,7 +1256,7 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|                 current_album.add_photo(photo, commit=False) |                 current_album.add_photo(photo, commit=False) | ||||||
| 
 | 
 | ||||||
|         if commit: |         if commit: | ||||||
|             self.log.debug('Commiting - digest') |             self.log.debug('Committing - digest') | ||||||
|             self.commit() |             self.commit() | ||||||
|         return album |         return album | ||||||
| 
 | 
 | ||||||
|  | @ -1318,7 +1385,7 @@ class PhotoDB(PDBAlbumMixin, PDBPhotoMixin, PDBTagMixin, PDBUserMixin): | ||||||
|         ID is actually used. |         ID is actually used. | ||||||
|         ''' |         ''' | ||||||
|         table = table.lower() |         table = table.lower() | ||||||
|         if table not in ['photos', 'tags', 'groups']: |         if table not in ['photos', 'tags', 'groups', 'bookmarks']: | ||||||
|             raise ValueError('Invalid table requested: %s.', table) |             raise ValueError('Invalid table requested: %s.', table) | ||||||
| 
 | 
 | ||||||
|         cur = self.sql.cursor() |         cur = self.sql.cursor() | ||||||
|  | @ -1377,6 +1444,12 @@ _THING_CLASSES = { | ||||||
|         'exception': exceptions.NoSuchAlbum, |         'exception': exceptions.NoSuchAlbum, | ||||||
|         'table': 'albums', |         'table': 'albums', | ||||||
|     }, |     }, | ||||||
|  |     'bookmark': | ||||||
|  |     { | ||||||
|  |         'class': objects.Bookmark, | ||||||
|  |         'exception': exceptions.NoSuchBookmark, | ||||||
|  |         'table': 'bookmarks', | ||||||
|  |     }, | ||||||
|     'photo': |     'photo': | ||||||
|     { |     { | ||||||
|         'class': objects.Photo, |         'class': objects.Photo, | ||||||
|  |  | ||||||
|  | @ -26,6 +26,17 @@ def build_query(orderby): | ||||||
|     query += ' ORDER BY %s' % orderby |     query += ' ORDER BY %s' % orderby | ||||||
|     return query |     return query | ||||||
| 
 | 
 | ||||||
|  | def get_user(photodb, username_or_id): | ||||||
|  |     try: | ||||||
|  |         user = photodb.get_user(username=username_or_id) | ||||||
|  |     except exceptions.NoSuchUser: | ||||||
|  |         try: | ||||||
|  |             user = photodb.get_user(id=username_or_id) | ||||||
|  |         except exceptions.NoSuchUser: | ||||||
|  |             raise | ||||||
|  | 
 | ||||||
|  |     return user | ||||||
|  | 
 | ||||||
| def minmax(key, value, minimums, maximums, warning_bag=None): | def minmax(key, value, minimums, maximums, warning_bag=None): | ||||||
|     ''' |     ''' | ||||||
|     Dissects a hyphenated range string and inserts the correct k:v pair into |     Dissects a hyphenated range string and inserts the correct k:v pair into | ||||||
|  | @ -69,6 +80,14 @@ def minmax(key, value, minimums, maximums, warning_bag=None): | ||||||
|         maximums[key] = high |         maximums[key] = high | ||||||
| 
 | 
 | ||||||
| def normalize_authors(authors, photodb, warning_bag=None): | def normalize_authors(authors, photodb, warning_bag=None): | ||||||
|  |     ''' | ||||||
|  |     Either: | ||||||
|  |     - A string, where the usernames are separated by commas | ||||||
|  |     - An iterable containing usernames | ||||||
|  |     - An iterable containing User objects. | ||||||
|  | 
 | ||||||
|  |     Returns: A set of user IDs. | ||||||
|  |     ''' | ||||||
|     if not authors: |     if not authors: | ||||||
|         return None |         return None | ||||||
| 
 | 
 | ||||||
|  | @ -84,7 +103,7 @@ def normalize_authors(authors, photodb, warning_bag=None): | ||||||
|                 requested_author = requested_author.username |                 requested_author = requested_author.username | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             user = photodb.get_user(username=requested_author) |             user = get_user(photodb, requested_author) | ||||||
|         except exceptions.NoSuchUser: |         except exceptions.NoSuchUser: | ||||||
|             if warning_bag: |             if warning_bag: | ||||||
|                 warning_bag.add(constants.WARNING_NO_SUCH_USER.format(username=requested_author)) |                 warning_bag.add(constants.WARNING_NO_SUCH_USER.format(username=requested_author)) | ||||||
|  |  | ||||||
|  | @ -138,16 +138,6 @@ li | ||||||
|     right: 8px; |     right: 8px; | ||||||
|     font-size: 0.8em; |     font-size: 0.8em; | ||||||
| } | } | ||||||
| .photo_card_grid_info a |  | ||||||
| { |  | ||||||
|     position: absolute; |  | ||||||
|     max-height: 30px; |  | ||||||
|     overflow: hidden; |  | ||||||
| } |  | ||||||
| .photo_card_grid_info a:hover |  | ||||||
| { |  | ||||||
|     max-height: 100%; |  | ||||||
| } |  | ||||||
| .photo_card_grid_file_metadata | .photo_card_grid_file_metadata | ||||||
| { | { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|  | @ -156,8 +146,15 @@ li | ||||||
| } | } | ||||||
| .photo_card_grid_filename | .photo_card_grid_filename | ||||||
| { | { | ||||||
|  |     position: absolute; | ||||||
|  |     max-height: 30px; | ||||||
|  |     overflow: hidden; | ||||||
|     word-break:break-word; |     word-break:break-word; | ||||||
| } | } | ||||||
|  | .photo_card_grid_filename:hover | ||||||
|  | { | ||||||
|  |     max-height: 100%; | ||||||
|  | } | ||||||
| .photo_card_grid_tags | .photo_card_grid_tags | ||||||
| { | { | ||||||
|     position: absolute; |     position: absolute; | ||||||
|  |  | ||||||
|  | @ -36,7 +36,7 @@ p | ||||||
|     {% if sub_albums %} |     {% if sub_albums %} | ||||||
|     <h3>Sub-albums</h3> |     <h3>Sub-albums</h3> | ||||||
|     <ul> |     <ul> | ||||||
|         {% for sub_album in sub_albums %} |         {% for sub_album in sub_albums|sort(attribute='title') %} | ||||||
|         <li><a href="/album/{{sub_album.id}}"> |         <li><a href="/album/{{sub_album.id}}"> | ||||||
|             {% if sub_album.title %} |             {% if sub_album.title %} | ||||||
|             {{sub_album.title}} |             {{sub_album.title}} | ||||||
|  |  | ||||||
|  | @ -8,6 +8,26 @@ | ||||||
|     <script src="/static/common.js"></script> |     <script src="/static/common.js"></script> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
|  | #bookmarks | ||||||
|  | { | ||||||
|  |     display: flex; | ||||||
|  |     flex: 0 0 auto; | ||||||
|  |     flex-direction: column; | ||||||
|  | } | ||||||
|  | .bookmark_card | ||||||
|  | { | ||||||
|  |     background-color: #ffffd4; | ||||||
|  |     display: inline-flex; | ||||||
|  |     flex: 0 0 auto; | ||||||
|  |     flex-direction: column; | ||||||
|  |     padding: 8px; | ||||||
|  |     margin: 8px; | ||||||
|  |     border-radius: 8px; | ||||||
|  | } | ||||||
|  | .bookmark_card .bookmark_url | ||||||
|  | { | ||||||
|  |     color: #aaa; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
| </head> | </head> | ||||||
| 
 | 
 | ||||||
|  | @ -15,7 +35,20 @@ | ||||||
| <body> | <body> | ||||||
|     {{header.make_header(session=session)}} |     {{header.make_header(session=session)}} | ||||||
|     <div id="content_body"> |     <div id="content_body"> | ||||||
|         <a href="/search?has_tags=no&orderby=random-desc&mimetype=image">Needs tagging</a> |         <div id="bookmarks"> | ||||||
|  |             {% for bookmark in bookmarks %} | ||||||
|  |             <div class="bookmark_card"> | ||||||
|  |                 <a href="{{bookmark.url}}" class="bookmark_title"> | ||||||
|  |                 {% if bookmark.title %} | ||||||
|  |                     {{bookmark.title}} | ||||||
|  |                 {% else %} | ||||||
|  |                     {{bookmark.id}} | ||||||
|  |                 {% endif %} | ||||||
|  |                 </a> | ||||||
|  |                 <a href="{{bookmark.url}}" class="bookmark_url">{{bookmark.url}}</a> | ||||||
|  |             </div> | ||||||
|  |             {% endfor %} | ||||||
|  |         </div> | ||||||
|     </div> |     </div> | ||||||
| </body> | </body> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ | ||||||
|         {% if photo.duration %} |         {% if photo.duration %} | ||||||
|             {{photo.duration_string()}}, |             {{photo.duration_string()}}, | ||||||
|         {% endif %} |         {% endif %} | ||||||
|         {{photo.bytestring()}} |         <a target="_blank" href="/file/{{photo.id}}.{{photo.extension}}">{{photo.bytestring()}}</a> | ||||||
|         </span> |         </span> | ||||||
|         <span class="photo_card_grid_tags"> |         <span class="photo_card_grid_tags"> | ||||||
|             {% set tags = photo.tags() %} |             {% set tags = photo.tags() %} | ||||||
|  |  | ||||||
|  | @ -12,9 +12,10 @@ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| <div id="content_body"> |     {{header.make_header(session=session)}} | ||||||
|     <p>test</p> |     <div id="content_body"> | ||||||
| </div> |         <p>test</p> | ||||||
|  |     </div> | ||||||
| </body> | </body> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								templates/user.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								templates/user.html
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | <!DOCTYPE html5> | ||||||
|  | <html> | ||||||
|  | <head> | ||||||
|  |     {% import "header.html" as header %} | ||||||
|  |     <title>User {{user.username}}</title> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <link rel="stylesheet" href="/static/common.css"> | ||||||
|  | 
 | ||||||
|  | <style> | ||||||
|  | #content_body | ||||||
|  | { | ||||||
|  |     /* overriding common.css here */ | ||||||
|  |     display: block; | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | </head> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <body> | ||||||
|  |     {{header.make_header(session=session)}} | ||||||
|  |     <div id="content_body"> | ||||||
|  |         <h2>{{user.username}}</h2> | ||||||
|  |         <p>ID: {{user.id}}</p> | ||||||
|  |         <p><a href="/search?author={{user.username}}">Photos by {{user.username}}</a></p> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | <script type="text/javascript"> | ||||||
|  | </script> | ||||||
|  | </html> | ||||||
		Loading…
	
		Reference in a new issue