From 53acfb9b7857a7cb6867b47a1abfe530f76e2aa6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Sat, 20 Apr 2024 18:28:07 +0200
Subject: [PATCH 1/4] Hide headings for empty constraints in detail
 representation of AKSlot

This will remove empty lines from the "bubbles" containing details in scheduler.
This implements #215
---
 AKModel/models.py | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/AKModel/models.py b/AKModel/models.py
index 926b7e0e..fc8ab147 100644
--- a/AKModel/models.py
+++ b/AKModel/models.py
@@ -367,7 +367,7 @@ class AK(models.Model):
     @property
     def details(self):
         """
-        Generate a detailled string representation, e.g., for usage in scheduling
+        Generate a detailed string representation, e.g., for usage in scheduling
         :return: string representation of that AK with all details
         :rtype: str
         """
@@ -376,15 +376,19 @@ class AK(models.Model):
         from AKModel.availability.models import Availability
         availabilities = ', \n'.join(f'{a.simplified}' for a in Availability.objects.select_related('event')
                                      .filter(ak=self))
-        return f"""{self.name}{" (R)" if self.reso else ""}:
+        detail_string = f"""{self.name}{" (R)" if self.reso else ""}:
         
         {self.owners_list}
 
-        {_('Interest')}: {self.interest}
-        {_("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())}
-        {_("Availabilities")}: \n{availabilities}"""
+        {_('Interest')}: {self.interest}"""
+        if self.requirements.count() > 0:
+            detail_string += f"\n{_('Requirements')}: {', '.join(str(r) for r in self.requirements.all())}"
+        if self.conflicts.count() > 0:
+            detail_string += f"\n{_('Conflicts')}: {', '.join(str(c) for c in self.conflicts.all())}"
+        if self.prerequisites.count() > 0:
+            detail_string += f"\n{_('Prerequisites')}: {', '.join(str(p) for p in self.prerequisites.all())}"
+        detail_string += f"\n{_('Availabilities')}: \n{availabilities}"
+        return detail_string
 
     @property
     def owners_list(self):
-- 
GitLab


From 15914cf9dc6032fd72b2ecf3b54f67be24d22541 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Sat, 20 Apr 2024 18:55:58 +0200
Subject: [PATCH 2/4] Count only non-resolved CVs in scheduler and CV overview

This implements #201
---
 .../AKScheduling/constraint_violations.html   | 19 +++++++++++-------
 .../admin/AKScheduling/scheduling.html        | 20 +++++++++++--------
 2 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/AKScheduling/templates/admin/AKScheduling/constraint_violations.html b/AKScheduling/templates/admin/AKScheduling/constraint_violations.html
index 6a71f9b4..e8454247 100644
--- a/AKScheduling/templates/admin/AKScheduling/constraint_violations.html
+++ b/AKScheduling/templates/admin/AKScheduling/constraint_violations.html
@@ -23,26 +23,31 @@
             const callback_success = function(response) {
                let table_html = '';
 
+               let unresolved_constraint_violations = 0;
+
                if(response.length > 0) {
-                   // Update violation count badge
-                   $('#violationCountBadge').html(response.length).removeClass('bg-success').addClass('bg-warning');
 
                    // Update violations table
                    for(let i=0;i<response.length;i++) {
-                       if(response[i].manually_resolved)
+                       if(response[i].manually_resolved) {
                            table_html += '<tr class="text-muted"><td class="nowrap">{% fa6_icon "check" "fas" %}</td>';
-                       else
+                       }
+                       else {
                            table_html += '<tr><td></td>';
+                           unresolved_constraint_violations++;
+                       }
                        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('bg-warning').addClass('bg-success');
-
                    // Update violations table
                    table_html ='<tr class="text-muted"><td colspan="5" class="text-center">{% trans "No violations" %}</td></tr>'
                }
+               // Update violation count badge
+               if(unresolved_constraint_violations > 0)
+                   $('#violationCountBadge').html(unresolved_constraint_violations).removeClass('bg-success').addClass('bg-warning');
+               else
+                   $('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
 
                // Show violation list (potentially empty) in violations table
                $('#violationsTableBody').html(table_html);
diff --git a/AKScheduling/templates/admin/AKScheduling/scheduling.html b/AKScheduling/templates/admin/AKScheduling/scheduling.html
index 1be3071c..8d592aec 100644
--- a/AKScheduling/templates/admin/AKScheduling/scheduling.html
+++ b/AKScheduling/templates/admin/AKScheduling/scheduling.html
@@ -215,20 +215,24 @@
                if(response.length > 0) {
                    // 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">{% fa6_icon "check" "fas" %} ';
+                       let icon_html = '';
+                       let muted_html = '';
+                       if(response[i].manually_resolved) {
+                            icon_html = '{% fa6_icon "check" "fas" %} ';
+                            muted_html = 'text-muted';
+                          }
                        else {
-                           table_html += '<tr><td>';
                            unresolved_violations_count++;
                        }
 
                        if(response[i].level_display==='{% trans "Violation" %}')
-                           table_html += '{% fa6_icon "exclamation-triangle" "fas" %}';
+                           icon_html += '{% fa6_icon "exclamation-triangle" "fas" %}';
                        else
-                           table_html += '{% fa6_icon "info-circle" "fas" %}';
+                           icon_html += '{% fa6_icon "info-circle" "fas" %}';
 
+                       table_html += '<tr class="'+ muted_html+ '"><td class="nowrap">' + icon_html;
                        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>"
+                       table_html += "<tr class='" + muted_html + "'><td colspan='2' class='small'>" + response[i].details + "</td></tr>"
                    }
                }
                else {
@@ -238,7 +242,7 @@
 
                // Update violation count badge
                 if(unresolved_violations_count > 0)
-                    $('#violationCountBadge').html(response.length).removeClass('bg-success').addClass('bg-warning');
+                    $('#violationCountBadge').html(unresolved_violations_count).removeClass('bg-success').addClass('bg-warning');
                 else
                     $('#violationCountBadge').html(0).removeClass('bg-warning').addClass('bg-success');
 
@@ -358,7 +362,7 @@
                 {% endfor %}
               </div>
               <div class="tab-pane fade" id="violations">
-                  <table class="table table-striped mt-4 mb-4">
+                  <table class="table mt-4 mb-4">
                     <thead>
                         <tr>
                             <th>{% trans "Level" %}</th>
-- 
GitLab


From 0f312eb8823f507512ffed0408b4a4e0706c8eeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Sat, 20 Apr 2024 19:01:28 +0200
Subject: [PATCH 3/4] Only check room size if interest > -1

This prevents warning for AKs where interest was not specified and room capacity is quite small.
This resolves #209
---
 AKScheduling/models.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/AKScheduling/models.py b/AKScheduling/models.py
index 769527f7..1495f311 100644
--- a/AKScheduling/models.py
+++ b/AKScheduling/models.py
@@ -80,8 +80,8 @@ def check_capacity_for_slot(slot: AKSlot):
     :rtype: ConstraintViolation or None
     """
 
-    # If slot is scheduled in a room
-    if slot.room and slot.room.capacity >= 0:
+    # If slot is scheduled in a room and interest was specified
+    if slot.room and slot.room.capacity >= 0 and slot.ak.interest >= 0:
         # Create a violation if interest exceeds room capacity
         if slot.room.capacity < slot.ak.interest:
             c = ConstraintViolation(
-- 
GitLab


From d5fad1a61b8e26b0f1a166c62dd97f0819fc6be9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Benjamin=20H=C3=A4ttasch?=
 <benjamin.haettasch@fachschaft.informatik.tu-darmstadt.de>
Date: Sat, 20 Apr 2024 19:54:37 +0200
Subject: [PATCH 4/4] Fix small code style issues

---
 AKModel/forms.py | 3 +++
 AKPlan/views.py  | 1 +
 2 files changed, 4 insertions(+)

diff --git a/AKModel/forms.py b/AKModel/forms.py
index 24074ecd..4d1fe7ef 100644
--- a/AKModel/forms.py
+++ b/AKModel/forms.py
@@ -14,6 +14,9 @@ from AKModel.models import Event, AKCategory, AKRequirement, Room
 
 
 class DateTimeInput(forms.DateInput):
+    """
+    Simple widget for datetime input fields using the HTML5 datetime-local input type
+    """
     input_type = 'datetime-local'
 
 
diff --git a/AKPlan/views.py b/AKPlan/views.py
index fdd4e7e8..4123256e 100644
--- a/AKPlan/views.py
+++ b/AKPlan/views.py
@@ -83,6 +83,7 @@ class PlanScreenView(PlanIndexView):
             return redirect(reverse_lazy("plan:plan_overview", kwargs={"event_slug": self.event.slug}))
         return s
 
+    # pylint: disable=attribute-defined-outside-init
     def get_queryset(self):
         now = datetime.now().astimezone(self.event.timezone)
         # Wall during event: Adjust, show only parts in the future
-- 
GitLab