Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 520-akowner
  • 520-fix-event-wizard-datepicker
  • 520-fix-scheduling
  • 520-improve-scheduling
  • 520-improve-scheduling-2
  • 520-improve-submission
  • 520-improve-trackmanager
  • 520-improve-wall
  • 520-message-resolved
  • 520-status
  • 520-upgrades
  • add_express_interest_to_ak_overview
  • admin-production-color
  • bugfixes
  • csp
  • featire-ical-export
  • feature-ak-requirement-lists
  • feature-akslide-export-better-filename
  • feature-akslides
  • feature-better-admin
  • feature-better-cv-list
  • feature-colors
  • feature-constraint-checking
  • feature-constraint-checking-wip
  • feature-dashboard-history-button
  • feature-event-status
  • feature-event-wizard
  • feature-export-flag
  • feature-improve-admin
  • feature-improve-filters
  • feature-improved-user-creation-workflow
  • feature-interest-view
  • feature-mails
  • feature-modular-status
  • feature-plan-autoreload
  • feature-present-default
  • feature-register-link
  • feature-remaining-constraint-validation
  • feature-room-import
  • feature-scheduler-improve
  • feature-scheduling-2.0
  • feature-special-attention
  • feature-time-input
  • feature-tracker
  • feature-wiki-wishes
  • feature-wish-slots
  • feature-wizard-buttons
  • features-availabilities
  • fix-ak-times-above-folg
  • fix-api
  • fix-constraint-violation-string
  • fix-cv-checking
  • fix-default-slot-length
  • fix-default-slot-localization
  • fix-doc-minor
  • fix-duration-display
  • fix-event-tz-pytz-update
  • fix-history-interest
  • fix-interest-view
  • fix-js
  • fix-pipeline
  • fix-plan-timezone-now
  • fix-room-add
  • fix-scheduling-drag
  • fix-slot-defaultlength
  • fix-timezone
  • fix-translation-scheduling
  • fix-virtual-room-admin
  • fix-wizard-csp
  • font-locally
  • improve-admin
  • improve-online
  • improve-slides
  • improve-submission-coupling
  • interest_restriction
  • main
  • master
  • meta-debug-toolbar
  • meta-export
  • meta-makemessages
  • meta-performance
  • meta-tests
  • meta-tests-gitlab-test
  • meta-upgrades
  • mollux-master-patch-02906
  • port-availabilites-fullcalendar
  • qs
  • remove-tags
  • renovate/configure
  • renovate/django-4.x
  • renovate/django-5.x
  • renovate/django-bootstrap-datepicker-plus-5.x
  • renovate/django-bootstrap5-23.x
  • renovate/django-bootstrap5-24.x
  • renovate/django-compressor-4.x
  • renovate/django-debug-toolbar-4.x
  • renovate/django-registration-redux-2.x
  • renovate/django-simple-history-3.x
  • renovate/django-split-settings-1.x
  • renovate/django-timezone-field-5.x
100 results

Target

Select target project
  • konstantin/akplanning
  • matedealer/akplanning
  • kif/akplanning
  • mirco/akplanning
  • lordofthevoid/akplanning
  • voidptr/akplanning
  • xayomer/akplanning-fork
  • mollux/akplanning
  • neumantm/akplanning
  • mmarx/akplanning
  • nerf/akplanning
  • felix_bonn/akplanning
  • sebastian.uschmann/akplanning
13 results
Select Git revision
  • komasolver
  • main
  • renovate/django-5.x
  • renovate/django-debug-toolbar-5.x
  • renovate/django_csp-4.x
  • renovate/djangorestframework-3.x
  • renovate/tzdata-2025.x
  • renovate/uwsgi-2.x
8 results
Show changes
Commits on Source (7)
Showing
with 535 additions and 340 deletions
......@@ -68,3 +68,7 @@ class NewEventWizardActivateForm(forms.ModelForm):
class Meta:
fields = ["active"]
model = Event
class AdminIntermediateForm(forms.Form):
pass
......@@ -2,6 +2,7 @@ import itertools
from datetime import timedelta
from django.db import models
from django.db.models import Count
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.datetime_safe import datetime
......@@ -105,6 +106,12 @@ class Event(models.Model):
categories_with_aks.append((category, ak_list))
return categories_with_aks
def get_unscheduled_wish_slots(self):
return self.akslot_set.filter(start__isnull=True).annotate(Count('ak__owners')).filter(ak__owners__count=0)
def get_aks_without_availabilities(self):
return self.ak_set.annotate(Count('availabilities', distinct=True)).annotate(Count('owners', distinct=True)).filter(availabilities__count=0, owners__count__gt=0)
class AKOwner(models.Model):
""" An AKOwner describes the person organizing/holding an AK.
......@@ -328,6 +335,14 @@ class AK(models.Model):
def availabilities(self):
return "Availability".objects.filter(ak=self)
@property
def availabilities_total_duration(self):
from AKModel.availability.models import Availability
for a in Availability.objects.filter(ak=self):
print(a)
print(a.end)
# [a.end - a.start for a in ]
return 0
class Room(models.Model):
""" A room describes where an AK can be held.
......
{% extends "admin/base_site.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load bootstrap4 %}
{% load fontawesome_5 %}
{% block title %}{{event}}: {{ title }}{% endblock %}
{% block content %}
{% block action_preview %}
<p>
{{ preview|linebreaksbr }}
</p>
{% endblock %}
<form method="post">{% csrf_token %}
{% bootstrap_form form %}
<div class="float-right">
<button type="submit" class="save btn btn-success" value="Submit">
{% fa5_icon "check" 'fas' %} {% trans "Confirm" %}
</button>
</div>
<a href="javascript:history.back()" class="btn btn-info">
{% fa5_icon "times" 'fas' %} {% trans "Cancel" %}
</a>
</form>
{% endblock %}
{% extends "admin/base_site.html" %}
{% extends "admin/AKModel/action_intermediate.html" %}
{% load tags_AKModel %}
{% load i18n %}
{% load fontawesome_5 %}
{% block title %}{{event}}: {% trans "Delete Orga-Messages" %}{% endblock %}
{% block content %}
<h2>{% trans "Delete AK Orga Messages" %}</h2>
{% block action_preview %}
<p>{% blocktrans with message_count=ak_messages.count %}Are you sure you want to delete all orga messages for {{ event }}? This will permanently delete {{ message_count }} message(s):{% endblocktrans %}</p>
{% include "admin/AKModel/render_ak_messages.html" %}
<form method="post">{% csrf_token %}
<button type="submit" class="save btn btn-danger float-right" value="Confirm">
{% fa5_icon "check" 'fas' %} {% trans "Delete" %}
</button>
<a href="{% url 'admin:event_status' slug=event.slug %}" class="btn btn-info">
{% fa5_icon "times" 'fas' %} {% trans "Cancel" %}
</a>
</form>
{% endblock %}
......@@ -115,7 +115,7 @@
<div class="col-md-4">
<h3 class="block-header">{% trans "Messages" %}</h3>
{% include "admin/AKModel/render_ak_messages.html" %}
<a class="btn btn-danger" href="{% url 'admin:ak_delete_orga_messages' slug=event.slug %}">{% trans "Delete all messages" %}</a>
<a class="btn btn-danger" href="{% url 'admin:ak_delete_orga_messages' event_slug=event.slug %}">{% trans "Delete all messages" %}</a>
</div>
</div>
{% endtimezone %}
......
......@@ -79,7 +79,7 @@ def get_admin_urls_event(admin_site):
name="ak_csv_export"),
path('<slug:slug>/ak-wiki-export/', admin_site.admin_view(AKWikiExportView.as_view()),
name="ak_wiki_export"),
path('<slug:slug>/delete-orga-messages/', admin_site.admin_view(AKMessageDeleteView.as_view()),
path('<slug:event_slug>/delete-orga-messages/', admin_site.admin_view(AKMessageDeleteView.as_view()),
name="ak_delete_orga_messages"),
path('<slug:event_slug>/ak-slide-export/', export_slides, name="ak_slide_export"),
......
from abc import ABC, abstractmethod
from itertools import zip_longest
from django.contrib import admin, messages
......@@ -11,7 +12,7 @@ from django_tex.shortcuts import render_to_pdf
from rest_framework import viewsets, permissions, mixins
from AKModel.forms import NewEventWizardStartForm, NewEventWizardSettingsForm, NewEventWizardPrepareImportForm, \
NewEventWizardImportForm, NewEventWizardActivateForm
NewEventWizardImportForm, NewEventWizardActivateForm, AdminIntermediateForm
from AKModel.models import Event, AK, AKSlot, Room, AKTrack, AKCategory, AKOwner, AKOrgaMessage, AKRequirement
from AKModel.serializers import AKSerializer, AKSlotSerializer, RoomSerializer, AKTrackSerializer, AKCategorySerializer, \
AKOwnerSerializer
......@@ -194,22 +195,43 @@ class AKWikiExportView(AdminViewMixin, DetailView):
return context
class AKMessageDeleteView(AdminViewMixin, DeleteView):
model = Event
class IntermediateAdminView(AdminViewMixin, FormView, ABC):
template_name = "admin/AKModel/action_intermediate.html"
form_class = AdminIntermediateForm
@abstractmethod
def get_preview(self):
pass
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["title"] = self.title
context["preview"] = self.get_preview()
return context
class AKMessageDeleteView(EventSlugMixin, IntermediateAdminView):
template_name = "admin/AKModel/message_delete.html"
title = _("Delete AK Orga Messages")
def get_orga_messages_for_event(self, event):
return AKOrgaMessage.objects.filter(ak__event=event)
def get_preview(self):
return None
def get_success_url(self):
return reverse_lazy('admin:event_status', kwargs={'slug': self.event.slug})
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["ak_messages"] = self.get_orga_messages_for_event(self.get_object())
context["ak_messages"] = self.get_orga_messages_for_event(self.event)
return context
def post(self, request, *args, **kwargs):
self.get_orga_messages_for_event(self.get_object()).delete()
def form_valid(self, form):
self.get_orga_messages_for_event(self.event).delete()
messages.add_message(self.request, messages.SUCCESS, _("AK Orga Messages successfully deleted"))
return HttpResponseRedirect(reverse_lazy('admin:event_status', kwargs={'slug': self.get_object().slug}))
return super().form_valid(form)
class WizardViewMixin:
......
......@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-08-17 22:41+0200\n"
"POT-Creation-Date: 2022-09-27 17:59+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -79,7 +79,7 @@ msgstr "Seit"
#: .\AKScheduling\templates\admin\AKScheduling\constraint_violations.html:139
#: .\AKScheduling\templates\admin\AKScheduling\manage_tracks.html:243
#: .\AKScheduling\templates\admin\AKScheduling\scheduling.html:208
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:43
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:48
#: .\AKScheduling\templates\admin\AKScheduling\unscheduled.html:34
msgid "Event Status"
msgstr "Event-Status"
......@@ -158,10 +158,18 @@ msgid "AKs without availabilities"
msgstr "AKs ohne Verfügbarkeiten"
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:28
msgid "Create default availabilities"
msgstr "Standardverfügbarkeiten anlegen"
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:31
msgid "AK wishes with slots"
msgstr "AK-Wünsche mit Slots"
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:35
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:38
msgid "Delete slots for wishes"
msgstr ""
#: .\AKScheduling\templates\admin\AKScheduling\special_attention.html:40
msgid "AKs without slots"
msgstr "AKs ohne Slots"
......@@ -173,10 +181,57 @@ msgstr "Noch nicht geschedulte AK-Slots"
msgid "Count"
msgstr "Anzahl"
#: .\AKScheduling\views.py:103
#: .\AKScheduling\views.py:109
msgid "Interest updated"
msgstr "Interesse aktualisiert"
#: .\AKScheduling\views.py:141
#: .\AKScheduling\views.py:147
msgid "Wishes"
msgstr "Wünsche"
#: .\AKScheduling\views.py:155
msgid "Cleanup: Delete unscheduled slots for wishes"
msgstr "Aufräumen: Noch nicht geplante Slots für Wünsche löschen"
#: .\AKScheduling\views.py:162
#, python-brace-format
msgid ""
"The following {count} unscheduled slots of wishes will be deleted:\n"
"\n"
" {slots}"
msgstr ""
"Die folgenden {count} noch nicht geplanten Slots von Wünschen werden "
"gelöscht:\n"
"\n"
" {slots}"
#: .\AKScheduling\views.py:169
msgid "Unscheduled slots for wishes successfully deleted"
msgstr "Noch nicht geplante Slots für Wünsche erfolgreich gelöscht"
#: .\AKScheduling\views.py:174
msgid "Create default availabilities for AKs"
msgstr "Standardverfügbarkeiten für AKs anlegen"
#: .\AKScheduling\views.py:181
#, python-brace-format
msgid ""
"The following {count} AKs don't have any availability information. Create "
"default availability for them:\n"
"\n"
" {aks}"
msgstr ""
"Die folgenden {count} AKs haben keine Verfügbarkeitsinformationen. "
"Standardverfügbarkeiten für sie anlegen:\n"
"\n"
" {aks}"
#: .\AKScheduling\views.py:199
#, python-brace-format
msgid "Could not create default availabilities for AK: {ak}"
msgstr "Konnte keine Verfügbarkeit anlegen für AK: {ak}"
#: .\AKScheduling\views.py:204
#, python-brace-format
msgid "Created default availabilities for {count} AKs"
msgstr "Standardverfügbarkeiten für {count} AKs angelegt"
......@@ -157,7 +157,7 @@ def ak_owners_changed_handler(sender, instance: AK, action: str, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{owner} has the following conflicts: {new_violations}")
#print(f"{owner} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -203,7 +203,7 @@ def ak_conflicts_changed_handler(sender, instance: AK, action: str, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -249,7 +249,7 @@ def ak_prerequisites_changed_handler(sender, instance: AK, action: str, **kwargs
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -298,7 +298,7 @@ def ak_requirements_changed_handler(sender, instance: AK, action: str, **kwargs)
c.ak_slots_tmp.add(slot)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -310,7 +310,7 @@ def ak_requirements_changed_handler(sender, instance: AK, action: str, **kwargs)
@receiver(post_save, sender=AKSlot)
def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
# Changes might affect: Duplicate parallel, Two in room, Resodeadline
print(f"{sender} changed")
# print(f"{sender} changed")
event = instance.event
# == Check for two parallel slots by one of the owners ==
......@@ -341,7 +341,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{owner} has the following conflicts: {new_violations}")
# print(f"{owner} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -373,7 +373,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"Multiple slots in room {instance.room}: {new_violations}")
# print(f"Multiple slots in room {instance.room}: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the slot that was recently changed (important!)
......@@ -437,7 +437,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(instance)
new_violations.append(c)
print(f"{instance.ak} has the following slots outside availabilities: {new_violations}")
# print(f"{instance.ak} has the following slots outside availabilities: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -470,7 +470,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(instance)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -502,7 +502,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -534,7 +534,7 @@ def akslot_changed_handler(sender, instance: AKSlot, **kwargs):
c.ak_slots_tmp.add(other_slot)
new_violations.append(c)
print(f"{instance} has the following conflicts: {new_violations}")
# print(f"{instance} has the following conflicts: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......@@ -556,7 +556,7 @@ def akslot_deleted_handler(sender, instance: AKSlot, **kwargs):
# Manually clean up or remove constraint violations that belong to this slot since there is no cascade deletion
# for many2many relationships. Explicitly listening for AK deletion signals is not necessary since they will
# transitively trigger this signal and we always set both AK and AKSlot references in a constraint violation
print(f"{instance} deleted")
# print(f"{instance} deleted")
for cv in instance.constraintviolation_set.all():
# Make sure not delete CVs that e.g., show three parallel slots in a single room
......@@ -599,7 +599,7 @@ def room_requirements_changed_handler(sender, instance: Room, action: str, **kwa
@receiver(post_save, sender=Availability)
def availability_changed_handler(sender, instance: Availability, **kwargs):
# Changes might affect: category availability, AK availability, Room availability
print(f"{instance} changed")
# print(f"{instance} changed")
event = instance.event
......@@ -627,7 +627,7 @@ def availability_changed_handler(sender, instance: Availability, **kwargs):
c.ak_slots_tmp.add(slot)
new_violations.append(c)
print(f"{instance.ak} has the following slots outside availabilities: {new_violations}")
# print(f"{instance.ak} has the following slots outside availabilities: {new_violations}")
# ... and compare to/update list of existing violations of this type
# belonging to the AK that was recently changed (important!)
......
......@@ -22,21 +22,26 @@
{% for ak in aks_without_availabilities %}
<a href="{% url "submit:ak_edit" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br>
{% empty %}
-
-<br>
{% endfor %}
<a class="btn btn-warning mt-2" href="{% url "admin:autocreate-availabilities" event_slug=event.slug %}">{% trans "Create default availabilities" %}</a>
<h4 class="mt-4 mb-4">{% trans "AK wishes with slots" %}</h4>
{% for ak in ak_wishes_with_slots %}
<a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br>
<a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a> <a href="{% url "admin:AKModel_akslot_changelist" %}?ak={{ ak.pk }}">({{ ak.akslot__count }})</a><br>
{% empty %}
-
-<br>
{% endfor %}
<a class="btn btn-warning mt-2" href="{% url "admin:cleanup-wish-slots" event_slug=event.slug %}">{% trans "Delete slots for wishes" %}</a>
<h4 class="mt-4 mb-4">{% trans "AKs without slots" %}</h4>
{% for ak in aks_without_slots %}
<a href="{% url "submit:ak_detail" event_slug=event.slug pk=ak.pk %}">{{ ak }}</a><br>
{% empty %}
-
-<br>
{% endfor %}
<div class="mt-5">
......
from django.urls import path
from AKScheduling.views import SchedulingAdminView, UnscheduledSlotsAdminView, TrackAdminView, \
ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView
ConstraintViolationsAdminView, SpecialAttentionAKsAdminView, InterestEnteringAdminView, WishSlotCleanupView, \
AvailabilityAutocreateView
def get_admin_urls_scheduling(admin_site):
......@@ -14,6 +15,10 @@ def get_admin_urls_scheduling(admin_site):
name="constraint-violations"),
path('<slug:slug>/special-attention/', admin_site.admin_view(SpecialAttentionAKsAdminView.as_view()),
name="special-attention"),
path('<slug:event_slug>/cleanup-wish-slots/', admin_site.admin_view(WishSlotCleanupView.as_view()),
name="cleanup-wish-slots"),
path('<slug:event_slug>/autocreate-availabilities/', admin_site.admin_view(AvailabilityAutocreateView.as_view()),
name="autocreate-availabilities"),
path('<slug:event_slug>/tracks/', admin_site.admin_view(TrackAdminView.as_view()),
name="tracks_manage"),
path('<slug:event_slug>/enter-interest/<int:pk>', admin_site.admin_view(InterestEnteringAdminView.as_view()),
......
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Count
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import ListView, DetailView, UpdateView
from AKModel.models import AKSlot, AKTrack, Event, AK, AKCategory
from AKModel.views import AdminViewMixin, FilterByEventSlugMixin, EventSlugMixin
from AKModel.views import AdminViewMixin, FilterByEventSlugMixin, EventSlugMixin, IntermediateAdminView
from AKScheduling.forms import AKInterestForm
......@@ -71,7 +74,7 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView):
context = super().get_context_data(**kwargs)
context["title"] = f"{_('AKs requiring special attention for')} {context['event']}"
aks = AK.objects.filter(event=context["event"])
aks = AK.objects.filter(event=context["event"]).annotate(Count('owners', distinct=True)).annotate(Count('akslot', distinct=True)).annotate(Count('availabilities', distinct=True))
aks_with_comment = []
ak_wishes_with_slots = []
aks_without_availabilities = []
......@@ -81,13 +84,13 @@ class SpecialAttentionAKsAdminView(AdminViewMixin, DetailView):
if ak.notes != "":
aks_with_comment.append(ak)
if ak.wish:
if ak.akslot_set.count() > 0:
if ak.owners__count == 0:
if ak.akslot__count > 0:
ak_wishes_with_slots.append(ak)
else:
if ak.akslot_set.count() == 0:
if ak.akslot__count == 0:
aks_without_slots.append(ak)
if ak.availabilities.count() == 0:
if ak.availabilities__count == 0:
aks_without_availabilities.append(ak)
context["aks_with_comment"] = aks_with_comment
......@@ -146,3 +149,58 @@ class InterestEnteringAdminView(SuccessMessageMixin, AdminViewMixin, EventSlugMi
context["categories_with_aks"] = categories_with_aks
return context
class WishSlotCleanupView(EventSlugMixin, IntermediateAdminView):
title = _('Cleanup: Delete unscheduled slots for wishes')
def get_success_url(self):
return reverse_lazy('admin:special-attention', kwargs={'slug': self.event.slug})
def get_preview(self):
slots = self.event.get_unscheduled_wish_slots()
return _("The following {count} unscheduled slots of wishes will be deleted:\n\n {slots}").format(
count=len(slots),
slots=", ".join(str(s.ak) for s in slots)
)
def form_valid(self, form):
self.event.get_unscheduled_wish_slots().delete()
messages.add_message(self.request, messages.SUCCESS, _("Unscheduled slots for wishes successfully deleted"))
return super().form_valid(form)
class AvailabilityAutocreateView(EventSlugMixin, IntermediateAdminView):
title = _('Create default availabilities for AKs')
def get_success_url(self):
return reverse_lazy('admin:special-attention', kwargs={'slug': self.event.slug})
def get_preview(self):
aks = self.event.get_aks_without_availabilities()
return _("The following {count} AKs don't have any availability information. "
"Create default availability for them:\n\n {aks}").format(
count=len(aks),
aks=", ".join(str(ak) for ak in aks)
)
def form_valid(self, form):
from AKModel.availability.models import Availability
success_count = 0
for ak in self.event.get_aks_without_availabilities():
try:
availability = Availability.with_event_length(event=self.event, ak=ak)
availability.save()
success_count += 1
except:
messages.add_message(
self.request, messages.WARNING,
_("Could not create default availabilities for AK: {ak}").format(ak=ak)
)
messages.add_message(
self.request, messages.SUCCESS,
_("Created default availabilities for {count} AKs").format(count=success_count)
)
return super().form_valid(form)
......@@ -151,7 +151,7 @@ class AKEditForm(AKForm):
self.fields["tags_raw"].initial = "; ".join(str(tag) for tag in self.instance.tags.all())
class AKWishForm(AKSubmissionForm):
class AKWishForm(AKForm):
class Meta(AKForm.Meta):
exclude = ['owners', 'link', 'protocol_link']
......
......@@ -235,95 +235,96 @@
<p style="margin-top: 30px;margin-bottom: 30px;">{{ ak.description|linebreaks }}</p>
<table class="table">
<thead>
<tr>
{% if not ak.event.plan_hidden or user.is_staff %}
<th>{% trans "When?" %}</th>
{% endif %}
<th>{% trans "Duration" %}</th>
{% if not ak.event.plan_hidden or user.is_staff %}
<th>{% trans "Room" %}</th>
{% endif %}
<th></th>
</tr>
</thead>
<tbody>
{% for slot in ak.akslot_set.all %}
{% if not ak.wish %}
<table class="table">
<thead>
<tr>
{% if not ak.event.plan_hidden or user.is_staff %}
<td>{{ slot.time_simplified }}</td>
<th>{% trans "When?" %}</th>
{% endif %}
<td>{{ slot.duration_simplified }}</td>
<th>{% trans "Duration" %}</th>
{% if not ak.event.plan_hidden or user.is_staff %}
<td>
{% if slot.room %}
{% if "AKPlan"|check_app_installed %}
<a href="{% url 'plan:plan_room' event_slug=ak.event.slug pk=slot.room.pk %}">{{ slot.room }}</a>
<th>{% trans "Room" %}</th>
{% endif %}
<th></th>
</tr>
</thead>
<tbody>
{% for slot in ak.akslot_set.all %}
<tr>
{% if not ak.event.plan_hidden or user.is_staff %}
<td>{{ slot.time_simplified }}</td>
{% endif %}
<td>{{ slot.duration_simplified }}</td>
{% if not ak.event.plan_hidden or user.is_staff %}
<td>
{% if slot.room %}
{% if "AKPlan"|check_app_installed %}
<a href="{% url 'plan:plan_room' event_slug=ak.event.slug pk=slot.room.pk %}">{{ slot.room }}</a>
{% else %}
{{ slot.room }}
{% endif %}
{% else %}
{{ slot.room }}
-
{% endif %}
</td>
{% endif %}
<td>
{% if not slot.start %}
<a href="{% url 'submit:akslot_edit' event_slug=ak.event.slug pk=slot.pk %}"
data-toggle="tooltip" title="{% trans 'Edit' %}"
class="btn btn-success">{% fa5_icon 'pencil-alt' 'fas' %}</a>
<a href="{% url 'submit:akslot_delete' event_slug=ak.event.slug pk=slot.pk %}"
data-toggle="tooltip" title="{% trans 'Delete' %}"
class="btn btn-danger">{% fa5_icon 'times' 'fas' %}</a>
{% else %}
-
{% if "AKOnline"|check_app_installed and slot.room and slot.room.virtualroom and slot.room.virtualroom.url != '' %}
<a class="btn btn-success" href="{{ slot.room.virtualroom.url }}">
{% fa5_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %}
</a>
{% endif %}
{% endif %}
</td>
{% endif %}
<td>
{% if not slot.start %}
<a href="{% url 'submit:akslot_edit' event_slug=ak.event.slug pk=slot.pk %}"
data-toggle="tooltip" title="{% trans 'Edit' %}"
class="btn btn-success">{% fa5_icon 'pencil-alt' 'fas' %}</a>
<a href="{% url 'submit:akslot_delete' event_slug=ak.event.slug pk=slot.pk %}"
data-toggle="tooltip" title="{% trans 'Delete' %}"
class="btn btn-danger">{% fa5_icon 'times' 'fas' %}</a>
{% else %}
{% if "AKOnline"|check_app_installed and slot.room and slot.room.virtualroom and slot.room.virtualroom.url != '' %}
<a class="btn btn-success" href="{{ slot.room.virtualroom.url }}">
{% fa5_icon 'external-link-alt' 'fas' %} {% trans "Go to virtual room" %}
</a>
{% if user.is_staff %}
<a href="{% url 'admin:AKModel_akslot_change' slot.pk %}"
data-toggle="tooltip" title="{% trans 'Schedule' %}"
class="btn btn-outline-success">{% fa5_icon 'stream' 'fas' %}</a>
{% endif %}
{% endif %}
{% if user.is_staff %}
<a href="{% url 'admin:AKModel_akslot_change' slot.pk %}"
data-toggle="tooltip" title="{% trans 'Schedule' %}"
class="btn btn-outline-success">{% fa5_icon 'stream' 'fas' %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if ak.event.active %}
<div class="">
<a href="{% url 'submit:akslot_add' event_slug=ak.event.slug pk=ak.pk %}"
class="btn btn-success">{% fa5_icon 'plus' 'fas' %} {% trans "Add another slot" %}</a>
</div>
{% endif %}
{% if ak.event.active %}
<div class="">
<a href="{% url 'submit:akslot_add' event_slug=ak.event.slug pk=ak.pk %}"
class="btn btn-success">{% fa5_icon 'plus' 'fas' %} {% trans "Add another slot" %}</a>
</div>
{% endif %}
{% if 'AKPlan'|check_app_installed %}
<div id='akSlotCalendar' style="margin-top: 50px;margin-bottom: 50px;"></div>
{% endif %}
{% if 'AKPlan'|check_app_installed %}
<div id='akSlotCalendar' style="margin-top: 50px;margin-bottom: 50px;"></div>
{% endif %}
<h4 style="margin-top: 30px;">{% trans "Possible Times" %}</h4>
<table class="table">
<thead>
<tr>
<th>{% trans "Start" %}</th>
<th>{% trans "End" %}</th>
</tr>
</thead>
<tbody>
{% for a in availabilities %}
<h4 style="margin-top: 30px;">{% trans "Possible Times" %}</h4>
<table class="table">
<thead>
<tr>
<td>{{ a.start | timezone:event.timezone | date:"l H:i" }}</td>
<td>{{ a.end | timezone:event.timezone | date:"l H:i" }}</td>
<th>{% trans "Start" %}</th>
<th>{% trans "End" %}</th>
</tr>
{% endfor %}
</tbody>
</table>
</thead>
<tbody>
{% for a in availabilities %}
<tr>
<td>{{ a.start | timezone:event.timezone | date:"l H:i" }}</td>
<td>{{ a.end | timezone:event.timezone | date:"l H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endblock %}
......@@ -223,10 +223,11 @@ class AKAndAKWishSubmissionView(EventSlugMixin, EventInactiveRedirectMixin, Crea
tag, was_created = AKTag.objects.get_or_create(name=tag_name)
self.object.tags.add(tag)
# Generate slot(s)
for duration in form.cleaned_data["durations"]:
new_slot = AKSlot(ak=self.object, duration=duration, event=self.object.event)
new_slot.save()
# Generate slot(s) (but not for wishes)
if "durations" in form.cleaned_data:
for duration in form.cleaned_data["durations"]:
new_slot = AKSlot(ak=self.object, duration=duration, event=self.object.event)
new_slot.save()
return super_form_valid
......@@ -269,6 +270,8 @@ class AKEditView(EventSlugMixin, EventInactiveRedirectMixin, UpdateView):
return redirect(reverse_lazy('submit:submission_overview',
kwargs={'event_slug': form.cleaned_data["event"].slug}))
previous_owner_count = self.object.owners.count()
super_form_valid = super().form_valid(form)
# Detach existing tags
......@@ -279,6 +282,17 @@ class AKEditView(EventSlugMixin, EventInactiveRedirectMixin, UpdateView):
tag, was_created = AKTag.objects.get_or_create(name=tag_name)
self.object.tags.add(tag)
# Did this AK change from wish to AK or vice versa?
new_owner_count = self.object.owners.count()
# Now AK:
if previous_owner_count == 0 and new_owner_count > 0 and self.object.akslot_set.count() == 0:
# Create one slot with default length
AKSlot.objects.create(ak=self.object, duration=self.object.event.default_slot, event=self.object.event)
# Now wish:
elif previous_owner_count > 0 and new_owner_count == 0:
# Delete all unscheduled slots
self.object.akslot_set.filter(start__isnull=True).delete()
return super_form_valid
......