diff --git a/arbeitsplan/admin.py b/arbeitsplan/admin.py
index 9d81e7a..88f5113 100644
--- a/arbeitsplan/admin.py
+++ b/arbeitsplan/admin.py
@@ -31,7 +31,7 @@ class AufgabeAdmin(admin.ModelAdmin):
class MeldungAdmin(admin.ModelAdmin):
date_hierachy = 'erstellt'
- list_display = 'melder', 'aufgabe', 'prefMitglied', 'prefVorstand'
+ list_display = 'melder', 'aufgabe', 'prefMitglied'
# list_filter = ('melder', 'aufgabe')
list_filter = ('aufgabe__gruppe__gruppe', )
search_fields = ('melder__last_name', 'melder__first_name', 'aufgabe__aufgabe')
diff --git a/arbeitsplan/forms.py b/arbeitsplan/forms.py
index 4cf2a58..411746e 100644
--- a/arbeitsplan/forms.py
+++ b/arbeitsplan/forms.py
@@ -294,45 +294,25 @@ class PersonAufgabengruppeFilterForm (NameFilterForm,
pass
-class PraeferenzFilterForm (CrispyFilterMixin, forms.Form):
-
- praeferenz = forms.MultipleChoiceField(choices=models.Meldung.Preferences.choices,
- widget=forms.CheckboxSelectMultiple,
- label="Vorliebe Mitglied",
- required=False,
- initial=[models.Meldung.Preferences.RELUCTANTLY,
- models.Meldung.Preferences.OK,
- models.Meldung.Preferences.GLADLY,
- ],
- )
- __layout = Layout(
- HTML('
'),
- InlineCheckboxes('praeferenz'),
- )
-
-
-class PraeferenzVorstandFilterForm (CrispyFilterMixin, forms.Form):
-
- praeferenzVorstand = forms.MultipleChoiceField(choices=models.Meldung.Preferences.choices,
- widget=forms.CheckboxSelectMultiple,
- label="Vorliebe Vorstand",
- required=False,
- initial=[models.Meldung.Preferences.NEVER,
- models.Meldung.Preferences.RELUCTANTLY,
- models.Meldung.Preferences.OK,
- models.Meldung.Preferences.GLADLY,
- ],
- )
+class PraeferenzFilterForm(CrispyFilterMixin, forms.Form):
+ praeferenz = forms.MultipleChoiceField(
+ choices=models.Meldung.Preferences.choices,
+ widget=forms.CheckboxSelectMultiple,
+ label="Vorliebe Mitglied",
+ required=False,
+ initial=[
+ models.Meldung.Preferences.YES,
+ ],
+ )
__layout = Layout(
- HTML('
'),
- InlineCheckboxes('praeferenzVorstand'),
- )
+ HTML("
"),
+ InlineCheckboxes("praeferenz"),
+ )
-class PersonAufgGrpPraefernzFilterForm (NameFilterForm,
+class PersonAufgGrpPraeferenzFilterForm (NameFilterForm,
AufgabengruppeFilterForm,
PraeferenzFilterForm,
- PraeferenzVorstandFilterForm,
forms.Form):
pass
diff --git a/arbeitsplan/migrations/0024_alter_meldung_prefmitglied_and_more.py b/arbeitsplan/migrations/0024_alter_meldung_prefmitglied_and_more.py
new file mode 100644
index 0000000..31f3a71
--- /dev/null
+++ b/arbeitsplan/migrations/0024_alter_meldung_prefmitglied_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2026-01-09 20:28
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('arbeitsplan', '0023_remove_aufgabe_teamleader'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='meldung',
+ name='prefMitglied',
+ field=models.IntegerField(choices=[(-1, 'Nein'), (1, 'Ja')], default=-1, help_text='Welche Präferenz hast du für diese Aufgabe?', verbose_name='Präferenz'),
+ ),
+ migrations.AlterField(
+ model_name='meldung',
+ name='prefVorstand',
+ field=models.IntegerField(choices=[(-1, 'Nein'), (1, 'Ja')], default=1, help_text='Traust du diesem Mitglied die Aufgabe zu?'),
+ ),
+ ]
diff --git a/arbeitsplan/migrations/0025_remove_meldung_bemerkungvorstand_and_more.py b/arbeitsplan/migrations/0025_remove_meldung_bemerkungvorstand_and_more.py
new file mode 100644
index 0000000..9f10742
--- /dev/null
+++ b/arbeitsplan/migrations/0025_remove_meldung_bemerkungvorstand_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 4.2.19 on 2026-01-09 22:00
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('arbeitsplan', '0024_alter_meldung_prefmitglied_and_more'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='meldung',
+ name='bemerkungVorstand',
+ ),
+ migrations.RemoveField(
+ model_name='meldung',
+ name='prefVorstand',
+ ),
+ ]
diff --git a/arbeitsplan/migrations/0026_meldung_unique_aufgabe_melder_and_more.py b/arbeitsplan/migrations/0026_meldung_unique_aufgabe_melder_and_more.py
new file mode 100644
index 0000000..1d3c7fb
--- /dev/null
+++ b/arbeitsplan/migrations/0026_meldung_unique_aufgabe_melder_and_more.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.19 on 2026-01-10 12:08
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('arbeitsplan', '0025_remove_meldung_bemerkungvorstand_and_more'),
+ ]
+
+ operations = [
+ migrations.AddConstraint(
+ model_name='meldung',
+ constraint=models.UniqueConstraint(fields=('aufgabe', 'melder'), name='unique_aufgabe_melder'),
+ ),
+ migrations.AddConstraint(
+ model_name='stundenzuteilung',
+ constraint=models.UniqueConstraint(fields=('uhrzeit', 'zuteilung'), name='unique_uhrzeit_zuteilung'),
+ ),
+ migrations.AddConstraint(
+ model_name='zuteilung',
+ constraint=models.UniqueConstraint(fields=('aufgabe', 'ausfuehrer'), name='unique_aufgabe_ausfuehrer'),
+ ),
+ ]
diff --git a/arbeitsplan/models.py b/arbeitsplan/models.py
index 2bf427c..04eb528 100644
--- a/arbeitsplan/models.py
+++ b/arbeitsplan/models.py
@@ -150,13 +150,13 @@ def __str__(self):
def gemeldeteAnzahlAufgaben(self):
return self.user.meldung_set.exclude(
- prefMitglied=Meldung.Preferences.NEVER
+ prefMitglied=Meldung.Preferences.NO
).count()
def gemeldeteStunden(self):
"""Compute hours for which the Mitglied has entered a Meldung."""
echteMeldungen = self.user.meldung_set.exclude(
- prefMitglied=Meldung.Preferences.NEVER
+ prefMitglied=Meldung.Preferences.NO
)
return sum([m.aufgabe.stunden for m in echteMeldungen])
@@ -292,10 +292,10 @@ class Aufgabe(models.Model):
bemerkung = models.TextField(blank=True)
def numMeldungen(self):
- """How many Meldungen of status better than NEVER
+ """How many Meldungen of status better than NO
exist for this Aufgabe?
"""
- return self.meldung_set.exclude(prefMitglied=Meldung.Preferences.NEVER).count()
+ return self.meldung_set.exclude(prefMitglied=Meldung.Preferences.NO).count()
def has_Stundenplan(self):
"""Is there a Stundenplan for this Aufgabe?"""
@@ -356,25 +356,16 @@ class Meta:
class Meldung(models.Model):
class Preferences(models.IntegerChoices):
- NEVER = -1, "Nein"
- RELUCTANTLY = 0, "Wenn es sein muss"
- OK = 1, "Ok"
- GLADLY = 2, "Gerne!"
+ NO = -1, "Nein"
+ YES = 1, "Ja"
- PREFERENCES_STRING = "; ".join(
- [f"{value}: {label}" for (value, label) in Preferences.choices]
- )
PREFERENCES_BUTTONS = {
- Preferences.NEVER: "btn-outline-secondary",
- Preferences.RELUCTANTLY: "btn-outline-secondary",
- Preferences.OK: "btn-outline-secondary",
- Preferences.GLADLY: "btn-outline-secondary",
+ Preferences.NO: "btn-outline-secondary",
+ Preferences.YES: "btn-outline-secondary",
}
MODELDEFAULTS = {
- "prefMitglied": Preferences.NEVER,
- "prefVorstand": Preferences.OK,
+ "prefMitglied": Preferences.NO,
"bemerkung": "",
- "bemerkungVorstand": "",
}
erstellt = models.DateField(auto_now_add=True)
@@ -388,17 +379,11 @@ class Preferences(models.IntegerChoices):
prefMitglied = models.IntegerField(
choices=Preferences.choices,
- default=Preferences.OK,
+ default=Preferences.NO,
verbose_name="Präferenz",
- help_text="Haben Sie Vorlieben für diese Aufgabe?",
- )
- prefVorstand = models.IntegerField(
- choices=Preferences.choices,
- default=Preferences.OK,
- help_text="Trauen Sie diesem Mitglied die Aufgabe zu?",
+ help_text="Welche Präferenz hast du für diese Aufgabe?",
)
bemerkung = models.TextField(blank=True)
- bemerkungVorstand = models.TextField(blank=True)
def __str__(self):
return (
@@ -410,6 +395,14 @@ def __str__(self):
)
class Meta:
+ constraints = [
+ # Enforce uniqueness to ensure get_or_create works as intended, see
+ # https://docs.djangoproject.com/en/5.2/ref/models/querysets/#get-or-create
+ models.UniqueConstraint(
+ fields=["aufgabe", "melder"],
+ name="unique_aufgabe_melder"
+ )
+ ]
verbose_name_plural = "Meldungen"
verbose_name = "Meldung"
@@ -484,6 +477,14 @@ def stundenString(self):
return r
class Meta:
+ constraints = [
+ # Enforce uniqueness to ensure get_or_create works as intended, see
+ # https://docs.djangoproject.com/en/5.2/ref/models/querysets/#get-or-create
+ models.UniqueConstraint(
+ fields=["aufgabe", "ausfuehrer"],
+ name="unique_aufgabe_ausfuehrer"
+ )
+ ]
verbose_name_plural = "Zuteilungen"
verbose_name = "Zuteilung"
@@ -504,6 +505,14 @@ def delete(self, *args, **kwargs):
super(StundenZuteilung, self).delete(*args, **kwargs)
class Meta:
+ constraints = [
+ # Enforce uniqueness to ensure get_or_create works as intended, see
+ # https://docs.djangoproject.com/en/5.2/ref/models/querysets/#get-or-create
+ models.UniqueConstraint(
+ fields=["uhrzeit", "zuteilung"],
+ name="unique_uhrzeit_zuteilung"
+ )
+ ]
verbose_name = "Zuteilung einer Stunde"
verbose_name_plural = "Zuteilungen für einzelne Stunden"
diff --git a/arbeitsplan/tables.py b/arbeitsplan/tables.py
index b1b9684..74ea614 100644
--- a/arbeitsplan/tables.py
+++ b/arbeitsplan/tables.py
@@ -590,27 +590,32 @@ class Meta:
class MeldungListeTable(django_tables2.Table):
- """A table to only display all Meldungen of a user.
- """
+ """A table to only display all Meldungen of a user."""
- aufgabenGruppe = django_tables2.Column(accessor="aufgabe.gruppe.gruppe",
- verbose_name="Aufgabengruppe")
- aufgabeName = django_tables2.Column(accessor="aufgabe.aufgabe",
- verbose_name="Aufgabe")
- aufgabenDatum = django_tables2.Column(accessor="aufgabe.datum",
- verbose_name="Datum")
+ aufgabenGruppe = django_tables2.Column(
+ accessor="aufgabe.gruppe.gruppe", verbose_name="Aufgabengruppe"
+ )
+ aufgabeName = django_tables2.Column(
+ accessor="aufgabe.aufgabe", verbose_name="Aufgabe"
+ )
+ aufgabenDatum = django_tables2.Column(
+ accessor="aufgabe.datum", verbose_name="Datum"
+ )
class Meta:
model = models.Meldung
- fields = ("aufgabenGruppe",
- "aufgabeName",
- "aufgabenDatum",
- "prefMitglied",
- "bemerkung",
- )
- exclude = ("id", "erstellt", "veraendert",
- "prefVorstand", "bemerkungVorstand",
- )
+ fields = (
+ "aufgabenGruppe",
+ "aufgabeName",
+ "aufgabenDatum",
+ "prefMitglied",
+ "bemerkung",
+ )
+ exclude = (
+ "id",
+ "erstellt",
+ "veraendert",
+ )
class MeldungTable(RadioButtonTable):
@@ -693,7 +698,7 @@ def render_bemerkung(self, value, record, bound_row):
tmp = format_html(
"""""",
+ placeholder="Bemerkung eingeben" rows=4>{1}""",
str(record['id']),
record['bemerkung'] if record['bemerkung'] else ""
)
@@ -710,67 +715,52 @@ class Meta:
exclude = ("fehlende_zuteilungen", 'zuteilungen')
-class MeldungTableVorstand (RadioButtonTable):
+class MeldungTableVorstand(RadioButtonTable):
+ aufgabe = django_tables2.Column(accessor="aufgabe", verbose_name="Aufgabe")
- aufgabe = django_tables2.Column(accessor="aufgabe",
- verbose_name="Aufgabe")
-
- gruppe = django_tables2.Column(accessor="aufgabe.gruppe",
- verbose_name="Aufgabengruppe")
-
- datum = django_tables2.Column(accessor="aufgabe.datum",
- verbose_name="Datum")
-
- stunden = django_tables2.Column(accessor="aufgabe.stunden",
- verbose_name="Umfang (h)")
-
- prefMitglied = django_tables2.Column(accessor="prefMitglied",
- verbose_name="Vorlieben Melder",
- empty_values=(),
- )
+ gruppe = django_tables2.Column(
+ accessor="aufgabe.gruppe", verbose_name="Aufgabengruppe"
+ )
- bemerkung = django_tables2.Column(accessor="bemerkung",
- verbose_name="Bemerkung Melder",
- empty_values=(),
- )
+ datum = django_tables2.Column(accessor="aufgabe.datum", verbose_name="Datum")
- melder = KontaktColumn(accessor="melder",
- verbose_name="Melder",
- )
+ stunden = django_tables2.Column(
+ accessor="aufgabe.stunden", verbose_name="Umfang (h)"
+ )
- bemerkungVorstand = django_tables2.Column(
+ prefMitglied = django_tables2.Column(
+ accessor="prefMitglied",
+ verbose_name="Vorlieben Melder",
empty_values=(),
- verbose_name="Bemerkungen des Vorstandes")
+ )
- prefVorstand = django_tables2.Column(
- accessor="prefVorstand",
- verbose_name="Vorlieben des Vorstandes",
+ bemerkung = django_tables2.Column(
+ accessor="bemerkung",
+ verbose_name="Bemerkung Melder",
empty_values=(),
- )
-
- def render_prefVorstand(self, value, record):
- return self.render_radio(
- choices=models.Meldung.Preferences.choices,
- buttontexts=models.Meldung.PREFERENCES_BUTTONS,
- fieldname="prefVorstand",
- record=record)
-
- def render_bemerkungVorstand (self, value, record):
- tmp = format_html ('',
- str(record.id),
- record.bemerkungVorstand if record.bemerkungVorstand else ""
- )
- return tmp
+ )
+ melder = KontaktColumn(
+ accessor="melder",
+ verbose_name="Melder",
+ )
class Meta(MeldungTable.Meta):
- model = models.Meldung
- fields = ('gruppe', 'aufgabe', 'datum', 'stunden',
- # 'melder_last', 'melder_first',
- 'melder',
- 'bemerkung', 'prefMitglied',
- 'bemerkungVorstand', 'prefVorstand')
- exclude = ('melder_last', 'melder_first',)
+ model = models.Meldung
+ fields = (
+ "gruppe",
+ "aufgabe",
+ "datum",
+ "stunden",
+ "melder",
+ "bemerkung",
+ "prefMitglied",
+ )
+ exclude = (
+ "melder_last",
+ "melder_first",
+ )
+
##############################
diff --git a/arbeitsplan/templates/base_arbeitsplan.html b/arbeitsplan/templates/base_arbeitsplan.html
index be4be77..9587afd 100644
--- a/arbeitsplan/templates/base_arbeitsplan.html
+++ b/arbeitsplan/templates/base_arbeitsplan.html
@@ -49,7 +49,7 @@
{% if g.name == "Vorstand" %}
- Aufgaben aus der Vergangenheit werden nicht angezeigt! - Wenn Sie sich im Laufe des Jahres für Aufgaben ohne Datum - eintragen wollen, sprechen Sie dies bitte mit dem zuständigen - Vorstand ab. + Trag bitte ein, an welchen Aufgaben du mitarbeiten möchtest. + Wähle dazu für die entsprechende Aufgabe in der letzten Spalte „Ja“ aus. + Du kannst zusätzlich noch eine Bemerkung eingeben (z.B. wenn du die Aufgabe mit + einem Partner zusammen erledigen möchtest oder nur zu bestimmten Uhrzeiten kannst).
- Sie können die Aufgabenliste eingrenzen, in dem Sie nach - Aufgabengruppen filtern (--- entfernt den Filter). - Zusätzlich können Sie (mit dem zweiten Filter) - die Liste der Aufgaben weiter einschränken auf Aufgaben, - für die Sie bereits eine Meldung abgegeben haben - (nützlich, um Quickmeldungen zu verfeinern) - oder keine Meldung (nützlich, um sich für weitere Aufgaben zu melden). - Die dritte und vierte Filtermöglichkeit grenzt das Datum der Aufgaben ein. + Aufgaben aus der Vergangenheit und bereits vergebene Aufgaben werden nicht + angezeigt!
- Wählen Sie aus der jeweiligen Liste aus und drücken - dann auf `Filter anwenden'. + Du kannst die Aufgabenliste eingrenzen, indem du nach Aufgabengruppen filterst + (--- entfernt den Filter). + Zusätzlich kannst du mit dem zweiten Filter die Liste der Aufgaben weiter + einschränken auf Aufgaben, für die du bereits eine Meldung abgegeben hast + (nützlich, um Quickmeldungen zu verfeinern) oder keine Meldung abgegeben hast + (nützlich, um sich für weitere Aufgaben zu melden). + Die dritte und vierte Filtermöglichkeit grenzt das Datum der Aufgaben ein. + Klicke nach Auswahl der Filter auf „Filter anwenden“.
- Zeigen Sie auf den Aufgabennamen um ggf. weitere Information - über die Aufgabe zu sehen. + Bewege die Maus über den Aufgabennamen, um die Aufgabenbeschreibung anzuzeigen.
- WICHTIG: Eine Meldung hier ist nur ein WUNSCH; es ist
- nicht die automatische Zusgae, für die Aufgabe auch
- eingeteilt zu werden.
+ Beachte: Eine Meldung hier ist nur ein Wunsch, es ist nicht die automatische
+ Zusage, für die Aufgabe auch zugeteilt zu werden.
"""
@@ -877,53 +855,31 @@ def post(self, request, *args, **kwargs):
class MeldungVorstandView(isVorstandMixin, MeldungEdit):
"""Display a (filtered) list of all Meldungen from all Users,
- with all preferences.
- Allow Vorstand to update its fields and store them.
- """
+ with all preferences."""
- title = "Meldungen für Aufgaben bewerten"
- # filterform_class = forms.PersonAufgabengruppeFilterForm
- filterform_class = forms.PersonAufgGrpPraefernzFilterForm
- filtertitle = "Meldungen nach Person, Aufgabengruppen"
- " oder Präferenz filtern"
- tabletitle = "Meldungen bewerten"
+ title = "Meldungen für Aufgaben anzeigen"
+ filterform_class = forms.PersonAufgGrpPraeferenzFilterForm
+ filtertitle = "Meldungen nach Person oder Aufgabengruppe filtern"
+ tabletitle = "Meldungen anzeigen"
# tableform ?
filterconfig = [('aufgabengruppe', 'aufgabe__gruppe__gruppe'),
('first_name', 'melder__first_name__icontains'),
('last_name', 'melder__last_name__icontains'),
('praeferenz', 'prefMitglied__in'),
- ('praeferenzVorstand', 'prefVorstand__in'),
]
tableClass = MeldungTableVorstand
model = models.Meldung
- tableform = {'name': "eintragen",
- 'value': "Meldungen eintragen/ändern"}
-
intro_text = """
- Bewerten Sie die Meldungen der Mitglieder nach Eignung für eine Aufgabe.
-
-
+
+ Unten werden die Meldungen der Mitglieder angezeigt. + Du kannst die Liste filtern nach Name des Mitglieds und nach Aufgabengruppe. +
+ Früher konnten hier Meldungen bewertet werden. Diese Funktion entfällt, bitte + nimm direkt Zuteilungen vor! """ - ## todo_text = """ - ##
- Filtern Sie nach Mitgliedsnamen oder Aufgabengruppe. Zusätzlich können Sie nach Auslastung und Meldung der Mitglieder filtern: nur solche mit Meldungen für diese Aufgabengruppe (genauer: für irgendeine Aufgabe in der gewählten Gruppe), Mitglieder mit noch freier Arbeitskapazität, oder ausgelastete Mitglieder. + In der Tabelle werden Boxen angezeigt, wenn sich ein Mitglied für eine Aufgabe + gemeldet hat. Wähle die Box an (ankreuzen), wenn du ein Mitglied für eine Aufgabe + zuteilen möchtest. Entferne einen Haken, um die Zuteilung zu löschen.
- Hinweise: -