diff --git a/formity/admin.py b/formity/admin.py index 9488425..ff7a63b 100644 --- a/formity/admin.py +++ b/formity/admin.py @@ -5,41 +5,8 @@ from wtforms import TextAreaField from formity.models import FaceshieldRequest, RequestChange, Status, PostalCode, ExternalUser from spaceauth import current_user import enum -import decimal from flask_weasyprint import HTML, render_pdf -from sqlalchemy import inspect, func -from sqlalchemy.orm import class_mapper -from sqlalchemy.orm.attributes import get_history - -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 +from sqlalchemy import func class IndexView(AdminSecurityMixin, flask_admin.AdminIndexView): @@ -106,8 +73,13 @@ class FaceshieldRequestAdmin(ModelView): 'id', 'entity_info', 'full_name', 'phone_number', 'email', 'extra', 'faceshield_front_required', 'faceshield_model', - 'faceshield_full_required', - 'faceshield_full_delivered', 'faceshield_front_delivered', 'handling_orga', + 'faceshield_front_delivered', + 'faceshield_full_required', 'faceshield_full_delivered', + 'adapter_3m_dar_required', 'adapter_3m_dar_delivered', + 'adapter_easybreath_dar_required', 'adapter_easybreath_dar_delivered', + 'adapter_rd40_dar_required', 'adapter_rd40_dar_delivered', + 'adapter_secura_dar_required', 'adapter_secura_dar_delivered', + 'handling_orga', 'created', 'ua', 'ip', 'status', 'remarks', 'shipping_name', 'shipping_street', 'shipping_postalcode', 'shipping_city', 'shipping_latitude', 'shipping_longitude', @@ -144,24 +116,6 @@ class FaceshieldRequestAdmin(ModelView): def can_export(self): return not current_user.external - def on_model_change(self, form, model, is_created): - if is_created: - return - - try: - before, after = get_diff(model) - except Exception: - # xD - before, after = {'diff': 'failed'}, {'diff': 'failed'} - - if before or after: - db.session.add(RequestChange( - user_id=current_user.get_id() or 'nobody', - request_id=model.id, - state_before=before, - state_after=after, - )) - @flask_admin.expose('/label/') def label(self): return render_pdf(HTML(string=self.label_html())) @@ -180,7 +134,6 @@ class FaceshieldRequestAdmin(ModelView): if req.status != status: req.status = status count += 1 - self.on_model_change(None, req, False) db.session.commit() diff --git a/formity/models.py b/formity/models.py index 0ea29c8..fbf925c 100644 --- a/formity/models.py +++ b/formity/models.py @@ -2,11 +2,11 @@ from datetime import datetime import enum import hashlib import os - +from jinja2 import Markup from formity.extensions import db - +from sqlalchemy import event from sqlalchemy.ext.hybrid import hybrid_property - +from formity.utils import get_diff class Status(enum.Enum): new = 1 @@ -124,3 +124,23 @@ class ExternalUser(db.Model): def check_password(self, password): salt, h = self.password.split(':') return self._hash(salt, password) == h + +@event.listens_for(FaceshieldRequest, 'after_update') +def on_request_change(mapper, connection, target): + try: + before, after = get_diff(target) + except Exception as exc: + # xD + before, after = {'diff': 'failed'}, {'diff': 'failed'} + + if before or after: + from flask_login import current_user + user_id = current_user.get_id() if current_user and current_user.get_id() else 'nobody' + @event.listens_for(db.session, "after_flush", once=True) + def receive_after_flush(session, context): + db.session.add(RequestChange( + user_id=user_id, + request_id=target.id, + state_before=before, + state_after=after, + )) diff --git a/formity/utils.py b/formity/utils.py index 6ecbc96..1aa3fa0 100644 --- a/formity/utils.py +++ b/formity/utils.py @@ -1,6 +1,10 @@ import re +import enum +import decimal import unicodedata -from sqlalchemy.orm import exc +from sqlalchemy import inspect +from sqlalchemy.orm import exc, class_mapper +from sqlalchemy.orm.attributes import get_history from werkzeug.exceptions import abort @@ -47,3 +51,35 @@ def text_to_id(text): 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