Skip to content
Snippets Groups Projects
Commit d8bc1ca5 authored by Benjamin Hättasch's avatar Benjamin Hättasch Committed by Benjamin Hättasch
Browse files

Introduce model for constraint violation

parent 0adacca7
No related branches found
No related tags found
No related merge requests found
...@@ -12,7 +12,8 @@ from simple_history.admin import SimpleHistoryAdmin ...@@ -12,7 +12,8 @@ from simple_history.admin import SimpleHistoryAdmin
from AKModel.availability.forms import AvailabilitiesFormMixin from AKModel.availability.forms import AvailabilitiesFormMixin
from AKModel.availability.models import Availability from AKModel.availability.models import Availability
from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room, AKOrgaMessage from AKModel.models import Event, AKOwner, AKCategory, AKTrack, AKTag, AKRequirement, AK, AKSlot, Room, AKOrgaMessage, \
ConstraintViolation
from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView from AKModel.views import EventStatusView, AKCSVExportView, AKWikiExportView, AKMessageDeleteView
...@@ -259,3 +260,10 @@ class AKOrgaMessageAdmin(admin.ModelAdmin): ...@@ -259,3 +260,10 @@ class AKOrgaMessageAdmin(admin.ModelAdmin):
list_display = ['timestamp', 'ak', 'text'] list_display = ['timestamp', 'ak', 'text']
list_filter = ['ak__event'] list_filter = ['ak__event']
readonly_fields = ['timestamp', 'ak', 'text'] readonly_fields = ['timestamp', 'ak', 'text']
@admin.register(ConstraintViolation)
class ConstraintViolationAdmin(admin.ModelAdmin):
list_display = ['type', 'level', 'get_details']
list_filter = ['event']
readonly_fields = ['timestamp']
# Generated by Django 3.0.6 on 2020-11-03 23:27
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('AKModel', '0040_event_reso_deadline'),
]
operations = [
migrations.CreateModel(
name='ConstraintViolation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.CharField(choices=[('ots', 'Owner has two parallel slots'), ('soa', "AK Slot was scheduled outside the AK's availabilities"), ('rts', 'Room has two AK slots scheduled at the same time'), ('rng', 'Room does not satisfy the requirement of the scheduled AK'), ('acc', 'AK Slot is scheduled at the same time as an AK listed as a conflict'), ('abp', 'AK Slot is scheduled before an AK listed as a prerequisite'), ('aar', 'AK Slot for AK with intention to submit a resolution is scheduled after resolution deadline'), ('acm', 'AK Slot in a category is outside that categories availabilities'), ('asc', 'Two AK Slots for the same AK scheduled at the same time'), ('rce', 'AK Slot is scheduled in a room with less space than interest'), ('soe', "AK Slot is scheduled outside the event's availabilities")], help_text='Type of violation, i.e. what kind of constraint was violated', max_length=3, verbose_name='Type')),
('level', models.PositiveSmallIntegerField(choices=[(1, 'Warning'), (10, 'Violation')], help_text='Severity level of the violation', verbose_name='Level')),
('comment', models.TextField(blank=True, help_text='Comment or further details for this violation', verbose_name='Comment')),
('timestamp', models.DateTimeField(auto_now_add=True, help_text='Time of creation', verbose_name='Timestamp')),
('manually_resolved', models.BooleanField(default=False, help_text='Mark this violation manually as resolved', verbose_name='Manually Resolved')),
('ak_owner', models.ForeignKey(blank=True, help_text='AK Owner belonging to this constraint', null=True, on_delete=django.db.models.deletion.CASCADE, to='AKModel.AKOwner', verbose_name='AK Owner')),
('ak_slots', models.ManyToManyField(blank=True, help_text='AK Slot(s) belonging to this constraint', to='AKModel.AKSlot', verbose_name='AK Slots')),
('aks', models.ManyToManyField(blank=True, help_text='AK(s) belonging to this constraint', to='AKModel.AK', verbose_name='AKs')),
('category', models.ForeignKey(blank=True, help_text='AK Category belonging to this constraint', null=True, on_delete=django.db.models.deletion.CASCADE, to='AKModel.AKCategory', verbose_name='AK Category')),
('event', models.ForeignKey(help_text='Associated event', on_delete=django.db.models.deletion.CASCADE, to='AKModel.Event', verbose_name='Event')),
('requirement', models.ForeignKey(blank=True, help_text='AK Requirement belonging to this constraint', null=True, on_delete=django.db.models.deletion.CASCADE, to='AKModel.AKRequirement', verbose_name='AK Requirement')),
('room', models.ForeignKey(blank=True, help_text='Room belonging to this constraint', null=True, on_delete=django.db.models.deletion.CASCADE, to='AKModel.Room', verbose_name='Room')),
],
options={
'verbose_name': 'Constraint Violation',
'verbose_name_plural': 'Constraint Violations',
'ordering': ['-timestamp'],
},
),
]
...@@ -359,3 +359,68 @@ class AKOrgaMessage(models.Model): ...@@ -359,3 +359,68 @@ class AKOrgaMessage(models.Model):
def __str__(self): def __str__(self):
return f'AK Orga Message for "{self.ak}" @ {self.timestamp}' return f'AK Orga Message for "{self.ak}" @ {self.timestamp}'
class ConstraintViolation(models.Model):
class Meta:
verbose_name = _('Constraint Violation')
verbose_name_plural = _('Constraint Violations')
ordering = ['-timestamp']
class ViolationType(models.TextChoices):
OWNER_TWO_SLOTS = 'ots', _('Owner has two parallel slots')
SLOT_OUTSIDE_AVAIL = 'soa', _('AK Slot was scheduled outside the AK\'s availabilities')
ROOM_TWO_SLOTS = 'rts', _('Room has two AK slots scheduled at the same time')
REQUIRE_NOT_GIVEN = 'rng', _('Room does not satisfy the requirement of the scheduled AK')
AK_CONFLICT_COLLISION = 'acc', _('AK Slot is scheduled at the same time as an AK listed as a conflict')
AK_BEFORE_PREREQUISITE = 'abp', _('AK Slot is scheduled before an AK listed as a prerequisite')
AK_AFTER_RESODEADLINE = 'aar', _('AK Slot for AK with intention to submit a resolution is scheduled after resolution deadline')
AK_CATEGORY_MISMATCH = 'acm', _('AK Slot in a category is outside that categories availabilities')
AK_SLOT_COLLISION = 'asc', _('Two AK Slots for the same AK scheduled at the same time')
ROOM_CAPACITY_EXCEEDED = 'rce', _('AK Slot is scheduled in a room with less space than interest')
SLOT_OUTSIDE_EVENT = 'soe', _('AK Slot is scheduled outside the event\'s availabilities')
class ViolationLevel(models.IntegerChoices):
WARNING = 1, _('Warning')
VIOLATION = 10, _('Violation')
type = models.CharField(verbose_name=_('Type'), max_length=3, choices=ViolationType.choices, help_text=_('Type of violation, i.e. what kind of constraint was violated'))
level = models.PositiveSmallIntegerField(verbose_name=_('Level'), choices=ViolationLevel.choices, help_text=_('Severity level of the violation'))
event = models.ForeignKey(to=Event, on_delete=models.CASCADE, verbose_name=_('Event'), help_text=_('Associated event'))
aks = models.ManyToManyField(to=AK, blank=True, verbose_name=_('AKs'), help_text=_('AK(s) belonging to this constraint'))
ak_slots = models.ManyToManyField(to=AKSlot, blank=True, verbose_name=_('AK Slots'), help_text=_('AK Slot(s) belonging to this constraint'))
ak_owner = models.ForeignKey(to=AKOwner, on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('AK Owner'), help_text=_('AK Owner belonging to this constraint'))
room = models.ForeignKey(to=Room, on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('Room'), help_text=_('Room belonging to this constraint'))
requirement = models.ForeignKey(to=AKRequirement, on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('AK Requirement'), help_text=_('AK Requirement belonging to this constraint'))
category = models.ForeignKey(to=AKCategory, on_delete=models.CASCADE, blank=True, null=True, verbose_name=_('AK Category'), help_text=_('AK Category belonging to this constraint'))
comment = models.TextField(verbose_name=_('Comment'), help_text=_('Comment or further details for this violation'), blank=True)
timestamp = models.DateTimeField(auto_now_add=True, verbose_name=_('Timestamp'), help_text=_('Time of creation'))
manually_resolved = models.BooleanField(verbose_name=_('Manually Resolved'), default=False, help_text=_('Mark this violation manually as resolved'))
fields = ['ak_owner', 'room', 'requirement', 'category']
fields_mm = ['aks', 'ak_slots']
def get_details(self):
"""
Get details of this constraint (all fields connected to it)
:return: string of details
:rtype: str
"""
output = []
# Stringify all ManyToMany fields
for field_mm in self.fields_mm:
output.append(f"{field_mm}: {', '.join(str(a) for a in getattr(self, field_mm).all())}")
# Stringify all other fields
for field in self.fields:
a = getattr(self, field, None)
if a is not None:
output.append(f"{field}: {a}")
return ", ".join(output)
get_details.short_description = _('Details')
def __str__(self):
return f"{self.get_level_display()}: {self.get_type_display()} [{self.get_details()}]"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment