Skip to content

Commit

Permalink
Guaranteed spots (#725)
Browse files Browse the repository at this point in the history
Wprowadza mechanizm gwarantowanych miejsc w grupach zajęciowych.

Dzięki temu rozwiązaniu administratorzy mogą, poza ogólnym limitem miejsc w grupie, wprowadzić pule miejsc gwarantowanych dla wybranych grup studentów (np. dla studentów ISIM-u). Istnienie dodatkowej puli nie ogranicza od góry liczby studentów z tej promowanej grupy (może ich być najwyżej limit grupy + pula dodatkowa).
  • Loading branch information
jasiekmarc committed Sep 12, 2019
1 parent cf91612 commit 6ad287a
Show file tree
Hide file tree
Showing 19 changed files with 455 additions and 368 deletions.
13 changes: 11 additions & 2 deletions zapisy/apps/enrollment/courses/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from apps.enrollment.courses.models.course_instance import CourseInstance
from apps.enrollment.courses.models.course_type import Type
from apps.enrollment.courses.models.effects import Effects
from apps.enrollment.courses.models.group import Group
from apps.enrollment.courses.models.group import Group, GuaranteedSpots
from apps.enrollment.courses.models.semester import Semester, Freeday, ChangedDay
from apps.enrollment.courses.models.tag import Tag
from apps.enrollment.courses.models.term import Term
Expand Down Expand Up @@ -88,6 +88,15 @@ class TermInline(admin.TabularInline):
extra = 0


class GuaranteedSpotsInline(admin.StackedInline):
model = GuaranteedSpots
extra = 0
fieldsets = ((None, {
'fields': ('role', 'limit'),
'description': "<strong style='color: red'>Grupy użytkowników, którym gwarantujemy miejsca w jednej grupie zajęciowej muszą być rozłączne!</strong>",
}), )


class RecordInline(admin.TabularInline):
model = Record
extra = 0
Expand Down Expand Up @@ -120,7 +129,7 @@ class GroupAdmin(admin.ModelAdmin):
'teacher__user__last_name',
'course__name')
inlines = [
TermInline, RecordInline
TermInline, RecordInline, GuaranteedSpotsInline
]

raw_id_fields = ('course', 'teacher')
Expand Down
212 changes: 0 additions & 212 deletions zapisy/apps/enrollment/courses/management.py

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.9 on 2019-08-22 14:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('courses', '0026_courseinformation_discipline'),
]

operations = [
migrations.AlterField(
model_name='group',
name='extra',
field=models.CharField(blank=True, choices=[('', ''), ('pierwsze 7 tygodni', 'pierwsze 7 tygodni'), ('drugie 7 tygodni', 'drugie 7 tygodni'), ('grupa rezerwowa', 'grupa rezerwowa'), ('grupa licencjacka', 'grupa licencjacka'), ('grupa magisterska', 'grupa magisterska'), ('grupa zaawansowana', 'grupa zaawansowana'), ('zajecia na mat.', 'zajęcia na matematyce'), ('wykład okrojony', 'wykład okrojony'), ('grupa 1', 'grupa 1'), ('grupa 2', 'grupa 2'), ('grupa 3', 'grupa 3'), ('grupa 4', 'grupa 4'), ('grupa 5', 'grupa 5'), ('pracownia linuksowa', 'pracownia linuksowa'), ('grupa anglojęzyczna', 'grupa anglojęzyczna'), ('I rok', 'I rok'), ('II rok', 'II rok'), ('ISIM', 'ISIM'), ('hidden', 'grupa ukryta')], default='', max_length=20, verbose_name='dodatkowe informacje'),
),
]
25 changes: 25 additions & 0 deletions zapisy/apps/enrollment/courses/migrations/0028_guaranteedspots.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 2.1.9 on 2019-08-22 15:11

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
('courses', '0027_auto_20190822_1414'),
]

operations = [
migrations.CreateModel(
name='GuaranteedSpots',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('limit', models.PositiveSmallIntegerField(verbose_name='liczba miejsc')),
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guaranteed_spots', to='courses.Group', verbose_name='grupa zajęciowa')),
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='auth.Group', verbose_name='grupa użytkowników')),
],
options={'verbose_name': 'miejsca gwarantowane', 'verbose_name_plural': 'miejsca gwarantowane'},
),
]
45 changes: 39 additions & 6 deletions zapisy/apps/enrollment/courses/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
A group may have multiple terms - that is, students may meet with the teacher
more than one time a week.
"""
from typing import Iterable
from typing import Optional

from django.db import models, transaction
from django.urls import reverse
Expand Down Expand Up @@ -37,9 +37,6 @@
('pracownia linuksowa', 'pracownia linuksowa'),
('grupa anglojęzyczna', 'grupa anglojęzyczna'),
('I rok', 'I rok'), ('II rok', 'II rok'), ('ISIM', 'ISIM'),
('tura1', 'grupa wirtualna dla pierwszej 1/3 pierwszaków'),
('tura2', 'grupa wirtualna dla drugiej 1/3 pierwszaków'),
('tura3', 'grupa wirtualna dla trzeciej 1/3 pierwszaków'),
('hidden', 'grupa ukryta'),
]

Expand Down Expand Up @@ -175,14 +172,17 @@ def copy_term(t: Term) -> Term:
return copy

@classmethod
def get_lecture_groups(cls, course_id: int) -> Iterable['Group']:
def get_lecture_group(cls, course_id: int) -> Optional['Group']:
"""Given a course_id returns a lecture group for this course, if one exists.
The Group.MultipleObjectsReturned exception will be raised when many
lecture groups exist for course.
"""
group_query = cls.objects.filter(course_id=course_id, type=Group.GROUP_TYPE_LECTURE)
return list(group_query)
try:
return group_query.get()
except cls.DoesNotExist:
return None

def save(self, *args, **kwargs):
"""Overloaded save method - during save check changes and send signals to notifications app"""
Expand All @@ -191,3 +191,36 @@ def save(self, *args, **kwargs):
if old:
if old.teacher != self.teacher:
teacher_changed.send(sender=self.__class__, instance=self, teacher=self.teacher)


class GuaranteedSpotsManager(models.Manager):
"""This thin manager always pulls auth.Group names for efficiency."""
def get_queryset(self):
return super().get_queryset().select_related('role')


class GuaranteedSpots(models.Model):
"""Defines an additional pool of spots in a course group reserved for a role.
Normally a course group would have a single limit defining how many students
can be enrolled to it at the same time. Sometimes we would however like to
reserve a number of spots to a group of students (i.e. freshmen, or ISIM).
This mechanism will allow us to do that.
"""
group = models.ForeignKey(
Group,
on_delete=models.CASCADE,
related_name='guaranteed_spots',
verbose_name="grupa zajęciowa")
role = models.ForeignKey(
'auth.Group', on_delete=models.CASCADE, related_name='+', verbose_name='grupa użytkowników')
limit = models.PositiveSmallIntegerField("liczba miejsc")

objects = GuaranteedSpotsManager()

class Meta:
verbose_name = 'miejsca gwarantowane'
verbose_name_plural = 'miejsca gwarantowane'

def __str__(self):
return f"{self.limit} miejsc gwarantowanych w grupie {self.group} dla użytkowników {self.role}"
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,15 @@ <h3>{{ tutorial.grouper|decode_class_type_plural }}</h3>
<span>{{ term }}</span>
{% endfor %}
</td>
<td class="number termLimit align-middle">{{ group.limit }}</td>
<td class="number termLimit align-middle">
{{ group.limit }}
{% for gs in group.guaranteed_spots.all %}
+
<span title="Miejsca gwarantowane dla grupy {{gs.role.name}}.">
{{ gs.limit }}
</span>
{% endfor %}
</td>
<td class="number termEnrolledCount align-middle">{{ group.num_enrolled }}</td>
<td class="number termQueuedCount align-middle">{{ group.num_enqueued }}</td>
{% if course.is_enrollment_on %}
Expand Down
7 changes: 7 additions & 0 deletions zapisy/apps/enrollment/courses/templates/courses/group.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ <h6>{% for term in group.term.all %}{{ term }}{% endfor %}</h6>
<h3>{% trans "Lista osób zapisanych na przedmiot" %}:</h3>
<p>
{% trans "Liczba zapisanych osób" %}: {{students_in_group|length}} / {{group.limit}}
{% for gs in guaranteed_spots %}
+
<span title="Miejsca gwarantowane dla grupy {{gs.role.name}}.">
{{ gs.limit }}
<span class="badge badge-dark">{{ gs.role.name }}</span>
</span>
{% endfor %}
</p>

{% if students_in_group %}
Expand Down
Loading

0 comments on commit 6ad287a

Please sign in to comment.