diff --git a/zapisy/apps/schedule/assets/reservation.js b/zapisy/apps/schedule/assets/reservation.js index 1488df54fa..72a789d23d 100644 --- a/zapisy/apps/schedule/assets/reservation.js +++ b/zapisy/apps/schedule/assets/reservation.js @@ -31,9 +31,15 @@ const listOfEmpty = []; function setFormDisplay() { if ($("#form-type").val() === "2") { $("#form-course").addClass("d-none"); + $("#form-thesis").addClass("d-none"); $(".form-event").removeClass("d-none"); + } else if ($("#form-type").val() === "5") { + $("#form-course").addClass("d-none"); + $("#form-thesis").removeClass("d-none"); + $(".form-event").addClass("d-none"); } else { $("#form-course").removeClass("d-none"); + $("#form-thesis").addClass("d-none"); $(".form-event").addClass("d-none"); } } diff --git a/zapisy/apps/schedule/forms.py b/zapisy/apps/schedule/forms.py index 468421ff15..d125cb6ad3 100644 --- a/zapisy/apps/schedule/forms.py +++ b/zapisy/apps/schedule/forms.py @@ -13,6 +13,8 @@ from apps.schedule.models.event import Event from apps.schedule.models.message import EventMessage, EventModerationMessage from apps.schedule.models.term import Term +from apps.theses.enums import ThesisStatus +from apps.theses.models import Thesis class TermForm(forms.ModelForm): @@ -104,6 +106,15 @@ class Meta: model = Event exclude = ('status', 'author', 'created', 'edited', 'group', 'interested') + def clean(self): + cleaned_data = super().clean() + if cleaned_data['type'] != Event.TYPE_DEFENCE: + cleaned_data['thesis'] = None + if cleaned_data['type'] not in (Event.TYPE_EXAM, Event.TYPE_TEST): + cleaned_data['course'] = None + if cleaned_data['type'] != Event.TYPE_GENERIC: + cleaned_data['title'] = None + title = forms.CharField(label="Nazwa", required=False) description = forms.CharField( label="Opis", @@ -122,6 +133,9 @@ class Meta: course = forms.ModelChoiceField(queryset=CourseInstance.objects.none(), label="Przedmiot", required=False) + thesis = forms.ModelChoiceField(queryset=Thesis.objects.none(), + label="Praca dyplomowa", + required=False) def __init__(self, user, *args, **kwargs): super(EventForm, self).__init__(*args, **kwargs) @@ -150,9 +164,19 @@ def __init__(self, user, *args, **kwargs): self.fields['course'].queryset = queryset + if not user.has_perm('schedule.manage_events'): + thesis_queryset = Thesis.objects.filter( + advisor=user.employee, + status=ThesisStatus.IN_PROGRESS) + else: + thesis_queryset = Thesis.objects.filter(status=ThesisStatus.IN_PROGRESS) + + self.fields['thesis'].queryset = thesis_queryset.order_by('title') + self.helper.layout = Layout( 'type', Div('course', css_id="form-course"), + Div('thesis', css_id="form-thesis", css_class="d-none"), Div(CustomVisibleCheckbox('visible'), css_class="d-none form-event"), Div('title', css_class='d-none form-event'), 'description', diff --git a/zapisy/apps/schedule/migrations/0011_auto_20240510_0122.py b/zapisy/apps/schedule/migrations/0011_auto_20240510_0122.py new file mode 100644 index 0000000000..66d755630b --- /dev/null +++ b/zapisy/apps/schedule/migrations/0011_auto_20240510_0122.py @@ -0,0 +1,35 @@ +# Generated by Django 3.1.14 on 2024-05-10 01:22 + +from django.db import migrations, models +import django.db.models.deletion + +def update_entry_names(apps, schema_editor): + Event = apps.get_model('schedule', 'Event') + for event in Event.objects.all(): + if event.type == '5': + event.title = (event.get_type_display() + ": " + event.thesis.title)[:255] + elif event.type in ('0', '1') and event.course: + event.title = (event.get_type_display() + ": " + event.course.name)[:255] + event.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('theses', '0006_thesis_max_number_of_students'), + ('schedule', '0010_auto_20220801_2026'), + ('courses', '0036_auto_20211022_1641'), + ] + + operations = [ + migrations.AddField( + model_name='event', + name='thesis', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='theses.thesis'), + ), + migrations.AlterField( + model_name='event', + name='type', + field=models.CharField(choices=[('0', 'Egzamin'), ('1', 'Kolokwium'), ('5', 'Obrona pracy dyplomowej'), ('2', 'Wydarzenie'), ('3', 'Zajęcia'), ('4', 'Inne')], max_length=1, verbose_name='Typ'), + ), + migrations.RunPython(update_entry_names), + ] diff --git a/zapisy/apps/schedule/models/event.py b/zapisy/apps/schedule/models/event.py index 26c326b364..b42e6a346a 100644 --- a/zapisy/apps/schedule/models/event.py +++ b/zapisy/apps/schedule/models/event.py @@ -9,6 +9,9 @@ from apps.enrollment.courses.models.course_instance import CourseInstance from apps.enrollment.courses.models.group import Group from apps.enrollment.records.models import Record, RecordStatus +from apps.theses.models import Thesis + +MAX_EVENT_TITLE_LEN = 255 class Event(models.Model): @@ -17,6 +20,7 @@ class Event(models.Model): TYPE_GENERIC = '2' TYPE_CLASS = '3' TYPE_OTHER = '4' + TYPE_DEFENCE = '5' STATUS_PENDING = '0' STATUS_ACCEPTED = '1' @@ -28,6 +32,7 @@ class Event(models.Model): TYPES = [(TYPE_EXAM, 'Egzamin'), (TYPE_TEST, 'Kolokwium'), + (TYPE_DEFENCE, 'Obrona pracy dyplomowej'), (TYPE_GENERIC, 'Wydarzenie'), (TYPE_CLASS, 'Zajęcia'), (TYPE_OTHER, 'Inne')] @@ -36,14 +41,16 @@ class Event(models.Model): TYPES_FOR_TEACHER = [(TYPE_EXAM, 'Egzamin'), (TYPE_TEST, 'Kolokwium'), + (TYPE_DEFENCE, 'Obrona pracy dyplomowej'), (TYPE_GENERIC, 'Wydarzenie')] - title = models.CharField(max_length=255, verbose_name='Tytuł', null=True, blank=True) + title = models.CharField(max_length=MAX_EVENT_TITLE_LEN, verbose_name='Tytuł', null=True, blank=True) description = models.TextField(verbose_name='Opis', blank=True) type = models.CharField(choices=TYPES, max_length=1, verbose_name='Typ') visible = models.BooleanField(verbose_name='Wydarzenie jest publiczne', default=False) status = models.CharField(choices=STATUSES, max_length=1, verbose_name='Stan', default='0') course = models.ForeignKey(CourseInstance, null=True, blank=True, on_delete=models.CASCADE) + thesis = models.ForeignKey(Thesis, null=True, blank=True, on_delete=models.CASCADE) group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.CASCADE) reservation = models.ForeignKey( 'schedule.SpecialReservation', @@ -86,10 +93,27 @@ def clean(self, *args, **kwargs): self.author.has_perm('schedule.manage_events')): self.status = self.STATUS_ACCEPTED - # all exams and tests should be public + # all exams, tests & defenses should be public, we also need to create titles for them - if self.type in [Event.TYPE_EXAM, Event.TYPE_TEST]: + if self.type in [Event.TYPE_EXAM, Event.TYPE_TEST, Event.TYPE_DEFENCE]: self.visible = True + if self.title: + pass + elif self.type in (Event.TYPE_EXAM, Event.TYPE_TEST): + self.title = (self.get_type_display() + ": " + self.course.name)[:MAX_EVENT_TITLE_LEN] + elif self.type == Event.TYPE_DEFENCE: + self.title = (self.get_type_display() + ": " + self.thesis.title)[:MAX_EVENT_TITLE_LEN] + elif self.type == Event.TYPE_CLASS: + self.title = self.group.course.get_short_name() + else: + self.title = "Wydarzenie bez tytułu" + + # only the advisor and supporting advisor should be able to schedule a thesis defence + + if self.type == Event.TYPE_DEFENCE and self.author.employee in \ + (self.thesis.advisor, self.thesis.supporting_advisor) or \ + self.author.has_perm('schedule.manage_events'): + self.status = self.STATUS_ACCEPTED # students can only add generic events that have to be accepted first diff --git a/zapisy/apps/schedule/templates/schedule/event.html b/zapisy/apps/schedule/templates/schedule/event.html index a8b100d705..0d472ec40a 100644 --- a/zapisy/apps/schedule/templates/schedule/event.html +++ b/zapisy/apps/schedule/templates/schedule/event.html @@ -10,13 +10,7 @@ {% block content %}
Tytuł | - {% if event.course %} - {{ event.course.name }} - {% else %} - {{ event.title }} - {% endif %} + {{ event.title }} | @@ -51,4 +47,4 @@ -
---|