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