diff --git a/ticketsystem/forms.py b/ticketsystem/forms.py index 7a90fec..5c59851 100644 --- a/ticketsystem/forms.py +++ b/ticketsystem/forms.py @@ -17,45 +17,55 @@ class CommentForm(forms.ModelForm): class TicketForm(forms.ModelForm): # Zentrale Definition der Status-Übergänge STATUS_TRANSITIONS = { - 'tutor': { - 'new': ['in_progress'], - 'in_progress': ['resolved', 'new'], - 'resolved': ['closed'], - 'closed': [], + "tutor": { + "new": ["in_progress"], + "in_progress": ["resolved", "new"], + "resolved": ["closed"], + "closed": [], }, - 'creator': { - 'new': [], - 'in_progress': [], - 'resolved': ['closed', 'new'], - 'closed': [], + "creator": { + "new": [], + "in_progress": [], + "resolved": ["closed", "new"], + "closed": [], }, - 'superuser': { + "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'], - } + "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 + "resolved": ["answer"], # Answer required when resolving } class Meta: model = Ticket - fields = ["title", "description", "status", "mistake", "course", "answer", "material"] + fields = [ + "title", + "description", + "status", + "mistake", + "course", + "answer", + "material", + ] widgets = { - 'answer': forms.Textarea(attrs={ - 'rows': 4, - 'placeholder': 'Beschreibe die Lösung des Problems...' - }) + "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) + self.user = kwargs.pop("user", None) + self.ticket = kwargs.pop("ticket", None) super().__init__(*args, **kwargs) if self.ticket and self.user: @@ -79,17 +89,17 @@ class TicketForm(forms.ModelForm): if is_superuser: return - if self.ticket.status == 'resolved' and is_creator: + 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'] + 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': + elif is_creator and self.ticket.status != "resolved": for field_name in self.fields: self.fields[field_name].disabled = True @@ -99,11 +109,11 @@ class TicketForm(forms.ModelForm): # Superuser bekommen alle Status-Optionen if is_superuser: - role = 'superuser' + role = "superuser" elif is_tutor: - role = 'tutor' + role = "tutor" elif is_creator: - role = 'creator' + role = "creator" else: role = None @@ -114,10 +124,9 @@ class TicketForm(forms.ModelForm): 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 + 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): @@ -137,7 +146,7 @@ class TicketForm(forms.ModelForm): def clean(self): cleaned_data = super().clean() - status = cleaned_data.get('status') + status = cleaned_data.get("status") if not self.ticket or not status: return cleaned_data @@ -150,27 +159,31 @@ class TicketForm(forms.ModelForm): # Superuser dürfen alle Übergänge if is_superuser: - role = 'superuser' + role = "superuser" elif is_tutor: - role = 'tutor' + role = "tutor" elif is_creator: - role = '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.' - }) + 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.' - }) + raise ValidationError( + { + field_name: f'{field_label} ist erforderlich, wenn der Status auf "{dict(self.fields["status"].choices)[status]}" gesetzt wird.' + } + ) return cleaned_data @@ -184,8 +197,9 @@ class TicketForm(forms.ModelForm): # 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 \ No newline at end of file + return ticket diff --git a/ticketsystem/models.py b/ticketsystem/models.py index 21e7329..eacbdb3 100644 --- a/ticketsystem/models.py +++ b/ticketsystem/models.py @@ -4,8 +4,11 @@ from django.contrib.auth.models import User class Course(models.Model): """Kurs-Model für Backend-Verwaltung""" + name = models.CharField(max_length=200, verbose_name="Kurs-Name") - code = models.CharField(max_length=50, unique=True, verbose_name="Kurs-Code") # z.B. "PROG-101" + code = models.CharField( + max_length=50, unique=True, verbose_name="Kurs-Code" + ) # z.B. "PROG-101" description = models.TextField(blank=True, verbose_name="Beschreibung") tutor = models.ForeignKey( @@ -14,14 +17,14 @@ class Course(models.Model): null=True, blank=True, verbose_name="Tutor", - related_name="courses_as_tutor" + related_name="courses_as_tutor", ) is_active = models.BooleanField(default=True, verbose_name="Aktiv") created_at = models.DateTimeField(auto_now_add=True) class Meta: - ordering = ['name'] + ordering = ["name"] verbose_name = "Kurs" verbose_name_plural = "Kurse" @@ -64,37 +67,40 @@ class Ticket(models.Model): description = models.TextField() status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="new") mistake = models.CharField(max_length=20, choices=MISTAKE_CHOICES, default="medium") - material = models.CharField(max_length=20, choices=MATERIAL_CHOICES, default="script") + material = models.CharField( + max_length=20, choices=MATERIAL_CHOICES, default="script" + ) answer = models.TextField( blank=True, null=True, verbose_name="Antwort/Lösung", - help_text="Beschreibung der Lösung (erforderlich bei Status 'Gelöst')" + help_text="Beschreibung der Lösung (erforderlich bei Status 'Gelöst')", ) answered_at = models.DateTimeField( - blank=True, - null=True, - verbose_name="Beantwortet am" + blank=True, null=True, verbose_name="Beantwortet am" ) course = models.ForeignKey( - Course, - on_delete=models.CASCADE, - verbose_name="Kurs", - related_name="tickets" + Course, on_delete=models.CASCADE, verbose_name="Kurs", related_name="tickets" ) created_by = models.ForeignKey( User, related_name="tickets_created", on_delete=models.CASCADE ) assigned_to = models.ForeignKey( - User, related_name="tickets_assigned", null=True, blank=True, on_delete=models.CASCADE, + User, + related_name="tickets_assigned", + null=True, + blank=True, + on_delete=models.CASCADE, ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): - return f"[{self.get_mistake_display()}] {self.title} ({self.get_status_display()})" + return ( + f"[{self.get_mistake_display()}] {self.title} ({self.get_status_display()})" + ) class Comment(models.Model): @@ -126,15 +132,16 @@ class TicketHistory(models.Model): class FAQ(models.Model): """Einfaches FAQ Model""" + question = models.CharField(max_length=300, verbose_name="Frage") answer = models.TextField(verbose_name="Antwort") order = models.IntegerField(default=0, verbose_name="Reihenfolge") is_active = models.BooleanField(default=True, verbose_name="Aktiv") class Meta: - ordering = ['order', 'question'] + ordering = ["order", "question"] verbose_name = "FAQ" verbose_name_plural = "FAQs" def __str__(self): - return self.question \ No newline at end of file + return self.question diff --git a/ticketsystem/urls.py b/ticketsystem/urls.py index 273f2d2..06a1fe0 100644 --- a/ticketsystem/urls.py +++ b/ticketsystem/urls.py @@ -8,7 +8,7 @@ from .views import ( AssignedTicketListView, TicketDetailUpdateView, faq_list, - faq_pdf_download + faq_pdf_download, ) urlpatterns = [ @@ -22,6 +22,6 @@ urlpatterns = [ path("new/", TicketCreateView.as_view(), name="create"), path("/modify/", TicketUpdateView.as_view(), name="modify"), path("meine-tickets/", AssignedTicketListView.as_view(), name="assigned-tickets"), - path('faq/', faq_list, name='faq-list'), - path('faq/download/', faq_pdf_download, name='faq-pdf-download'), + path("faq/", faq_list, name="faq-list"), + path("faq/download/", faq_pdf_download, name="faq-pdf-download"), ]