Fix appearance of easybake errors; more exception improvements
New class EasyBakeException helps distinguish whether or not it should be displayed to the user; Exception class `error_type` attributes are now applied to the class via decorator instead of to the instance via init; Fixed easybake errors looking for the old json response format; Fixed incorrect error bubble when deleting a synonym after the tag has already been deleted
This commit is contained in:
		
							parent
							
								
									888c3b48cd
								
							
						
					
					
						commit
						e413e996d9
					
				
					 5 changed files with 68 additions and 31 deletions
				
			
		|  | @ -6,59 +6,74 @@ def pascal_to_loudsnakes(text): | ||||||
|     text = text.upper() |     text = text.upper() | ||||||
|     return text |     return text | ||||||
| 
 | 
 | ||||||
|  | def with_error_type(cls): | ||||||
|  |     cls.error_type = pascal_to_loudsnakes(cls.__name__) | ||||||
|  |     return cls | ||||||
|  | 
 | ||||||
| class EtiquetteException(Exception): | class EtiquetteException(Exception): | ||||||
|     error_message = '' |     error_message = '' | ||||||
|     def __init__(self, *args): |  | ||||||
|         self.error_type = pascal_to_loudsnakes(type(self).__name__) |  | ||||||
|         Exception.__init__(self, *args) |  | ||||||
| 
 | 
 | ||||||
| class WithFormat(EtiquetteException): | class WithFormat(EtiquetteException): | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|  |         self.given_args = args | ||||||
|  |         self.given_kwargs = kwargs | ||||||
|         self.error_message = self.error_message.format(*args, **kwargs) |         self.error_message = self.error_message.format(*args, **kwargs) | ||||||
|         EtiquetteException.__init__(self, self.error_message) |         EtiquetteException.__init__(self, self.error_message) | ||||||
| 
 | 
 | ||||||
| # NO SUCH | # NO SUCH | ||||||
|  | @with_error_type | ||||||
| class NoSuch(WithFormat): | class NoSuch(WithFormat): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchAlbum(NoSuch): | class NoSuchAlbum(NoSuch): | ||||||
|     error_message = 'Album "{}" does not exist.' |     error_message = 'Album "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchBookmark(NoSuch): | class NoSuchBookmark(NoSuch): | ||||||
|     error_message = 'Bookmark "{}" does not exist.' |     error_message = 'Bookmark "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchGroup(NoSuch): | class NoSuchGroup(NoSuch): | ||||||
|     error_message = 'Group "{}" does not exist.' |     error_message = 'Group "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchPhoto(NoSuch): | class NoSuchPhoto(NoSuch): | ||||||
|     error_message = 'Photo "{}" does not exist.' |     error_message = 'Photo "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchSynonym(NoSuch): | class NoSuchSynonym(NoSuch): | ||||||
|     error_message = 'Synonym "{}" does not exist.' |     error_message = 'Synonym "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchTag(NoSuch): | class NoSuchTag(NoSuch): | ||||||
|     error_message = 'Tag "{}" does not exist.' |     error_message = 'Tag "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class NoSuchUser(NoSuch): | class NoSuchUser(NoSuch): | ||||||
|     error_message = 'User "{}" does not exist.' |     error_message = 'User "{}" does not exist.' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # EXISTS | # EXISTS | ||||||
|  | @with_error_type | ||||||
| class GroupExists(WithFormat): | class GroupExists(WithFormat): | ||||||
|     error_message = '{member} already in group {group}' |     error_message = '{member} already in group {group}' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class PhotoExists(WithFormat): | class PhotoExists(WithFormat): | ||||||
|     error_message = 'Photo "{}" already exists.' |     error_message = 'Photo "{}" already exists.' | ||||||
|     def __init__(self, photo): |     def __init__(self, photo): | ||||||
|         self.photo = photo |         self.photo = photo | ||||||
|         WithFormat.__init__(self, photo.id) |         WithFormat.__init__(self, photo.id) | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class TagExists(WithFormat): | class TagExists(WithFormat): | ||||||
|     error_message = 'Tag "{}" already exists.' |     error_message = 'Tag "{}" already exists.' | ||||||
|     def __init__(self, tag): |     def __init__(self, tag): | ||||||
|         self.tag = tag |         self.tag = tag | ||||||
|         WithFormat.__init__(self, tag.name) |         WithFormat.__init__(self, tag.name) | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class UserExists(WithFormat): | class UserExists(WithFormat): | ||||||
|     error_message = 'User "{}" already exists.' |     error_message = 'User "{}" already exists.' | ||||||
|     def __init__(self, user): |     def __init__(self, user): | ||||||
|  | @ -67,43 +82,61 @@ class UserExists(WithFormat): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # TAG ERRORS | # TAG ERRORS | ||||||
|  | @with_error_type | ||||||
| class CantSynonymSelf(EtiquetteException): | class CantSynonymSelf(EtiquetteException): | ||||||
|     error_message = 'Cannot apply synonym to self.' |     error_message = 'Cannot apply synonym to self.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
|  | class EasyBakeError(EtiquetteException): | ||||||
|  |     error_message = '' | ||||||
|  |     def __init__(self, message): | ||||||
|  |         self.error_message = message | ||||||
|  |         EtiquetteException.__init__(self) | ||||||
|  | 
 | ||||||
