Skip to content
Snippets Groups Projects
Commit 9d94ec2e authored by Benjamin Hättasch's avatar Benjamin Hättasch Committed by Nadja Geisler
Browse files

Introduce basic constraint violation admin view showing current violations (using async reloading)

parent ec0d2a62
No related branches found
No related tags found
No related merge requests found
...@@ -74,6 +74,10 @@ ...@@ -74,6 +74,10 @@
<a class="btn btn-success" <a class="btn btn-success"
href="{% url 'admin:schedule' event_slug=event.slug %}">{% trans "Scheduling" %}</a> href="{% url 'admin:schedule' event_slug=event.slug %}">{% trans "Scheduling" %}</a>
{% if "AKScheduling | is_installed" %}
<a class="btn btn-success"
href="{% url 'admin:constraint-violations' slug=event.slug %}">{% trans "Constraint Violations" %} <span class="badge badge-secondary">{{ event.constraintviolation_set.count }}</span></a>
{% endif %}
<a class="btn btn-success" <a class="btn btn-success"
href="{% url 'admin:tracks_manage' event_slug=event.slug %}">{% trans "Manage ak tracks" %}</a> href="{% url 'admin:tracks_manage' event_slug=event.slug %}">{% trans "Manage ak tracks" %}</a>
<a class="btn btn-success" <a class="btn btn-success"
......
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-04-29 22:48+0000\n" "POT-Creation-Date: 2021-05-09 18:23+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -17,49 +17,92 @@ msgstr "" ...@@ -17,49 +17,92 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:11
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:11 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:11
#: AKScheduling/templates/admin/AKScheduling/scheduling.html:10 #: AKScheduling/templates/admin/AKScheduling/scheduling.html:10
msgid "Scheduling for" msgid "Scheduling for"
msgstr "Scheduling für" msgstr "Scheduling für"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:126 #: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:74
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:128
msgid "No violations"
msgstr "Keine Verletzungen"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:81
msgid "Cannot load current violations from server"
msgstr "Kann die aktuellen Verletzungen nicht vom Server laden"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:106
msgid "Violation(s)"
msgstr "Verletzung(en)"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:109
msgid "Auto reload?"
msgstr "Automatisch neu laden?"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:113
msgid "Reload now"
msgstr "Jetzt neu laden"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:118
msgid "Violation"
msgstr "Verletzung"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:119
msgid "Problem"
msgstr "Problem"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:120
msgid "Details"
msgstr "Details"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:121
msgid "Since"
msgstr "Seit"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:134
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:243
#: AKScheduling/templates/admin/AKScheduling/scheduling.html:197
#: AKScheduling/templates/admin/AKScheduling/unscheduled.html:34
msgid "Event Status"
msgstr "Event-Status"
#: AKScheduling/templates/admin/AKScheduling/constraint_violations.html:136
msgid "Scheduling"
msgstr "Scheduling"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:129
msgid "Name of new ak track" msgid "Name of new ak track"
msgstr "Name des neuen AK-Tracks" msgstr "Name des neuen AK-Tracks"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:142 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:145
msgid "Could not create ak track" msgid "Could not create ak track"
msgstr "Konnte neuen AK-Track nicht anlegen" msgstr "Konnte neuen AK-Track nicht anlegen"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:168 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:171
msgid "Could not update ak track name" msgid "Could not update ak track name"
msgstr "Konnte Namen des AK-Tracks nicht ändern" msgstr "Konnte Namen des AK-Tracks nicht ändern"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:174 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:177
msgid "Do you really want to delete this ak track?" msgid "Do you really want to delete this ak track?"
msgstr "Soll dieser AK-Track wirklich gelöscht werden?" msgstr "Soll dieser AK-Track wirklich gelöscht werden?"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:188 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:191
msgid "Could not delete ak track" msgid "Could not delete ak track"
msgstr "AK-Track konnte nicht gelöscht werden" msgstr "AK-Track konnte nicht gelöscht werden"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:200 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:203
msgid "Manage AK Tracks" msgid "Manage AK Tracks"
msgstr "AK-Tracks verwalten" msgstr "AK-Tracks verwalten"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:201 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:204
msgid "Add ak track" msgid "Add ak track"
msgstr "AK-Track hinzufügen" msgstr "AK-Track hinzufügen"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:206 #: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:209
msgid "AKs without track" msgid "AKs without track"
msgstr "AKs ohne Track" msgstr "AKs ohne Track"
#: AKScheduling/templates/admin/AKScheduling/manage_tracks.html:240
#: AKScheduling/templates/admin/AKScheduling/scheduling.html:197
#: AKScheduling/templates/admin/AKScheduling/unscheduled.html:34
msgid "Event Status"
msgstr "Event-Status"
#: AKScheduling/templates/admin/AKScheduling/scheduling.html:87 #: AKScheduling/templates/admin/AKScheduling/scheduling.html:87
msgid "Day (Horizontal)" msgid "Day (Horizontal)"
msgstr "Tag (horizontal)" msgstr "Tag (horizontal)"
......
{% extends "admin/base_site.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load l10n %}
{% load tz %}
{% load static %}
{% load tags_AKPlan %}
{% load fontawesome_5 %}
{% block title %}{% trans "Scheduling for" %} {{event}}{% endblock %}
{% block extrahead %}
{{ block.super }}
<script>
document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// (Re-)Load constraint violations using AJAX and visualize using violation count badge and violation table
function reload() {
$.ajax({
url: "{% url "model:scheduling-constraint-violations-list" event_slug=event.slug %}",
type: 'GET',
success: function (response) {
console.log(response);
let table_html = '';
if(response.length > 0) {
// Update violation count badge
$('#violationCountBadge').html(response.length).removeClass('badge-success').addClass('badge-warning');
// Update violations table
for(let i=0;i<response.length;i++) {
table_html += "<tr><td>" + response[i].level_display + "</td><td>" + response[i].type_display + "</td><td>" + response[i].details + "</td><td>" + response[i].timestamp_display + "</td><td></td></tr>";
}
}
else {
// Update violation count badge
$('#violationCountBadge').html(0).removeClass('badge-warning').addClass('badge-success');
// Update violations table
table_html ='<tr class="text-muted"><td colspan="5" class="text-center">{% trans "No violations" %}</td></tr>'
}
// Show violation list (potentially empty) in violations table
$('#violationsTableBody').html(table_html);
},
error: function (response) {
alert("{% trans 'Cannot load current violations from server' %}");
}
});
}
reload();
// Bind reload button
$('#btnReloadNow').click(reload);
// Toggle automatic reloading (every 30 s) based on checkbox
let autoReloadInterval = undefined;
$('#cbxAutoReload').change(function () {
if(this.checked) {
autoReloadInterval = setInterval(reload, 30*1000);
}
else {
if(autoReloadInterval !== undefined)
clearInterval(autoReloadInterval);
}
});
});
</script>
{% endblock extrahead %}
{% block content %}
<h4 class="mt-4 mb-4"><span id="violationCountBadge" class="badge badge-success">0</span> {% trans "Violation(s)" %}</h4>
<input type="checkbox" id="cbxAutoReload">
<label for="cbxAutoReload">{% trans "Auto reload?" %}</label>
<br>
<a href="#" id="btnReloadNow" class="btn btn-info">{% fa5_icon "sync-alt" "fas" %} {% trans "Reload now" %}</a>
<table class="table table-striped mt-4 mb-4">
<thead>
<tr>
<th>{% trans "Violation" %}</th>
<th>{% trans "Problem" %}</th>
<th>{% trans "Details" %}</th>
<th>{% trans "Since" %}</th>
<th></th>
</tr>
</thead>
<tbody id="violationsTableBody">
<tr class="text-muted">
<td colspan="5" class="text-center">
{% trans "No violations" %}
</td>
</tr>
</tbody>
</table>
<a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %}</a>
&middot;
<a href="{% url 'admin:schedule' event.slug %}">{% trans "Scheduling" %}</a>
{% endblock %}
from django.urls import path from django.urls import path
from AKScheduling.views import SchedulingAdminView, UnscheduledSlotsAdminView, TrackAdminView from AKScheduling.views import SchedulingAdminView, UnscheduledSlotsAdminView, TrackAdminView, \
ConstraintViolationsAdminView
def get_admin_urls_scheduling(admin_site): def get_admin_urls_scheduling(admin_site):
...@@ -9,6 +10,8 @@ def get_admin_urls_scheduling(admin_site): ...@@ -9,6 +10,8 @@ def get_admin_urls_scheduling(admin_site):
name="schedule"), name="schedule"),
path('<slug:event_slug>/unscheduled/', admin_site.admin_view(UnscheduledSlotsAdminView.as_view()), path('<slug:event_slug>/unscheduled/', admin_site.admin_view(UnscheduledSlotsAdminView.as_view()),
name="slots_unscheduled"), name="slots_unscheduled"),
path('<slug:slug>/constraint-violations/', admin_site.admin_view(ConstraintViolationsAdminView.as_view()),
name="constraint-violations"),
path('<slug:event_slug>/tracks/', admin_site.admin_view(TrackAdminView.as_view()), path('<slug:event_slug>/tracks/', admin_site.admin_view(TrackAdminView.as_view()),
name="tracks_manage"), name="tracks_manage"),
] ]
from django.views.generic import ListView from django.views.generic import ListView, DetailView
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from AKModel.models import AKSlot, AKTrack from AKModel.models import AKSlot, AKTrack, Event
from AKModel.views import AdminViewMixin, FilterByEventSlugMixin from AKModel.views import AdminViewMixin, FilterByEventSlugMixin
...@@ -47,3 +47,14 @@ class TrackAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView): ...@@ -47,3 +47,14 @@ class TrackAdminView(AdminViewMixin, FilterByEventSlugMixin, ListView):
context = super().get_context_data(object_list=object_list, **kwargs) context = super().get_context_data(object_list=object_list, **kwargs)
context["aks_without_track"] = self.event.ak_set.filter(track=None) context["aks_without_track"] = self.event.ak_set.filter(track=None)
return context return context
class ConstraintViolationsAdminView(AdminViewMixin, DetailView):
template_name = "admin/AKScheduling/constraint_violations.html"
model = Event
context_object_name = "event"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = f"{_('Constraint violations for')} {context['event']}"
return context
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