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

Improve CV overview and reuse in scheduler

Move general ajax setup call to external js file
Move common functionality for CV loading to external js file
Visualize existing violations in scheduler
Add reloading function to scheduler
parent a1705215
No related branches found
No related tags found
No related merge requests found
function loadCVs(url, callback_success, callback_error) {
$.ajax({
url: url,
type: 'GET',
success: callback_success,
error: callback_error
});
}
const default_cv_callback_error = function(response) {
alert("{% trans 'Cannot load current violations from server' %}");
}
...@@ -13,78 +13,45 @@ ...@@ -13,78 +13,45 @@
{% block extrahead %} {% block extrahead %}
{{ block.super }} {{ block.super }}
<script type="application/javascript" src="{% static "common/js/api.js" %}"></script>
<script type="application/javascript" src="{% static "AKScheduling/js/scheduling.js" %}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
// CSRF Protection/Authentication const url = "{% url "model:scheduling-constraint-violations-list" event_slug=event.slug %}";
function getCookie(name) {
let cookieValue = null; const callback_success = function(response) {
if (document.cookie && document.cookie !== '') { let table_html = '';
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) { if(response.length > 0) {
const cookie = cookies[i].trim(); // Update violation count badge
// Does this cookie string begin with the name we want? $('#violationCountBadge').html(response.length).removeClass('badge-success').addClass('badge-warning');
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); // Update violations table
break; for(let i=0;i<response.length;i++) {
} if(response[i].manually_resolved)
} table_html += '<tr class="text-muted"><td class="nowrap">{% fa5_icon "check" "fas" %}</td>';
} else
return cookieValue; table_html += '<tr><td></td>';
table_html += "<td>" + response[i].level_display + "</td><td>" + response[i].type_display + "</td><td>" + response[i].details + "</td><td class='nowrap'>" + response[i].timestamp_display + "</td><td><a href='" + response[i].edit_url + "'><i class='btn btn-primary fa fa-pen'></i></a></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);
} }
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 // (Re-)Load constraint violations using AJAX and visualize using violation count badge and violation table
function reload() { function reload() {
$.ajax({ loadCVs(url, callback_success, default_cv_callback_error)
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++) {
if(response[i].manually_resolved)
table_html += '<tr class="text-muted"><td class="nowrap">{% fa5_icon "check" "fas" %}</td>';
else
table_html += '<tr><td></td>';
table_html += "<td>" + response[i].level_display + "</td><td>" + response[i].type_display + "</td><td>" + response[i].details + "</td><td class='nowrap'>" + response[i].timestamp_display + "</td><td><a href='" + response[i].edit_url + "'><i class='btn btn-primary fa fa-pen'></i></a></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(); reload();
......
...@@ -59,48 +59,17 @@ ...@@ -59,48 +59,17 @@
} }
</style> </style>
<script type="application/javascript" src="{% static "common/js/api.js" %}"></script>
<script type="application/javascript" src="{% static "AKScheduling/js/scheduling.js" %}"></script>
<script> <script>
document.addEventListener('DOMContentLoaded', function () { 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);
}
}
});
// Place slots by dropping placeholders on calendar // Place slots by dropping placeholders on calendar
var containerEl = document.getElementById('unscheduled-slots'); var containerEl = document.getElementById('unscheduled-slots');
new FullCalendar.Draggable(containerEl, { new FullCalendar.Draggable(containerEl, {
itemSelector: '.unscheduled-slot', itemSelector: '.unscheduled-slot',
}); });
// Calendar // Calendar
var planEl = document.getElementById('planCalendar'); var planEl = document.getElementById('planCalendar');
...@@ -211,6 +180,58 @@ ...@@ -211,6 +180,58 @@
$('.unscheduled-slot').each(function() { $('.unscheduled-slot').each(function() {
$(this).tooltip({title: $(this).first().attr('data-details'), trigger: 'hover'}); $(this).tooltip({title: $(this).first().attr('data-details'), trigger: 'hover'});
}); });
const cv_url = "{% url "model:scheduling-constraint-violations-list" event_slug=event.slug %}";
const cv_callback_success = function(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++) {
if(response[i].manually_resolved)
table_html += '<tr class="text-muted"><td class="nowrap">{% fa5_icon "check" "fas" %} ';
else
table_html += '<tr><td>';
if(response[i].level_display==='{% trans "Violation" %}')
table_html += '{% fa5_icon "exclamation-circle" "fas" %}';
else
table_html += '{% fa5_icon "info-circle" "fas" %}';
table_html += "</td><td class='small'>" + response[i].type_display + "</td></tr>";
table_html += "<tr><td colspan='2' class='small'>" + response[i].details + "</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="2" class="text-center">{% trans "No violations" %}</td></tr>'
}
// Show violation list (potentially empty) in violations table
$('#violationsTableBody').html(table_html);
}
function reloadCVs() {
loadCVs(cv_url, cv_callback_success, default_cv_callback_error);
}
reloadCVs();
const reloadBtn = $('#reloadBtn');
function reload() {
plan.refetchEvents();
reloadCVs();
// TODO Reload unscheduled AKs
}
reloadBtn.click(reload);
}); });
</script> </script>
...@@ -218,28 +239,65 @@ ...@@ -218,28 +239,65 @@
<body> <body>
<div class="box p-3"> <div class="box p-3">
<div class="row header pb-2"> <div class="row header pb-2">
<div class="col-sm-10"> <div class="col">
<h2 class="d-inline">{% trans "Scheduling for" %} {{event}}</h2> <h5 class="d-inline ml-2"><a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %} {% fa5_icon "level-up-alt" "fas" %}</a></h5> <h2 class="d-inline">
<button class="btn btn-outline-warning" id="reloadBtn" style="vertical-align: text-bottom;">
<span id="reloadBtnVisDefault">{% fa5_icon "redo" "fas" %}</span>
</button>
{% trans "Scheduling for" %} {{event}}
</h2>
<h5 class="d-inline ml-2">
<a href="{% url 'admin:event_status' event.slug %}">{% trans "Event Status" %} {% fa5_icon "level-up-alt" "fas" %}</a>
</h5>
</div> </div>
<div class="col-sm-2"></div>
</div> </div>
<div class="row content"> <div class="row content">
<div class="col-md-10 col-lg-10"> <div class="col-md-8 col-lg-9 col-xl-10">
<div id="planCalendar"></div> <div id="planCalendar"></div>
</div> </div>
<div class="col-md-2 col-lg-2" id="unscheduled-slots"> <div class="col-md-4 col-lg-3 col-xl-2" id="sidebar">
{% regroup slots_unscheduled by ak.track as slots_unscheduled_by_track_list %} <ul class="nav nav-tabs">
{% for track_slots in slots_unscheduled_by_track_list %} <li class="nav-item">
{% if track_slots.grouper %} <a class="nav-link active" data-toggle="tab" href="#unscheduled-slots">{% trans "Unscheduled" %}</a>
<h5 class="mt-2">{{ track_slots.grouper }}</h5> </li>
{% endif %} <li class="nav-item">
{% for slot in track_slots.list %} <a class="nav-link" data-toggle="tab" href="#violations"><span id="violationCountBadge" class="badge badge-success">0</span> {% trans "Violation(s)" %}</a>
<div class="unscheduled-slot badge badge-primary" style='background-color: {{ slot.ak.category.color }}' </li>
data-event='{ "title": "{{ slot.ak.short_name }}", "duration": {"hours": "{{ slot.duration|unlocalize }}"}, "constraint": "roomAvailable", "description": "{{ slot.ak.details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}"}' data-details="{{ slot.ak.details }}">{{ slot.ak.short_name }} </ul>
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }} <div id="sidebarContent" class="tab-content">
</div> <div class="tab-pane fade show active" id="unscheduled-slots">
{% regroup slots_unscheduled by ak.track as slots_unscheduled_by_track_list %}
{% for track_slots in slots_unscheduled_by_track_list %}
{% if track_slots.grouper %}
<h5 class="mt-2">{{ track_slots.grouper }}</h5>
{% endif %}
{% for slot in track_slots.list %}
<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 }}"}, "constraint": "roomAvailable", "description": "{{ slot.ak.details | escapejs }}", "slotID": "{{ slot.pk }}", "backgroundColor": "{{ slot.ak.category.color }}"}' data-details="{{ slot.ak.details }}">{{ slot.ak.short_name }}
({{ slot.duration }} h)<br>{{ slot.ak.owners_list }}
</div>
{% endfor %}
{% endfor %} {% endfor %}
{% endfor %} </div>
<div class="tab-pane fade" id="violations">
<table class="table table-striped mt-4 mb-4">
<thead>
<tr>
<th>{% trans "Level" %}</th>
<th>{% trans "Problem" %}</th>
</tr>
</thead>
<tbody id="violationsTableBody">
<tr class="text-muted">
<td colspan="2" class="text-center">
{% trans "No violations" %}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
<div class="row footer"> <div class="row footer">
......
// 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);
}
}
});
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment