Initial work on new checklist handling. No more JSON!

This commit is contained in:
2020-08-29 13:56:57 +01:00
parent de04498517
commit 8ea96674db
16 changed files with 163 additions and 93 deletions

View File

@@ -4,9 +4,11 @@ from django.conf import settings
from django.core import serializers from django.core import serializers
from django.core.mail import EmailMessage, EmailMultiAlternatives from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm, PasswordResetForm
from django.db import transaction
from registration.forms import RegistrationFormUniqueEmail from registration.forms import RegistrationFormUniqueEmail
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import AuthenticationForm
from captcha.fields import ReCaptchaField from captcha.fields import ReCaptchaField
from reversion import revisions as reversion
import simplejson import simplejson
from RIGS import models from RIGS import models
@@ -18,8 +20,6 @@ forms.DateTimeField.widget = forms.DateTimeInput(attrs={'type': 'datetime-local'
# Events Shit # Events Shit
class EventForm(forms.ModelForm): class EventForm(forms.ModelForm):
datetime_input_formats = list(settings.DATETIME_INPUT_FORMATS) datetime_input_formats = list(settings.DATETIME_INPUT_FORMATS)
meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False) meet_at = forms.DateTimeField(input_formats=datetime_input_formats, required=False)
@@ -172,6 +172,50 @@ class EventRiskAssessmentForm(forms.ModelForm):
class EventChecklistForm(forms.ModelForm): class EventChecklistForm(forms.ModelForm):
items = {}
def clean(self):
vehicles = {key:val for key, val in self.data.items()
if key.startswith('vehicle')}
drivers = {key:val for key, val in self.data.items()
if key.startswith('driver')}
for key in vehicles:
pk = int(key.split('_')[1])
driver_key = 'driver_' + str(pk)
if(drivers[driver_key] == ''):
raise forms.ValidationError('Add a driver to vehicle ' + str(pk), code='vehicle_mismatch')
else:
try:
item = models.EventChecklistVehicle.objects.get(pk=pk)
except models.EventChecklistVehicle.DoesNotExist:
item = models.EventChecklistVehicle()
item.vehicle = vehicles['vehicle_' + str(pk)]
item.driver = models.Profile.objects.get(pk=drivers['driver_' + str(pk)])
# item does not have a database pk yet as it isn't saved
self.items[pk] = item
return super(EventChecklistForm, self).clean()
def save(self, commit=True):
checklist = super(EventChecklistForm, self).save(commit=False)
if (commit):
# Remove all existing, to be recreated from the form
checklist.vehicles.all().delete()
checklist.save()
for key in self.items:
item = self.items[key]
reversion.add_to_revision(item)
# finish and save new database items
item.checklist = checklist
item.save()
self.items.clear()
return checklist
class Meta: class Meta:
model = models.EventChecklist model = models.EventChecklist
fields = '__all__' fields = '__all__'

View File

@@ -100,7 +100,6 @@ class EventChecklistEdit(generic.UpdateView):
ec = models.EventChecklist.objects.get(pk=pk) ec = models.EventChecklist.objects.get(pk=pk)
context['event'] = ec.event context['event'] = ec.event
context['edit'] = True context['edit'] = True
context['vehicles_length'] = range(len(self.object.vehicles))
return context return context
class EventChecklistCreate(generic.CreateView): class EventChecklistCreate(generic.CreateView):
@@ -132,7 +131,6 @@ class EventChecklistCreate(generic.CreateView):
epk = self.kwargs.get('pk') epk = self.kwargs.get('pk')
event = models.Event.objects.get(pk=epk) event = models.Event.objects.get(pk=epk)
context['event'] = event context['event'] = event
context['vehicles_length'] = range(2)
return context return context
def get_success_url(self): def get_success_url(self):

View File

@@ -0,0 +1,38 @@
# Generated by Django 3.1 on 2020-08-28 11:46
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('RIGS', '0045_auto_20200824_1431'),
]
operations = [
migrations.RemoveField(
model_name='eventchecklist',
name='vehicles',
),
migrations.AlterField(
model_name='eventchecklist',
name='power_mic',
field=models.ForeignKey(help_text='Who is the Power MIC?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='checklists', to=settings.AUTH_USER_MODEL, verbose_name='Power MIC'),
),
migrations.AlterField(
model_name='profile',
name='first_name',
field=models.CharField(blank=True, max_length=150, verbose_name='first name'),
),
migrations.CreateModel(
name='EventChecklistVehicle',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('vehicle', models.CharField(max_length=255)),
('checklist', models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='vehicles', to='RIGS.eventchecklist')),
('driver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='drivers', to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@@ -505,7 +505,6 @@ class EventAuthorisation(models.Model, RevisionMixin):
return str("N%05d" % self.event.pk + ' (requested by ' + self.sent_by.initials + ')') return str("N%05d" % self.event.pk + ' (requested by ' + self.sent_by.initials + ')')
@reversion.register(follow=['payment_set'])
class Invoice(models.Model): class Invoice(models.Model):
event = models.OneToOneField('Event', on_delete=models.CASCADE) event = models.OneToOneField('Event', on_delete=models.CASCADE)
invoice_date = models.DateField(auto_now_add=True) invoice_date = models.DateField(auto_now_add=True)
@@ -627,15 +626,14 @@ class RiskAssessment(models.Model, RevisionMixin):
def __str__(self): def __str__(self):
return "%i - %s" % (self.pk, self.event) return "%i - %s" % (self.pk, self.event)
@reversion.register @reversion.register(follow=['vehicles',])
class EventChecklist(models.Model, RevisionMixin): class EventChecklist(models.Model, RevisionMixin):
event = models.OneToOneField('Event', on_delete=models.CASCADE) event = models.OneToOneField('Event', on_delete=models.CASCADE)
# General # General
power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checklist', power_mic = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='checklists', null=True,
verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?") verbose_name="Power MIC", on_delete=models.CASCADE, help_text="Who is the Power MIC?")
vehicles = models.JSONField(help_text="List vehicles and their drivers", default=dict, null=False) #vehicles = models.JSONField(help_text="List vehicles and their drivers", default=dict, null=False)
# Safety Checks # Safety Checks
safe_parking = models.BooleanField(help_text="Vehicles parked safely?<br><small>(does not obstruct venue access)</small>") safe_parking = models.BooleanField(help_text="Vehicles parked safely?<br><small>(does not obstruct venue access)</small>")
@@ -667,3 +665,13 @@ class EventChecklist(models.Model, RevisionMixin):
def __str__(self): def __str__(self):
return "%i - %s" % (self.pk, self.event) return "%i - %s" % (self.pk, self.event)
@reversion.register
class EventChecklistVehicle(models.Model):
checklist = models.ForeignKey('EventChecklist', related_name='vehicles', blank=True, on_delete=models.CASCADE)
vehicle = models.CharField(max_length=255)
driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='drivers', on_delete=models.CASCADE)
def __str__(self):
return "{} driven by {}".format(self.vehicle, str(self.driver))

View File

@@ -1 +1 @@
function changeSelectedValue(e,t,a,l){e.find("option").remove(),e.append($("<option></option>").attr("value",t).text(a).data("update_url",l)),e.selectpicker("render"),e.selectpicker("refresh"),e.selectpicker("val",t),e.change()}function refreshUpdateHref(e){targetObject=$("#"+e.attr("id")+"-update"),update_url=$("option:selected",e).data("update_url"),""==update_url?targetObject.attr("disabled",!0):(targetObject.attr("href",update_url),targetObject.attr("disabled",!1))}function initPicker(e){console.log("called!");var t={ajax:{url:e.data("sourceurl"),type:"GET",dataType:"json",data:{term:"{{{q}}}"}},locale:{emptyTitle:""},clearOnEmpty:!1,preprocessData:function(e){var t,a=e.length,l=[];if(l.push({text:clearSelectionLabel,value:"",data:{update_url:"",subtext:""}}),a)for(t=0;t<a;t++)l.push($.extend(!0,e[t],{text:e[t].label,value:e[t].pk,data:{update_url:e[t].update,subtext:""}}));return l}};e.prepend($("<option></option>").attr("value","").text(clearSelectionLabel).data("update_url","")),e.selectpicker().ajaxSelectPicker(t),e.change((function(){refreshUpdateHref(e)})),refreshUpdateHref(e)}$(document).ready((function(){clearSelectionLabel="(no selection)",$(".selectpicker").each((function(){initPicker($(this))})),$("#modal").on("hide.bs.modal",(function(e){null!=modaltarget&&""!=modalobject&&changeSelectedValue($(modaltarget),modalobject[0].pk,modalobject[0].fields.name,modalobject[0].update_url)}))})); function changeSelectedValue(e,t,a,r){e.find("option").remove(),e.append($("<option></option>").attr("value",t).text(a).data("update_url",r)),e.selectpicker("render"),e.selectpicker("refresh"),e.selectpicker("val",t),e.change()}function refreshUpdateHref(e){targetObject=$("#"+e.attr("id")+"-update"),update_url=$("option:selected",e).data("update_url"),""==update_url?targetObject.attr("disabled",!0):(targetObject.attr("href",update_url),targetObject.attr("disabled",!1))}function initPicker(e){var t={ajax:{url:e.data("sourceurl"),type:"GET",dataType:"json",data:{term:"{{{q}}}"}},locale:{emptyTitle:""},clearOnEmpty:!1,preprocessData:function(e){var t,a=e.length,r=[];if(r.push({text:clearSelectionLabel,value:"",data:{update_url:"",subtext:""}}),a)for(t=0;t<a;t++)r.push($.extend(!0,e[t],{text:e[t].label,value:e[t].pk,data:{update_url:e[t].update,subtext:""}}));return r}};e.prepend($("<option></option>").attr("value","").text(clearSelectionLabel).data("update_url","")),e.selectpicker().ajaxSelectPicker(t),e.change((function(){refreshUpdateHref(e)})),refreshUpdateHref(e)}$(document).ready((function(){clearSelectionLabel="(no selection)",$(".selectpicker").each((function(){initPicker($(this))})),$("#modal").on("hide.bs.modal",(function(e){null!=modaltarget&&""!=modalobject&&changeSelectedValue($(modaltarget),modalobject[0].pk,modalobject[0].fields.name,modalobject[0].update_url)}))}));

View File

@@ -28,7 +28,6 @@ function refreshUpdateHref(obj) {
} }
function initPicker(obj) { function initPicker(obj) {
console.log('called!');
var options = { var options = {
ajax: { ajax: {
url: obj.data('sourceurl'), url: obj.data('sourceurl'),

View File

@@ -1,8 +1,6 @@
{% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %} {% extends request.is_ajax|yesno:"base_ajax.html,base_rigs.html" %}
{% block title %}Risk Assessment for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}{% endblock %} {% block title %}Event Checklist for Event N{{ object.event.pk|stringformat:"05d" }} {{ object.event.name }}{% endblock %}
{% load help_text from filters %} {% load help_text from filters %}
{% load get_json_element from filters %}
{% load get_item from filters %}
{% load profile_by_index from filters %} {% load profile_by_index from filters %}
{% block content %} {% block content %}
@@ -24,11 +22,10 @@
{{ object.power_mic.name }} {{ object.power_mic.name }}
</dd> </dd>
</dl> </dl>
<p>{{ object|help_text:'vehicles' }}</p> <p>List vehicles and their drivers</p>
<ul> <ul>
{% for i in object.vehicles %} {% for i in object.vehicles.all %}
<li>{{i}}</li>
<li>Vehicle <strong>{{ object.vehicles|get_item:i|get_item:'vehicle'|default:'none' }}</strong> driven by <strong>{{ object.vehicles|get_item:i|get_item:'driver'|profile_by_index|default:'nobody'}}</strong></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>

View File

@@ -2,8 +2,6 @@
{% load widget_tweaks %} {% load widget_tweaks %}
{% load static %} {% load static %}
{% load help_text from filters %} {% load help_text from filters %}
{% load get_json_element from filters %}
{% load get_item from filters %}
{% load profile_by_index from filters %} {% load profile_by_index from filters %}
{% block title %}{% if edit %}Edit{% else %}Create{% endif %} Event Checklist for Event N{{ event.pk|stringformat:"05d" }}{% endblock %} {% block title %}{% if edit %}Edit{% else %}Create{% endif %} Event Checklist for Event N{{ event.pk|stringformat:"05d" }}{% endblock %}
@@ -55,39 +53,31 @@
$('#medium-event').slideUp(); $('#medium-event').slideUp();
} }
}); });
$("form").submit(function( event ) {
// Mmmm Javascript data mangling...
var raw = $('*[data-serialize]').serializeArray();
var post = raw.reduce(function (result, current) {
var index = current.name.split('_')[1];
var name = current.name.split('_')[0];
result[index] = result[index] || {};
var nested = result[index] || {};
if(current.value):
nested[name] = current.value;
result[index] = nested;
return result;
}, {});
$({{form.vehicles.id_for_label}}).val(JSON.stringify(post));
});
$('#vehicle-add').on('click', function (event) { $('#vehicle-add').on('click', function (event) {
event.preventDefault(); event.preventDefault();
var newID = Number($('#vehiclest').attr('data-pk')); var newID = Number($('#vehiclest').attr('data-pk'));
$('#vehicles_new').clone().attr('style', "").attr('id', 'vehicles_' + newID).appendTo('#vehiclest'); $('#vehicles_new').clone().attr('style', "").attr('id', 'vehicles_' + newID).appendTo('#vehiclest');
$('#vehicles_' + newID).find('select,input').attr('name', function(){ $('#vehicles_' + newID).find('select,input').attr('name', function(){
return this.name.split('_')[0] + '_' + newID; return this.name.split('_')[0] + '_' + newID;
}).attr('data-serialize', 'true'); //Disabled prevents the hidden row being sent to the form
}).removeAttr('disabled');
$('#vehicles_' + newID).find('button[data-action=delete]').attr('data-id', newID);
$('#vehicles_' + newID).find('select').addClass('selectpicker'); $('#vehicles_' + newID).find('select').addClass('selectpicker');
$('#vehicles_' + newID).find('.selectpicker').selectpicker('refresh'); $('#vehicles_' + newID).find('.selectpicker').selectpicker('refresh');
$(".selectpicker").each(function(){initPicker($(this))}); $(".selectpicker").each(function(){initPicker($(this))});
$('#vehiclest').attr('data-pk', newID + 1); $('#vehiclest').attr('data-pk', newID - 1);
});
$('button[data-action=delete]').on('click', function(event) {
event.preventDefault();
console.log($(this).attr('data-id'));
$('#vehicles_' + $(this).attr('data-id')).remove();
}); });
}); });
</script> </script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="col-sm-offset-1 col-sm-10"> <div class="col-12">
<h3>{% if edit %}Edit{% else %}Create{% endif %} Event Checklist for Event N{{ event.pk|stringformat:"05d" }}</h3> <h3>{% if edit %}Edit{% else %}Create{% endif %} Event Checklist for Event N{{ event.pk|stringformat:"05d" }}</h3>
{% include 'form_errors.html' %} {% include 'form_errors.html' %}
{% if edit %} {% if edit %}
@@ -122,36 +112,36 @@
{% endif %} {% endif %}
</select> </select>
</div> </div>
<label class="col-12 pt-3" for="{{ form.vehicles.id_for_label }}">{{ form.vehicles.help_text }}</label> <p class="pt-3">List vehicles and their drivers</p>
<input name="{{ form.vehicles.name }}" id="{{ form.vehicles.id_for_label }}"
value="{{ form.vehicles.value }}"/>
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>
<th scope="col">Vehicle</th> <th scope="col">Vehicle</th>
<th scope="col">Driver</th> <th scope="col">Driver</th>
<th scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody id="vehiclest" data-pk="2"> <tbody id="vehiclest" data-pk="-1">
<tr id="vehicles_new" style="display: none;"> <tr id="vehicles_new" style="display: none;">
<td><input name="vehicle_new" type="text" class="form-control"/></td> <td><input type="text" class="form-control" name="vehicle_new" disabled="true"/></td>
<td> <td>
<select name="driver_new" class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials"> <select class="form-control" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" name="driver_new" disabled="true">
</select> </select>
</td> </td>
<td><button class="btn btn-danger" data-action='delete'><span class="fas fa-times"></span></button</td>
</tr> </tr>
{# TODO Add required to all fields on row when one is edited #} {# TODO Add required to all fields on row when one is edited #}
{% for i in vehicles_length %} {% for i in object.vehicles.all %}
<tr id="vehicles_{{i}}"> <tr id="vehicles_{{i.pk}}">
<td><input name="vehicle_{{i}}" type="text" class="form-control" data-serialize="true" value="{{ form.vehicles.value|get_json_element:i|get_item:'vehicle'|default:'' }}"/></td> <td><input name="vehicle_{{i.pk}}" type="text" class="form-control" value="{{ i.vehicle }}"/></td>
<td> <td>
<select name="driver_{{i}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials" data-serialize="true"> <select name="driver_{{i.pk}}" class="form-control selectpicker" data-live-search="true" data-sourceurl="{% url 'api_secure' model='profile' %}?fields=first_name,last_name,initials">
{% if form.vehicles.value|get_json_element:i|get_item:'driver' is not 0 %} {% if i.driver != '' %}
<option value="{{ form.vehicles.value|get_json_element:i|get_item:'driver'|default:'0' }}" selected="selected">{{form.vehicles.value|get_json_element:i|get_item:'driver'|profile_by_index}}</option> <option value="{{i.driver.pk}}" selected="selected">{{ i.driver.name }}</option>
{% endif %} {% endif %}
</select> </select>
</td> </td>
{# TODO Delete functionality <td><button class="btn btn-danger" data-id='{{i}}' data-action='delete'><span class="fas fa-times"></span></button</td>#} <td><button class="btn btn-danger" data-id='{{i.pk}}' data-action='delete'><span class="fas fa-times"></span></button</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@@ -39,8 +39,12 @@
{% endif %} {% endif %}
</td> </td>
<td> <td>
<a href="{% url 'event_ec' event.pk %}" class="btn btn-success"><span class="fas fa-paperclip"></span> <span {% if event.eventchecklist %}
class="hidden-xs">Create Event Checklist</span></a> <a class="btn btn-primary" href="{% url 'ec_detail' event.eventchecklist.pk %}">View</a>
{% else %}
<a href="{% url 'event_ec' event.pk %}" class="btn btn-success"><span class="fas fa-paperclip"></span> <span
class="hidden-xs">Create Event Checklist</span></a>
{% endif %}
</td> </td>
</tr> </tr>
{% empty %} {% empty %}

View File

@@ -53,13 +53,13 @@
{% endif %} {% endif %}
</div> </div>
<div class="row"> <div class="row py-4">
<div class="col-sm-6"> <div class="col-sm-6">
<div class="card card-default"> <div class="card card-default">
<div class="card-body"> <div class="card-body">
<div class="pull-right"> <div class="text-right py-3">
<a href="{% url 'payment_create' %}?invoice={{ object.pk }}" <a href="{% url 'payment_create' %}?invoice={{ object.pk }}"
class="btn btn-default modal-href" class="btn btn-success modal-href"
data-target="#{{ form.person.id_for_label }}"> data-target="#{{ form.person.id_for_label }}">
<span class="fas fa-plus"></span> Add <span class="fas fa-plus"></span> Add
</a> </a>
@@ -104,7 +104,9 @@
</div> </div>
</div> </div>
</div> </div>
{% include 'partials/last_edited.html' with target="invoice_history" %} <div class="col-12 text-right">
{% include 'partials/last_edited.html' with target="invoice_history" %}
</div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -137,19 +137,6 @@ def profile_by_index(value):
else: else:
return "" return ""
#TODO More sensible returns
@register.filter
def get_json_element(value, element):
try:
return json.loads(value)[str(element)]
except Exception as e:
return None
@register.filter
def get_item(dictionary, key):
if(type(dictionary) is dict):
return dictionary.get(key)
@register.filter @register.filter
def next(alist, current_index): def next(alist, current_index):
""" """

View File

@@ -115,6 +115,9 @@ urlpatterns = [
name='ec_history', kwargs={'model': models.EventChecklist}), name='ec_history', kwargs={'model': models.EventChecklist}),
path('event/checklist/list', permission_required_with_403('RIGS.change_event')(hs.EventChecklistList.as_view()), path('event/checklist/list', permission_required_with_403('RIGS.change_event')(hs.EventChecklistList.as_view()),
name='ec_list'), name='ec_list'),
# TEMPORARY
path('event/vehicle/<int:pk>/history/', permission_required_with_403('RIGS.change_event')(versioning.VersionHistory.as_view()),
name='vc_history', kwargs={'model': models.EventChecklistVehicle}),
# Finance # Finance
path('invoice/', permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()), path('invoice/', permission_required_with_403('RIGS.view_invoice')(finance.InvoiceIndex.as_view()),

View File

@@ -35,7 +35,7 @@
{% if version.changes.old == None %} {% if version.changes.old == None %}
Created Created
{% else %} {% else %}
Changed {% include 'version_changes.html' %} in Changed {% include 'partials/version_changes.html' %} in
{% endif %} {% endif %}
{% include 'partials/object_button.html' with object=version.changes.new %} {% include 'partials/object_button.html' with object=version.changes.new %}

View File

@@ -1,4 +1,4 @@
{% if version.changes.item_changes or version.changes.field_changes or version.changes.old == None %} {% if version.changes.anything_changed or version.changes.old == None %}
{% for change in version.changes.field_changes %} {% for change in version.changes.field_changes %}
<span title="Changes to {{ change.field.verbose_name }}" class="badge badge-info p-2" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}{% include "version_changes_change.html" %}{% endspaceless %}'>{{ change.field.verbose_name }}</span> <span title="Changes to {{ change.field.verbose_name }}" class="badge badge-info p-2" data-container="body" data-html="true" data-trigger='hover' data-toggle="popover" data-content='{% spaceless %}{% include "version_changes_change.html" %}{% endspaceless %}'>{{ change.field.verbose_name }}</span>
{% endfor %} {% endfor %}

View File

@@ -19,7 +19,7 @@
{% if version.changes.old is None %} {% if version.changes.old is None %}
{{object|to_class_name}} Created {{object|to_class_name}} Created
{% else %} {% else %}
{% include 'version_changes.html' %} {% include 'partials/version_changes.html' %}
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@@ -67,7 +67,6 @@ class FieldComparison(object):
class ModelComparison(object): class ModelComparison(object):
def __init__(self, old=None, new=None, version=None, excluded_keys=[]): def __init__(self, old=None, new=None, version=None, excluded_keys=[]):
# recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects # recieves two objects of the same model, and compares them. Returns an array of FieldCompare objects
try: try:
@@ -117,29 +116,30 @@ class ModelComparison(object):
@cached_property @cached_property
def item_changes(self): def item_changes(self):
# Recieves two event version objects and compares their items, returns an array of ItemCompare objects # Recieves two event version objects and compares their items, returns an array of ItemCompare objects
item_type = ContentType.objects.get_for_model(models.EventItem) item_type = ContentType.objects.get_for_model(models.EventItem)
old_item_versions = self.version.parent.revision.version_set.filter(content_type=item_type) item_dict = {}
new_item_versions = self.version.revision.version_set.filter(content_type=item_type) if hasattr(self.version, 'parent'):
old_item_versions = self.version.parent.revision.version_set.filter(content_type=item_type)
new_item_versions = self.version.revision.version_set.filter(content_type=item_type)
comparisonParams = {'excluded_keys': ['id', 'event', 'order']} comparisonParams = {'excluded_keys': ['id', 'event', 'order']}
# Build some dicts of what we have # Build some dicts of what we have
item_dict = {} # build a list of items, key is the item_pk # build a list of items, key is the item_pk
for version in old_item_versions: # put all the old versions in a list for version in old_item_versions: # put all the old versions in a list
if version.field_dict["event_id"] == int(self.new.pk): if version.field_dict["event_id"] == int(self.new.pk):
compare = ModelComparison(old=version._object_version.object, **comparisonParams) compare = ModelComparison(old=version._object_version.object, **comparisonParams)
item_dict[version.object_id] = compare item_dict[version.object_id] = compare
for version in new_item_versions: # go through the new versions for version in new_item_versions: # go through the new versions
if version.field_dict["event_id"] == int(self.new.pk): if version.field_dict["event_id"] == int(self.new.pk):
try: try:
compare = item_dict[version.object_id] # see if there's a matching old version compare = item_dict[version.object_id] # see if there's a matching old version
compare.new = version._object_version.object # then add the new version to the dictionary compare.new = version._object_version.object # then add the new version to the dictionary
except KeyError: # there's no matching old version, so add this item to the dictionary by itself except KeyError: # there's no matching old version, so add this item to the dictionary by itself
compare = ModelComparison(new=version._object_version.object, **comparisonParams) compare = ModelComparison(new=version._object_version.object, **comparisonParams)
item_dict[version.object_id] = compare # update the dictionary with the changes item_dict[version.object_id] = compare # update the dictionary with the changes
changes = [] changes = []
for (_, compare) in list(item_dict.items()): for (_, compare) in list(item_dict.items()):