|  | @with_error_type | ||||||
| class RecursiveGrouping(EtiquetteException): | class RecursiveGrouping(EtiquetteException): | ||||||
|     error_message = '{group} is an ancestor of {member}.' |     error_message = '{group} is an ancestor of {member}.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class TagTooLong(WithFormat): | class TagTooLong(WithFormat): | ||||||
|     error_message = 'Tag "{}" is too long.' |     error_message = 'Tag "{}" is too long.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class TagTooShort(WithFormat): | class TagTooShort(WithFormat): | ||||||
|     error_message = 'Tag "{}" has too few valid characters.' |     error_message = 'Tag "{}" has too few valid characters.' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # USER ERRORS | # USER ERRORS | ||||||
|  | @with_error_type | ||||||
| class InvalidUsernameChars(WithFormat): | class InvalidUsernameChars(WithFormat): | ||||||
|     error_message = 'Username "{username}" contains invalid characters: {badchars}.' |     error_message = 'Username "{username}" contains invalid characters: {badchars}.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class PasswordTooShort(WithFormat): | class PasswordTooShort(WithFormat): | ||||||
|     error_message = 'Password is shorter than the minimum of {min_length}.' |     error_message = 'Password is shorter than the minimum of {min_length}.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class UsernameTooLong(WithFormat): | class UsernameTooLong(WithFormat): | ||||||
|     error_message = 'Username "{username}" is longer than maximum of {max_length}.' |     error_message = 'Username "{username}" is longer than maximum of {max_length}.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class UsernameTooShort(WithFormat): | class UsernameTooShort(WithFormat): | ||||||
|     error_message = 'Username "{username}" is shorter than minimum of {min_length}.' |     error_message = 'Username "{username}" is shorter than minimum of {min_length}.' | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class WrongLogin(EtiquetteException): | class WrongLogin(EtiquetteException): | ||||||
|     error_message = 'Wrong username-password combination.' |     error_message = 'Wrong username-password combination.' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # GENERAL ERRORS | # GENERAL ERRORS | ||||||
|  | @with_error_type | ||||||
| class NotExclusive(EtiquetteException): | class NotExclusive(EtiquetteException): | ||||||
|     ''' |     ''' | ||||||
|     For when two or more mutually exclusive actions have been requested. |     For when two or more mutually exclusive actions have been requested. | ||||||
|     ''' |     ''' | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | @with_error_type | ||||||
| class OutOfOrder(WithFormat): | class OutOfOrder(WithFormat): | ||||||
|     ''' |     ''' | ||||||
|     For when a requested minmax range (a, b) has b > a |     For when a requested minmax range (a, b) has b > a | ||||||
|  |  | ||||||
|  | @ -813,11 +813,11 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|             raise exceptions.CantSynonymSelf() |             raise exceptions.CantSynonymSelf() | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             self.photodb.get_tag_by_name(synname) |             existing_tag = self.photodb.get_tag_by_name(synname) | ||||||
|         except exceptions.NoSuchTag: |         except exceptions.NoSuchTag: | ||||||
|             pass |             pass | ||||||
|         else: |         else: | ||||||
|             raise exceptions.TagExists(synname) |             raise exceptions.TagExists(existing_tag) | ||||||
| 
 | 
 | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|         cur = self.photodb.sql.cursor() |         cur = self.photodb.sql.cursor() | ||||||
|  | @ -926,11 +926,11 @@ class Tag(ObjectBase, GroupableMixin): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         try: |         try: | ||||||
|             self.photodb.get_tag(new_name) |             existing_tag = self.photodb.get_tag(new_name) | ||||||
|         except exceptions.NoSuchTag: |         except exceptions.NoSuchTag: | ||||||
|             pass |             pass | ||||||
|         else: |         else: | ||||||
|             raise exceptions.TagExists(new_name) |             raise exceptions.TagExists(existing_tag) | ||||||
| 
 | 
 | ||||||
|         self._cached_qualified_name = None |         self._cached_qualified_name = None | ||||||
|         self.photodb._cached_frozen_children = None |         self.photodb._cached_frozen_children = None | ||||||
|  |  | ||||||
|  | @ -912,11 +912,11 @@ class PDBTagMixin: | ||||||
|         ''' |         ''' | ||||||
|         tagname = self.normalize_tagname(tagname) |         tagname = self.normalize_tagname(tagname) | ||||||
|         try: |         try: | ||||||
|             self.get_tag_by_name(tagname) |             existing_tag = self.get_tag_by_name(tagname) | ||||||
|         except exceptions.NoSuchTag: |         except exceptions.NoSuchTag: | ||||||
|             pass |             pass | ||||||
|         else: |         else: | ||||||
|             raise exceptions.TagExists(tagname) |             raise exceptions.TagExists(existing_tag) | ||||||
| 
 | 
 | ||||||
|         tagid = self.generate_id('tags') |         tagid = self.generate_id('tags') | ||||||
|         self._cached_frozen_children = None |         self._cached_frozen_children = None | ||||||
|  | @ -1051,10 +1051,12 @@ class PDBUserMixin: | ||||||
|         if len(password) < self.config['min_password_length']: |         if len(password) < self.config['min_password_length']: | ||||||
|             raise exceptions.PasswordTooShort(min_length=self.config['min_password_length']) |             raise exceptions.PasswordTooShort(min_length=self.config['min_password_length']) | ||||||
| 
 | 
 | ||||||
|         cur = self.sql.cursor() |         try: | ||||||
|         cur.execute('SELECT * FROM users WHERE username == ?', [username]) |             existing_user = self.get_user(username=username) | ||||||
|         if cur.fetchone() is not None: |         except exceptions.NoSuchUser: | ||||||
|             raise exceptions.UserExists(username) |             pass | ||||||
|  |         else: | ||||||
|  |             raise exceptions.UserExists(existing_user) | ||||||
| 
 | 
 | ||||||
|         user_id = self.generate_user_id() |         user_id = self.generate_user_id() | ||||||
|         hashed_password = bcrypt.hashpw(password, bcrypt.gensalt()) |         hashed_password = bcrypt.hashpw(password, bcrypt.gensalt()) | ||||||
|  | @ -1291,10 +1293,10 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs | ||||||
|         ebstring = ebstring.strip() |         ebstring = ebstring.strip() | ||||||
|         ebstring = ebstring.strip('.+=') |         ebstring = ebstring.strip('.+=') | ||||||
|         if ebstring == '': |         if ebstring == '': | ||||||
|             return output_notes |             raise exceptions.EasyBakeError('No tag supplied') | ||||||
| 
 | 
 | ||||||
|         if '=' in ebstring and '+' in ebstring: |         if '=' in ebstring and '+' in ebstring: | ||||||
|             raise ValueError('Cannot rename and assign snynonym at once') |             raise exceptions.EasyBakeError('Cannot rename and assign snynonym at once') | ||||||
| 
 | 
 | ||||||
|         rename_parts = ebstring.split('=') |         rename_parts = ebstring.split('=') | ||||||
|         if len(rename_parts) == 2: |         if len(rename_parts) == 2: | ||||||
|  | @ -1303,7 +1305,7 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs | ||||||
|             ebstring = rename_parts[0] |             ebstring = rename_parts[0] | ||||||
|             rename_to = None |             rename_to = None | ||||||
|         else: |         else: | ||||||
|             raise ValueError('Too many equals signs') |             raise exceptions.EasyBakeError('Too many equals signs') | ||||||
| 
 | 
 | ||||||
|         create_parts = ebstring.split('+') |         create_parts = ebstring.split('+') | ||||||
|         if len(create_parts) == 2: |         if len(create_parts) == 2: | ||||||
|  | @ -1312,10 +1314,10 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs | ||||||
|             tag = create_parts[0] |             tag = create_parts[0] | ||||||
|             synonym = None |             synonym = None | ||||||
|         else: |         else: | ||||||
|             raise ValueError('Too many plus signs') |             raise exceptions.EasyBakeError('Too many plus signs') | ||||||
| 
 | 
 | ||||||
|         if not tag: |         if not tag: | ||||||
|             return output_notes |             raise exceptions.EasyBakeError('No tag supplied') | ||||||
| 
 | 
 | ||||||
|         if rename_to: |         if rename_to: | ||||||
|             tag = self.get_tag(tag) |             tag = self.get_tag(tag) | ||||||
|  | @ -1335,13 +1337,10 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs | ||||||
|             tag = tags[-1] |             tag = tags[-1] | ||||||
| 
 | 
 | ||||||
