Skip to content
Snippets Groups Projects
Commit a801560d authored by Nadja Geisler's avatar Nadja Geisler :sunny:
Browse files

Merge branch 'improve-scheduling-2' into 'master'

More improvements for scheduling

See merge request kif/akplanning!63
parents 5b51d754 5e6f5e74
No related branches found
No related tags found
No related merge requests found
......@@ -231,6 +231,10 @@ class Availability(models.Model):
result = cls._pair_intersection(result, availset)
return result
@property
def simplified(self):
return f'{self.start.astimezone(self.event.timezone).strftime("%a %H:%M")}-{self.end.astimezone(self.event.timezone).strftime("%a %H:%M")}'
class Meta:
verbose_name = _('Availability')
verbose_name_plural = _('Availabilities')
......
# Generated by Django 3.0.6 on 2020-11-04 23:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('AKModel', '0041_constraint_violation'),
]
operations = [
migrations.AddField(
model_name='akslot',
name='fixed',
field=models.BooleanField(default=False, help_text='Length and time of this AK should not be changed', verbose_name='Scheduling Fixed'),
),
]
......@@ -25,7 +25,7 @@ class Event(models.Model):
start = models.DateTimeField(verbose_name=_('Start'), help_text=_('Time the event begins'))
end = models.DateTimeField(verbose_name=_('End'), help_text=_('Time the event ends'))
reso_deadline = models.DateTimeField(verbose_name=_('Resolution Deadline'), blank=True, null=True,
help_text=_('When should AKs with intention to submit a resolution be done?'))
help_text=_('When should AKs with intention to submit a resolution be done?'))
public = models.BooleanField(verbose_name=_('Public event'), default=True,
help_text=_('Show this event on overview page.'))
......@@ -247,13 +247,16 @@ class AK(models.Model):
@property
def details(self):
from AKModel.availability.models import Availability
availabilities = ', \n'.join(f'{a.simplified}' for a in Availability.objects.filter(ak=self))
return f"""{self.name}{" (R)" if self.reso else ""}:
{self.owners_list}
{_("Requirements")}: {", ".join(str(r) for r in self.requirements.all())}
{_("Conflicts")}: {", ".join(str(c) for c in self.conflicts.all())}
{_("Prerequisites")}: {", ".join(str(p) for p in self.prerequisites.all())}"""
{_("Prerequisites")}: {", ".join(str(p) for p in self.prerequisites.all())}
{_("Availabilities")}: \n{availabilities}"""
@property
def owners_list(self):
......@@ -316,6 +319,8 @@ class AKSlot(models.Model):
duration = models.DecimalField(max_digits=4, decimal_places=2, default=2, verbose_name=_('Duration'),
help_text=_('Length in hours'))
fixed = models.BooleanField(default=False, verbose_name=_('Scheduling Fixed'), help_text=_('Length and time of this AK should not be changed'))
event = models.ForeignKey(to=Event, on_delete=models.CASCADE, verbose_name=_('Event'),
help_text=_('Associated event'))
......@@ -358,8 +363,10 @@ class AKSlot(models.Model):
class AKOrgaMessage(models.Model):
ak = models.ForeignKey(to=AK, on_delete=models.CASCADE, verbose_name=_('AK'), help_text=_('AK this message belongs to'))
text = models.TextField(verbose_name=_("Message text"), help_text=_("Message to the organizers. This is not publicly visible."))
ak = models.ForeignKey(to=AK, on_delete=models.CASCADE, verbose_name=_('AK'),
help_text=_('AK this message belongs to'))
text = models.TextField(verbose_name=_("Message text"),
help_text=_("Message to the organizers. This is not publicly visible."))
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
......@@ -384,7 +391,8 @@ class ConstraintViolation(models.Model):
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_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')
......@@ -394,13 +402,18 @@ class ConstraintViolation(models.Model):
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'))
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'))
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'))
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'),
......@@ -437,6 +450,7 @@ class ConstraintViolation(models.Model):
if a is not None:
output.append(f"{field}: {a}")
return ", ".join(output)
get_details.short_description = _('Details')
def __str__(self):
......
......@@ -48,10 +48,10 @@ class EventsView(LoginRequiredMixin, EventSlugMixin, ListView):
"start": timezone.localtime(slot.start, self.event.timezone).strftime("%Y-%m-%d %H:%M:%S"),
"end": timezone.localtime(slot.end, self.event.timezone).strftime("%Y-%m-%d %H:%M:%S"),
"backgroundColor": slot.ak.category.color,
# TODO Mark conflicts here?
"borderColor": slot.ak.category.color,
"borderColor": "#ff291d" if slot.fixed else slot.ak.category.color,
"constraint": 'roomAvailable',
'url': str(reverse('submit:ak_detail', kwargs={"event_slug": self.event.slug, "pk": slot.ak.pk})),
"editable": not slot.fixed,
'url': str(reverse('admin:AKModel_akslot_change', args=[slot.pk])),
} for slot in context["object_list"]],
safe=False,
**response_kwargs
......
......@@ -7,7 +7,7 @@
{% load static %}
{% load tags_AKPlan %}
{% block title %}{% trans "Scheduling for" %} {{ event }}{% endblock %}
{% block title %}{% trans "Scheduling for" %} {{event}}{% endblock %}
{% block extrahead %}
{{ block.super }}
......@@ -94,12 +94,14 @@
buttonText: '{% trans "Day (Horizontal)" %}',
slotDuration: '00:15',
scrollTime: '08:00',
titleFormat: {weekday: 'long', day: 'numeric', month: 'numeric'},
},
resourceTimelineDayVert: {
type: 'resourceTimeGridDay',
buttonText: '{% trans "Day (Vertical)" %}',
slotDuration: '00:30',
scrollTime: '08:00',
titleFormat: {weekday: 'long', day: 'numeric', month: 'numeric'},
},
resourceTimelineEventHoriz: {
type: 'resourceTimeline',
......@@ -174,6 +176,10 @@
}
});
}
$('.unscheduled-slot').each(function() {
$(this).tooltip({title: $(this).first().attr('data-details'), trigger: 'hover'});
});
});
</script>
{% endblock extrahead %}
......@@ -181,13 +187,13 @@
{% block content %}
<div class="row" style="margin-bottom: 50px;">
<div class="col-md-10 col-lg-11">
<div class="col-md-10 col-lg-10">
<div id="planCalendar"></div>
</div>
<div class="col-md-2 col-lg-1" id="unscheduled-slots">
<div class="col-md-2 col-lg-2" id="unscheduled-slots">
{% for slot in slots_unscheduled %}
<div class="unscheduled-slot badge badge-primary" style='background-color: {{ slot.ak.category.color }}'
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "description": "{{ slot.ak.name }}", "slotID": "{{ slot.pk }}"}'>{{ slot.ak.short_name }}
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "description": "{{ slot.ak.name }}", "slotID": "{{ slot.pk }}"}' data-details="{{ slot.ak.details }}">{{ slot.ak.short_name }}
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }}
</div>
{% endfor %}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment