207 lines
6.9 KiB
Python
207 lines
6.9 KiB
Python
from django import forms
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from .models import Comment, Ticket
|
|
|
|
|
|
class CommentForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Comment
|
|
fields = ["text"]
|
|
widgets = {
|
|
"text": forms.Textarea(
|
|
attrs={"rows": 3, "placeholder": "Kommentar schreiben..."}
|
|
),
|
|
}
|
|
|
|
|
|
class TicketForm(forms.ModelForm):
|
|
# Zentrale Definition der Status-Übergänge
|
|
STATUS_TRANSITIONS = {
|
|
"tutor": {
|
|
"new": ["in_progress"],
|
|
"in_progress": ["resolved", "new"],
|
|
"resolved": ["closed"],
|
|
"closed": [],
|
|
},
|
|
"creator": {
|
|
"new": [],
|
|
"in_progress": [],
|
|
"resolved": ["closed", "new"],
|
|
"closed": [],
|
|
},
|
|
"superuser": {
|
|
# Superuser können alle Übergänge machen
|
|
"new": ["in_progress", "resolved", "closed"],
|
|
"in_progress": ["new", "resolved", "closed"],
|
|
"resolved": ["new", "in_progress", "closed"],
|
|
"closed": ["new", "in_progress", "resolved"],
|
|
},
|
|
}
|
|
|
|
# Zentrale Definition welche Felder wann required sind
|
|
REQUIRED_FIELDS_BY_STATUS = {
|
|
"resolved": ["answer"], # Answer required when resolving
|
|
}
|
|
|
|
class Meta:
|
|
model = Ticket
|
|
fields = [
|
|
"title",
|
|
"description",
|
|
"status",
|
|
"mistake",
|
|
"course",
|
|
"answer",
|
|
"material",
|
|
]
|
|
widgets = {
|
|
"answer": forms.Textarea(
|
|
attrs={
|
|
"rows": 4,
|
|
"placeholder": "Beschreibe die Lösung des Problems...",
|
|
}
|
|
)
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.user = kwargs.pop("user", None)
|
|
self.ticket = kwargs.pop("ticket", None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
if self.ticket and self.user:
|
|
self._configure_form_based_on_permissions()
|
|
|
|
def _configure_form_based_on_permissions(self):
|
|
"""Konfiguriert das Formular basierend auf Benutzerrechten"""
|
|
is_creator = self.user == self.ticket.created_by
|
|
is_tutor = self.user == self.ticket.assigned_to
|
|
is_superuser = self.user.is_superuser
|
|
|
|
# Status-Choices einschränken
|
|
self._limit_status_choices(is_tutor, is_creator, is_superuser)
|
|
|
|
# Feld-Berechtigungen setzen
|
|
self._set_field_permissions(is_tutor, is_creator, is_superuser)
|
|
|
|
def _set_field_permissions(self, is_tutor, is_creator, is_superuser):
|
|
"""Setzt welche Felder bearbeitet werden dürfen"""
|
|
# Superuser können alles bearbeiten
|
|
if is_superuser:
|
|
return
|
|
|
|
if self.ticket.status == "resolved" and is_creator:
|
|
for field_name in self.fields:
|
|
if field_name == "answer":
|
|
self.fields[field_name].disabled = True
|
|
elif is_tutor:
|
|
# Tutor darf ändern:
|
|
readonly_fields = ["title", "description", "material"]
|
|
for field_name in readonly_fields:
|
|
if field_name in self.fields:
|
|
self.fields[field_name].disabled = True
|
|
elif is_creator and self.ticket.status != "resolved":
|
|
for field_name in self.fields:
|
|
self.fields[field_name].disabled = True
|
|
|
|
def _limit_status_choices(self, is_tutor, is_creator, is_superuser):
|
|
"""Beschränkt verfügbare Status-Optionen basierend auf der zentralen Logik"""
|
|
current_status = self.ticket.status
|
|
|
|
# Superuser bekommen alle Status-Optionen
|
|
if is_superuser:
|
|
role = "superuser"
|
|
elif is_tutor:
|
|
role = "tutor"
|
|
elif is_creator:
|
|
role = "creator"
|
|
else:
|
|
role = None
|
|
|
|
if role:
|
|
allowed_statuses = self._get_allowed_transitions(current_status, role)
|
|
else:
|
|
# Andere User sehen nur aktuellen Status
|
|
allowed_statuses = [current_status]
|
|
|
|
# Status-Choices filtern
|
|
all_choices = list(self.fields["status"].choices)
|
|
self.fields["status"].choices = [
|
|
choice for choice in all_choices if choice[0] in allowed_statuses
|
|
]
|
|
|
|
def _get_allowed_transitions(self, from_status, role):
|
|
"""Gibt erlaubte Status-Übergänge zurück (zentrale Logik)"""
|
|
transitions = self.STATUS_TRANSITIONS.get(role, {}).get(from_status, [])
|
|
return [from_status] + transitions
|
|
|
|
def _is_transition_allowed(self, from_status, to_status, role):
|
|
"""Prüft ob ein Status-Übergang erlaubt ist"""
|
|
if from_status == to_status:
|
|
return True
|
|
return to_status in self.STATUS_TRANSITIONS.get(role, {}).get(from_status, [])
|
|
|
|
def _get_required_fields_for_status(self, status):
|
|
"""Gibt zurück, welche Felder für einen Status required sind"""
|
|
return self.REQUIRED_FIELDS_BY_STATUS.get(status, [])
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
status = cleaned_data.get("status")
|
|
|
|
if not self.ticket or not status:
|
|
return cleaned_data
|
|
|
|
# Prüfe Status-Übergang
|
|
old_status = self.ticket.status
|
|
is_tutor = self.user == self.ticket.assigned_to
|
|
is_creator = self.user == self.ticket.created_by
|
|
is_superuser = self.user.is_superuser
|
|
|
|
# Superuser dürfen alle Übergänge
|
|
if is_superuser:
|
|
role = "superuser"
|
|
elif is_tutor:
|
|
role = "tutor"
|
|
elif is_creator:
|
|
role = "creator"
|
|
else:
|
|
role = None
|
|
|
|
if role and not self._is_transition_allowed(old_status, status, role):
|
|
raise ValidationError(
|
|
{
|
|
"status": f'Übergang von "{self.ticket.get_status_display()}" zu "{dict(self.fields["status"].choices)[status]}" ist nicht erlaubt.'
|
|
}
|
|
)
|
|
|
|
# Prüfe required fields für neuen Status
|
|
required_fields = self._get_required_fields_for_status(status)
|
|
for field_name in required_fields:
|
|
if not cleaned_data.get(field_name):
|
|
field_label = self.fields[field_name].label
|
|
raise ValidationError(
|
|
{
|
|
field_name: f'{field_label} ist erforderlich, wenn der Status auf "{dict(self.fields["status"].choices)[status]}" gesetzt wird.'
|
|
}
|
|
)
|
|
|
|
return cleaned_data
|
|
|
|
def save(self, commit=True):
|
|
ticket = super().save(commit=False)
|
|
|
|
# Automatische Tutor-Zuweisung
|
|
if ticket.course and ticket.course.tutor:
|
|
ticket.assigned_to = ticket.course.tutor
|
|
|
|
# Setze answered_at wenn eine Antwort gegeben wird
|
|
if ticket.answer and not ticket.answered_at:
|
|
from django.utils import timezone
|
|
|
|
ticket.answered_at = timezone.now()
|
|
|
|
if commit:
|
|
ticket.save()
|
|
return ticket
|