|         if synonym: |         if synonym: | ||||||
|             try: |  | ||||||
|             tag.add_synonym(synonym) |             tag.add_synonym(synonym) | ||||||
|             note = ('new_synonym', '%s+%s' % (tag.name, synonym)) |             note = ('new_synonym', '%s+%s' % (tag.name, synonym)) | ||||||
|             output_notes.append(note) |             output_notes.append(note) | ||||||
|             print('New syn %s' % synonym) |             print('New syn %s' % synonym) | ||||||
|             except exceptions.TagExists: |  | ||||||
|                 pass |  | ||||||
|         return output_notes |         return output_notes | ||||||
| 
 | 
 | ||||||
|     def generate_id(self, table): |     def generate_id(self, table): | ||||||
|  |  | ||||||
|  | @ -65,7 +65,10 @@ def delete_synonym(synonym): | ||||||
|     synonym = synonym.split('+')[-1].split('.')[-1] |     synonym = synonym.split('+')[-1].split('.')[-1] | ||||||
|     synonym = P.normalize_tagname(synonym) |     synonym = P.normalize_tagname(synonym) | ||||||
| 
 | 
 | ||||||
|  |     try: | ||||||
|         master_tag = P.get_tag(synonym) |         master_tag = P.get_tag(synonym) | ||||||
|  |     except exceptions.NoSuchTag as e: | ||||||
|  |         raise exceptions.NoSuchSynonym(*e.given_args, **e.given_kwargs) | ||||||
|     master_tag.remove_synonym(synonym) |     master_tag.remove_synonym(synonym) | ||||||
| 
 | 
 | ||||||
|     return {'action':'delete_synonym', 'synonym': synonym} |     return {'action':'delete_synonym', 'synonym': synonym} | ||||||
|  | @ -694,6 +697,7 @@ def post_photo_refresh_metadata(photoid): | ||||||
|     photo.reload_metadata() |     photo.reload_metadata() | ||||||
|     return jsonify.make_json_response({}) |     return jsonify.make_json_response({}) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def post_tag_create_delete_core(tagname, function): | def post_tag_create_delete_core(tagname, function): | ||||||
|     try: |     try: | ||||||
|         response = function(tagname) |         response = function(tagname) | ||||||
|  | @ -704,6 +708,7 @@ def post_tag_create_delete_core(tagname, function): | ||||||
|             'error_message': e.error_message, |             'error_message': e.error_message, | ||||||
|         } |         } | ||||||
|         status = 400 |         status = 400 | ||||||
|  |     print(response) | ||||||
| 
 | 
 | ||||||
|     return jsonify.make_json_response(response, status=status) |     return jsonify.make_json_response(response, status=status) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -67,13 +67,13 @@ body | ||||||
|             {% for tag in tags %} |             {% for tag in tags %} | ||||||
|             {% set qualname = tag.qualified_name() %} |             {% set qualname = tag.qualified_name() %} | ||||||
|             <li> |             <li> | ||||||
|                 <a target="_blank" class="tag_object" href="/search?tag_musts={{tag[1]}}">{{qualname}}</a><!-- |                 <a target="_blank" class="tag_object" href="/search?tag_musts={{tag.name}}">{{qualname}}</a><!-- | ||||||
|                 --><button class="remove_tag_button" onclick="delete_tag('{{tag[0]}}', receive_callback);"></button> |                 --><button class="remove_tag_button" onclick="delete_tag('{{tag.name}}', receive_callback);"></button> | ||||||
|             </li> |             </li> | ||||||
|             {% for synonym in tag.synonyms() %} |             {% for synonym in tag.synonyms() %} | ||||||
|             <li> |             <li> | ||||||
|                 <a target="_blank" class="tag_object" href="/search?tag_musts={{tag.name}}">{{qualname + "+" + synonym}}</a> |                 <a target="_blank" class="tag_object" href="/search?tag_musts={{tag.name}}">{{qualname + "+" + synonym}}</a><!-- | ||||||
|                 <button class="remove_tag_button" onclick="delete_tag_synonym('{{synonym}}', receive_callback);"></button> |                 --><button class="remove_tag_button" onclick="delete_tag_synonym('{{synonym}}', receive_callback);"></button> | ||||||
|             </li> |             </li> | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
|             {% endfor %} |             {% endfor %} | ||||||
|  | @ -136,7 +136,7 @@ function receive_callback(responses) | ||||||
|         if ("error_type" in response) |         if ("error_type" in response) | ||||||
|         { |         { | ||||||
|             message_positivity = "message_negative"; |             message_positivity = "message_negative"; | ||||||
|             message_text = '"' + tagname + '" ' + response["error_message"]; |             message_text = response["error_message"]; | ||||||
|         } |         } | ||||||
|         else if ("action" in response) |         else if ("action" in response) | ||||||
|         { |         { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue