Commit 7e5acdab authored by Felix Schlösser / TinTin's avatar Felix Schlösser / TinTin
Browse files

added more generic views for drafts and financial requests

parent 1b84eb92
Pipeline #148065 failed with stages
in 13 seconds
......@@ -10,18 +10,13 @@ from .models import Motion, FinancialRequest, CostItem
class MotionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["_status"].disabled = True
self.fields["title"].help_text = _(
"Kann nach dem Abschicken nicht mehr verändert werden."
)
self.fields["text"].label = _("Beschlussvorschlag")
class Meta:
model = Motion
exclude = ["requestor",]
exclude = ('requestor', '_status')
widgets = {
"requestor": forms.HiddenInput(),
"title": forms.TextInput(
attrs={
"minlength": 10,
......@@ -65,75 +60,10 @@ eine gute alternative für Personen die kein Bier oder alkohol trinken mögen.""
"urgency_justification": forms.Textarea(
attrs={"rows": 3, "placeholder": _("Meine sehr gute Begründung.")}
),
"_status": forms.HiddenInput(),
}
class MotionUpdateForm(MotionForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["_status"].disabled = True
self.fields["title"].help_text = _(
"Kann nach dem Abschicken nicht mehr verändert werden."
)
self.fields["title"].help_text = _(
"Kann nach dem Abschicken nicht mehr verändert werden."
)
class Meta(MotionForm.Meta):
fields = [
"_status",
"group",
"preferred_language",
"tags",
"title",
"text",
"reasoning",
"attachment",
"is_urgent",
"urgency_justification",
]
def clean(self):
inital_status = self.initial["_status"]
inital_status = self.initial["title"]
inital_status = self.initial["requestor"]
logging.error(inital_status)
submitted_status = self.cleaned_data["_status"]
if submitted_status != inital_status:
raise forms.ValidationError(
_(
f"Statusveränderungen im Formular sind nicht vorgesehen. Der Status wurde von '{inital.lower()}' zu '{submitted.lower()}' verändert."
)
)
return super().clean()
class MotionStatusUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["_status"].label_tag = None
self.fields["_status"].disabled = True
class Meta:
model = Motion
fields = [
"_status",
]
widgets = {
"_status": forms.HiddenInput(),
}
def clean(self):
inital = self.initial["_status"]
submitted = self.cleaned_data["_status"]
if submitted != inital:
raise forms.ValidationError(
_(
f"Statusveränderungen im Formular sind nicht vorgesehen. Der Status wurde von '{inital.lower()}' zu '{submitted.lower()}' verändert."
)
)
return super().clean()
logging.error(self.errors)
class FinancialRequestForm(MotionForm):
......@@ -142,16 +72,13 @@ class FinancialRequestForm(MotionForm):
self.fields["title"].label = _(
"Name der Veranstaltung, des Projekts, der Sache"
)
self.fields["_status"].disabled = True
self.fields["text"].label = _("Beschreibung")
self.fields["reasoning"].required = True
self.fields["amount_euro"].label = _("Beantragtes Budget")
class Meta:
class Meta(MotionForm.Meta):
model = FinancialRequest
fields = "__all__"
widgets = {
"requestor": forms.HiddenInput(),
"title": forms.TextInput(
attrs={
"minlength": 10,
......@@ -208,9 +135,85 @@ die Vielfalt der Mate-basierten Kalt- und Warmgetränke aus Südamerika und Euro
"urgency_justification": forms.Textarea(
attrs={"rows": 3, "placeholder": _("Meine sehr gute Begründung.")}
),
}
class MotionUpdateForm(MotionForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.instance.is_draft:
self.fields["title"].disabled = True
class Meta(MotionForm.Meta):
pass
def clean(self):
inital_title = self.initial["title"]
submitted_title = self.cleaned_data["title"]
if not self.is_draft:
if submitted_title != inital_title:
raise forms.ValidationError(
_(
f"Titelveränderungen bei bereits abgeschickten Anträgen sind nicht vorgesehen."
)
)
return super().clean()
class FinancialRequestUpdateForm(FinancialRequestForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.instance.is_draft:
self.fields["title"].disabled = True
class Meta(FinancialRequestForm.Meta):
pass
def clean(self):
inital_title = self.initial["title"]
submitted_title = self.cleaned_data["title"]
if not self.is_draft:
if submitted_title != inital_title:
raise forms.ValidationError(
_(
f"Titelveränderungen bei bereits abgeschickten Anträgen sind nicht vorgesehen."
)
)
return super().clean()
class MotionStatusUpdateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["_status"].label_tag = None
self.fields["_status"].disabled = True
class Meta:
model = Motion
fields = [
"_status",
]
widgets = {
"_status": forms.HiddenInput(),
}
def clean(self):
inital = self.initial["_status"]
submitted = self.cleaned_data["_status"]
if submitted != inital:
raise forms.ValidationError(
_(
f"Statusveränderungen im Formular sind nicht vorgesehen. Der Status wurde von '{inital.lower()}' zu '{submitted.lower()}' verändert."
)
)
return super().clean()
class CostItemForm(forms.ModelForm):
class Meta:
......
......@@ -10,3 +10,6 @@ class DraftMotionManager(InheritanceManager):
class NoDraftsMotionManager(InheritanceManager):
def get_queryset(self):
return super().get_queryset().exclude(_status='DRAFT')
def including_drafts(self):
return super().get_queryset()
......@@ -245,7 +245,7 @@ class Motion(MotionStatusMixin, TimeStampedModel):
"Der entsprechende Tagsordnungspunkt wird nach Möglichkeit in dieser Sprache behandeln werden."
),
)
title = models.CharField(max_length=256, verbose_name=_("Titel"))
title = models.CharField(max_length=256, verbose_name=_("Titel"), help_text=_("Kann nach dem Abschicken nicht mehr verändert werden."))
text = models.TextField(max_length=4096, verbose_name=_("Text"))
reasoning = models.TextField(
max_length=4096,
......@@ -395,7 +395,7 @@ class Motion(MotionStatusMixin, TimeStampedModel):
@property
def is_accepted(self):
if self._get_raw_status() == "DECIDED":
if self._get_raw_status() == "ACCEPTED":
return True
else:
return False
......@@ -409,7 +409,13 @@ class Motion(MotionStatusMixin, TimeStampedModel):
@property
def is_draft(self):
if self._get_raw_status() == "ACCEPTED":
if self._get_raw_status() == "DRAFT":
return True
else:
return False
def is_financial_request(self):
if self.__class__.__name__ == "FinancialRequest":
return True
else:
return False
......@@ -430,13 +436,6 @@ class Motion(MotionStatusMixin, TimeStampedModel):
raise ValidationError("Nur aktive Anträge können Zurückgezogen werden.")
def set_status(self, new_status):
if new_status.upper() == 'WITHDRAWN':
self.withdraw()
else:
logging.error(f"Unkown Status: {new_status}")
def get_meetings(self):
items = self._get_agenda_items()
......@@ -470,8 +469,8 @@ class Motion(MotionStatusMixin, TimeStampedModel):
try:
last_version = self.versions.most_recent()
if last_version._status == "DRAFT" and (
self._status != "OPEN" and self._status != "DRAFT"
if last_version.is_draft and (
not self.is_open and not self.is_draft
):
raise ValidationError(
{
......@@ -480,7 +479,7 @@ class Motion(MotionStatusMixin, TimeStampedModel):
)
}
)
elif last_version._status == "OPEN" and self._status == "DRAFT":
elif last_version.is_open and self.is_draft:
raise ValidationError(
{
"_status": _(
......@@ -488,34 +487,33 @@ class Motion(MotionStatusMixin, TimeStampedModel):
)
}
)
elif last_version._status == "CHANGED" and (
self._status == "DRAFT" or self._status == "OPEN"
elif last_version.is_changed and (
self.is_draft or self.is_open
):
raise ValidationError(
{
"_status": _(
"Veränderte Anträge können nicht mehr in offene verwandelt werden."
"Veränderte Anträge können nicht mehr in offene oder Entwürfe verwandelt werden."
)
}
)
elif last_version._status == "ADJURNED" and (
self._status == "DRAFT" or self._status == "OPEN"
):
elif last_version.is_adjourned and (
self.is_draft or self.is_open):
raise ValidationError(
{
"_status": _(
"Vertagte Anträge können nur zurückgezogen oder entschieden werden."
"Vertagte Anträge können nur verändert, zurückgezogen oder entschieden werden."
)
}
)
elif last_version._status == "WITHDRAWN":
elif last_version.is_withdrawn:
raise ValidationError(
_(
"Zurückgezogene Anträge sind eingefroren und können nicht mehr verädndert werden"
),
code="editing-forzen-motion",
)
elif last_version._status == "DECIDED":
elif last_version.is_decided:
raise ValidationError(
_(
"Beschlossene Anträge sind eingefroren und können nicht mehr verädndert werden"
......@@ -533,14 +531,16 @@ class Motion(MotionStatusMixin, TimeStampedModel):
try:
recent_version = self.versions.as_of(one_hour_ago)
most_recent_version = self.versions.most_recent()
if (self._status == "OPEN" and recent_version._status == "OPEN") or (
self._status == "ADJURNED" and most_recent_version._status == "ADJURNED"
if (self.is_open and recent_version.is_open) or (
self.is_adjourned and most_recent_version.is_adjourned
):
logging.info("Motion was changed, setting its status to CHANGED.")
self._status = "CHANGED"
super().save(*args, **kwargs)
logging.info("Motion was changed after Submission.")
if motion.get_meetings():
logging.info("Motion was alreay on a meeting agenda, setting its status to CHANGED.")
self._status = "CHANGED"
super().save(*args, **kwargs)
else:
logging.debug("Keep Status the same.")
logging.debug("Keeping status the same.")
super().save(*args, **kwargs)
except Exception as e:
......@@ -549,7 +549,10 @@ class Motion(MotionStatusMixin, TimeStampedModel):
return
def get_absolute_url(self):
return reverse("motion-detail", args=[str(self.id)])
if self.is_draft:
return reverse("draft-detail", args=[str(self.id)])
else:
return reverse("motion-detail", args=[str(self.id)])
class FinancialRequest(Motion):
......@@ -590,7 +593,9 @@ class FinancialRequest(Motion):
verbose_name_plural = _("Finanzanträge")
def __str__(self):
return f"{ self.title } [{ self.amount_euro }]"
abreviation = _("FA")
return f"{abreviation}: { self.title }"
def get_budgeded_costs_sum(self):
result = self.cost_breakdown.all().aggregate(models.Sum("budgeted_costs_euro"))
......
......@@ -34,7 +34,7 @@ def status_tag(status, class_list=""):
color_class = "is-danger"
else:
color_class = "is-black"
status_str = _("Unbekannt")
status_str = _("Ungültig")
logging.error(f"Unknown Status: {status}")
return mark_safe(
......
......@@ -17,22 +17,21 @@ urlpatterns = [] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += i18n_patterns(
path(settings.ADMIN_URL, admin.site.urls),
path("i18n/", include("django.conf.urls.i18n")),
path("", MotionIndexView.as_view(), name="index"),
path("", DashboardView.as_view(), name="dashboard"),
path(_("entwürfe"), DraftListView.as_view(), name="my-drafts"),
path(_("entwurf/<str:pk>"), DraftDetailView.as_view(), name="draft-detail"),
path(_("entwurf/<str:pk>/löschen"), DraftDeleteView.as_view(), name="draft-delete"),
path(_("meine-anträge"), MotionListView.as_view(), name="my-motions"),
path(_("meine-entwürfe"), MotionDraftListView.as_view(), name="my-drafts"),
path(_("antrag/neu"), MotionCreateView.as_view(), name="submit-motion"),
path(_("antrag/<str:pk>"), MotionDetailView.as_view(), name="motion-detail"),
path(
_("antrag/<str:pk>/überarbeiten"),
MotionEditView.as_view(is_draft=True), name="motion-edit-draft",
),
path(
_("antrag/<str:pk>/ändern"),
MotionEditView.as_view(), name="motion-change",
MotionUpdateView.as_view(),
name="motion-change",
),
path(
_("antrag/<str:pk>/zurückziehen"),
MotionUpdateStatusView.as_view(new_status="withdrawn"),
MotionUpdateStatusView.as_view(),
name="motion-withdraw",
),
path(
......@@ -44,6 +43,11 @@ urlpatterns += i18n_patterns(
_("finanzantrag/<str:pk>"),
FinancialRequestDetailView.as_view(),
name="financial-request-detail",
),
path(
_("finanzantrag/<str:pk>/ändern"),
FinancialRequestUpdateView.as_view(),
name="financial-request-change",
),
path(_("archiv/"), MotionArchiveIndexView.as_view(), name="archive"),
path(_("archiv/int:year>/"), MotionYearArchiveView.as_view(), name="archive-year"),
......
from django.views.generic import (
ListView,
TemplateView,
CreateView,
DetailView,
UpdateView,
)
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import generic
from django.views.generic.dates import (
ArchiveIndexView,
YearArchiveView,
MonthArchiveView,
WeekArchiveView,
)
from django.utils.translation import get_language
from django.contrib.messages.views import SuccessMessageMixin
from django.views.defaults import bad_request
from committee_transparency.utils.mixins import FormsetMixin
from django.utils.translation import get_language, gettext_lazy as _
from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.urls import reverse_lazy
from .models import Motion, FinancialRequest, Language
from .forms import (
MotionForm,
MotionUpdateForm,
MotionStatusUpdateForm,
FinancialRequestForm,
CostItemFormset,
)
from committee_transparency.utils.mixins import FormsetMixin
from .models import Motion, FinancialRequest, Language
from .forms import *
import logging
# Create your views here.
class MotionListView(LoginRequiredMixin, ListView):
class DashboardView(generic.TemplateView):
template_name = "motions/dashboard.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["latest_motions"] = Motion.objects.all()[:5]
return context
class MotionListView(LoginRequiredMixin, generic.ListView):
model = Motion
paginate_by = 30 # if pagination is desired
def get_queryset(self):
return Motion.objects.filter(requestor=self.request.user.pk).order_by(
"-modified"
return (
Motion.objects.filter(requestor=self.request.user)
.select_subclasses()
.order_by("-modified")
)
class MotionDraftListView(LoginRequiredMixin, ListView):
model = Motion
paginate_by = 30 # if pagination is desired
class DraftListView(MotionListView):
template_name = "motions/draft_list.html"
def get_queryset(self):
return Motion.drafts.filter(requestor=self.request.user.pk).order_by(
"-modified"
return (
Motion.drafts.filter(requestor=self.request.user)
.select_subclasses()
.order_by("-modified")
)
class MotionIndexView(TemplateView):
template_name = "motions/motion_index.html"
class MotionDetailView(generic.DetailView):
model = Motion
def get(self, request, *args, **kwargs):
# If motion is a financial request or draft, redirect to the appropriate view
requested_pk = self.kwargs.get(self.pk_url_kwarg)
financial_request = FinancialRequest.objects.filter(pk=requested_pk)
draft = Motion.drafts.filter(pk=requested_pk, requestor=self.request.user)
if financial_request.exists():
logging.info("Is a financial request, redirecting.")
obj = financial_request.get()
return HttpResponseRedirect(obj.get_absolute_url())
elif draft.exists():
logging.info("Is a draft, redirecting.")
obj = draft.get()
return HttpResponseRedirect(obj.get_absolute_url())
else:
return super().get(request, *args, **kwargs)
class DraftDetailView(LoginRequiredMixin, generic.DetailView):
queryset = Motion.drafts.all().select_subclasses()
context_object_name = "motion"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["latest_motions"] = Motion.objects.all()[:5]
return context
class FinancialRequestDetailView(generic.DetailView):
model = FinancialRequest
context_object_name = "motion"
def get(self, request, *args, **kwargs):
# If motion is a financial request or draft, redirect to the appropriate view
requested_pk = self.kwargs.get(self.pk_url_kwarg)
draft = Motion.drafts.filter(pk=requested_pk, requestor=self.request.user)
if draft.exists():
logging.info("Is a draft, redirecting.")
obj = draft.get()
return HttpResponseRedirect(obj.get_absolute_url())
else:
return super().get(request, *args, **kwargs)
class MotionCreateView(LoginRequiredMixin, CreateView):
class MotionCreateView(LoginRequiredMixin, SuccessMessageMixin, generic.CreateView):
model = Motion
template_name_suffix = "_create_form"
form_class = MotionForm
success_message = _("Antrag erstellt.")
def get_initial(self):
current_lang = get_language()
lang_code = current_lang.split("-")[0]
language = Language.objects.filter(iso_code=lang_code).get()
try:
language = Language.objects.filter(iso_code=lang_code).get()
except ObjectDoesNotExist:
logging.error(
"Could not parse lang_code from URL, setting preferred_language to default lang."
)
language = Language.objects.first().get()
return {"preferred_language": language}
def form_valid(self, form):
"""If the form is valid, save the associated model."""
motion = form.save(commit=False)
current_user = self.request.user.pk
current_user = self.request.user
motion.requestor = current_user
if "save_draft" in self.request.POST.keys():
......@@ -101,41 +143,45 @@ class FinancialRequestCreateView(FormsetMixin, MotionCreateView):
formset_class = CostItemFormset
class MotionDetailView(SuccessMessageMixin, DetailView):
model = Motion
class MotionUpdateView(SuccessMessageMixin, generic.UpdateView):
form_class = MotionUpdateForm
template_name_suffix = "_update_form"
success_message = _("Antrag aktualisiert.")
def get(self, request, *args, **kwargs):
# If motion is a financial request, redirect to the appropriate view
requested_pk = self.kwargs.get(self.pk_url_kwarg)
financial_request = FinancialRequest.objects.filter(pk=requested_pk)
if financial_request.exists():
logging.info("Is a financial request, redirecting.")
obj = financial_request.get()
return HttpResponseRedirect(obj.get_absolute_url())
else:
return super().get(request, *args, **kwargs)
def get_queryset(self):