import re import enum import decimal import unicodedata from sqlalchemy import inspect from sqlalchemy.orm import exc, class_mapper from sqlalchemy.orm.attributes import get_history from werkzeug.exceptions import abort def get_object_or_404(model, *criterion): try: rv = model.query.filter(*criterion).one() except (exc.NoResultFound, exc.MultipleResultsFound): abort(404) else: return rv def strip_accents(text): """ Strip accents from input String. :param text: The input string. :type text: String. :returns: The processed String. :rtype: String. """ try: text = unicode(text, 'utf-8') except (TypeError, NameError): # unicode is a default on python 3 pass text = unicodedata.normalize('NFD', text) text = text.encode('ascii', 'ignore') text = text.decode("utf-8") return str(text) def text_to_id(text): """ Convert input text to id. :param text: The input string. :type text: String. :returns: The processed String. :rtype: String. """ text = strip_accents(text.lower()) text = re.sub('[ ]+', '_', text) text = re.sub('[^0-9a-zA-Z_-]', '', text) return text diff_sanitization_rules = [ (enum.Enum, lambda v: v.name), (decimal.Decimal, float), ] def get_diff(target, blacklist=['created', 'updated']): state_before = {} state_after = {} inspr = inspect(target) attrs = class_mapper(target.__class__).column_attrs for attr in attrs: if attr.key in blacklist: continue hist = getattr(inspr.attrs, attr.key).history if hist.has_changes(): state_before[attr.key] = get_history(target, attr.key)[2].pop() state_after[attr.key] = getattr(target, attr.key) for data in [state_before, state_after]: for t, c in diff_sanitization_rules: if isinstance(data[attr.key], t): data[attr.key] = c(data[attr.key]) if state_after[attr.key] == state_before[attr.key] or (state_after[attr.key] in ['', None] and state_before[attr.key] in ['', None]): state_after.pop(attr.key) state_before.pop(attr.key) return state_before, state_after