Add remarks, status and changelog
parent
b28e847a9c
commit
b497ac3d8d
|
@ -1,13 +1,69 @@
|
|||
from formity.extensions import admin, db, ModelView
|
||||
from formity.models import FaceshieldRequest
|
||||
from formity.models import FaceshieldRequest, RequestChange
|
||||
from spaceauth import current_user
|
||||
import enum
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy.orm import class_mapper
|
||||
from sqlalchemy.orm.attributes import get_history
|
||||
|
||||
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()
|
||||
if isinstance(state_before[attr.key], enum.Enum):
|
||||
state_before[attr.key] = state_before[attr.key].name
|
||||
state_after[attr.key] = getattr(target, 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
|
||||
|
||||
|
||||
class FaceshieldRequestAdmin(ModelView):
|
||||
details_modal_template = 'changelog_details_modal.html'
|
||||
edit_template = 'changelog_edit.html'
|
||||
|
||||
column_filters = (
|
||||
'entity_info', 'full_name', 'phone_number', 'email', 'extra',
|
||||
'faceshield_front_required', 'faceshield_model',
|
||||
'faceshield_full_required',
|
||||
'created', 'ua', 'ip',
|
||||
'created', 'ua', 'ip', 'status', 'remarks',
|
||||
)
|
||||
|
||||
column_list = ('entity_info', 'full_name', 'faceshield_front_required', 'faceshield_full_required', 'created', 'status')
|
||||
column_editable_list = ('status', 'remarks')
|
||||
|
||||
can_delete = False
|
||||
can_view_details = True
|
||||
details_modal = True
|
||||
|
||||
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,
|
||||
))
|
||||
|
||||
|
||||
admin.add_view(FaceshieldRequestAdmin(FaceshieldRequest, db.session))
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
from formity.extensions import db
|
||||
from datetime import datetime
|
||||
import enum
|
||||
|
||||
|
||||
class Status(enum.Enum):
|
||||
new = 1
|
||||
confirmed = 2
|
||||
allocated = 3
|
||||
fulfilled = 4
|
||||
rejected = 5
|
||||
|
||||
|
||||
class FaceshieldRequest(db.Model):
|
||||
|
@ -22,3 +31,19 @@ class FaceshieldRequest(db.Model):
|
|||
|
||||
ua = db.Column(db.String)
|
||||
ip = db.Column(db.String)
|
||||
|
||||
status = db.Column(db.Enum(Status), default=Status.new, server_default='new', nullable=False)
|
||||
remarks = db.Column(db.String)
|
||||
|
||||
|
||||
class RequestChange(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
request_id = db.Column(db.Integer, db.ForeignKey(FaceshieldRequest.id))
|
||||
request = db.relationship(FaceshieldRequest, backref='changelog')
|
||||
|
||||
user_id = db.Column(db.String, nullable=False)
|
||||
|
||||
state_before = db.Column(db.JSON)
|
||||
state_after = db.Column(db.JSON)
|
||||
remarks = db.Column(db.String)
|
||||
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
"""Add RequestChange
|
||||
|
||||
Revision ID: 77ec5ee5da15
|
||||
Revises: adce9713c86f
|
||||
Create Date: 2020-03-27 17:42:38.009456
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '77ec5ee5da15'
|
||||
down_revision = 'adce9713c86f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('request_change',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('request_id', sa.Integer(), nullable=True),
|
||||
sa.Column('user_id', sa.String(), nullable=False),
|
||||
sa.Column('state_before', sa.JSON(), nullable=True),
|
||||
sa.Column('state_after', sa.JSON(), nullable=True),
|
||||
sa.Column('remarks', sa.String(), nullable=True),
|
||||
sa.Column('created', sa.DateTime(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['request_id'], ['faceshield_request.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('request_change')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""Add remarks and status fields
|
||||
|
||||
Revision ID: adce9713c86f
|
||||
Revises: 383ca469a0fe
|
||||
Create Date: 2020-03-27 17:16:20.158951
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
status = sa.Enum('new', 'confirmed', 'allocated', 'fulfilled', 'rejected', name='status')
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'adce9713c86f'
|
||||
down_revision = '383ca469a0fe'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
status.create(op.get_bind())
|
||||
op.add_column('faceshield_request', sa.Column('remarks', sa.String(), nullable=True))
|
||||
op.add_column('faceshield_request', sa.Column('status', status, server_default='new', nullable=False))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('faceshield_request', 'status')
|
||||
op.drop_column('faceshield_request', 'remarks')
|
||||
status.drop(op.get_bind())
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,15 @@
|
|||
{% macro render_changelog(model) %}
|
||||
{% for entry in model.changelog %}
|
||||
<p>
|
||||
<span class="text-muted">{{ entry.created.strftime('%Y/%m/%d %H:%M') }}</span> <b>{{ entry.user_id }}</b> changed
|
||||
{% for key, value in entry.state_after.items() %}
|
||||
{%- if not loop.first -%}
|
||||
{%- if loop.last -%}
|
||||
and
|
||||
{%- else -%}
|
||||
,
|
||||
{%- endif -%}
|
||||
{%- endif %} <b>{{ key }}</b> to <code title="{{ value }}">{{ value|truncate(40) }}</code> {% endfor %}
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,7 @@
|
|||
{% extends "admin/model/modals/details.html" %}
|
||||
{% from "_changelog.html" import render_changelog %}
|
||||
{% block details_table %}
|
||||
{{ super() }}
|
||||
|
||||
{{ render_changelog(model) }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
|||
{% extends "admin/model/edit.html" %}
|
||||
{% from "_changelog.html" import render_changelog %}
|
||||
|
||||
{% block body %}
|
||||
{{ super() }}
|
||||
|
||||
{{ render_changelog(model) }}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue