Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/official/AlekSIS-App-Alsijil
  • sunweaver/AlekSIS-App-Alsijil
  • 8tincsoVluke/AlekSIS-App-Alsijil
  • perfreicpo/AlekSIS-App-Alsijil
  • noifobarep/AlekSIS-App-Alsijil
  • 7ingannisdo/AlekSIS-App-Alsijil
  • unmruntartpa/AlekSIS-App-Alsijil
  • balrorebta/AlekSIS-App-Alsijil
  • comliFdifwa/AlekSIS-App-Alsijil
  • 3ranaadza/AlekSIS-App-Alsijil
10 results
Show changes
Commits on Source (1)
import pkg_resources
try:
__version__ = pkg_resources.get_distribution('BiscuIT-App-Alsijil').version
__version__ = pkg_resources.get_distribution("BiscuIT-App-Alsijil").version
except Exception:
__version__ = 'unknown'
__version__ = "unknown"
default_app_config = 'biscuit.apps.alsijil.apps.AlsijilConfig'
default_app_config = "biscuit.apps.alsijil.apps.AlsijilConfig"
......@@ -2,5 +2,5 @@ from biscuit.core.util.apps import AppConfig
class AlsijilConfig(AppConfig):
name = 'biscuit.apps.alsijil'
verbose_name = 'BiscuIT - Alsijil (Class register)'
name = "biscuit.apps.alsijil"
verbose_name = "BiscuIT - Alsijil (Class register)"
from datetime import datetime
from django import forms
from django.db.models import Count
from django.utils.translation import ugettext_lazy as _
from django_select2.forms import Select2Widget
from datetime import datetime
from biscuit.apps.chronos.models import Room
from biscuit.core.models import Group, Person
......@@ -13,54 +15,75 @@ from .models import LessonDocumentation, PersonalNote, PersonalNoteFilter
class LessonDocumentationForm(forms.ModelForm):
class Meta:
model = LessonDocumentation
fields = ['topic', 'homework']
fields = ["topic", "homework"]
class PersonalNoteForm(forms.ModelForm):
class Meta:
model = PersonalNote
fields = ['absent', 'late', 'excused', 'remarks']
fields = ["absent", "late", "excused", "remarks"]
person_name = forms.CharField(disabled=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['person_name'].widget.attrs.update(
{'class': 'alsijil-lesson-personal-note-name'})
self.fields["person_name"].widget.attrs.update(
{"class": "alsijil-lesson-personal-note-name"}
)
if self.instance and getattr(self.instance, 'person', None):
self.fields['person_name'].initial = str(self.instance.person)
if self.instance and getattr(self.instance, "person", None):
self.fields["person_name"].initial = str(self.instance.person)
class SelectForm(forms.Form):
group = forms.ModelChoiceField(
queryset=Group.objects.annotate(lessons_count=Count('lessons')).filter(lessons_count__gt=0),
label=_('Group'), required=False, widget=Select2Widget)
queryset=Group.objects.annotate(lessons_count=Count("lessons")).filter(
lessons_count__gt=0
),
label=_("Group"),
required=False,
widget=Select2Widget,
)
teacher = forms.ModelChoiceField(
queryset=Person.objects.annotate(lessons_count=Count(
'lessons_as_teacher')).filter(lessons_count__gt=0),
label=_('Teacher'), required=False, widget=Select2Widget)
queryset=Person.objects.annotate(
lessons_count=Count("lessons_as_teacher")
).filter(lessons_count__gt=0),
label=_("Teacher"),
required=False,
widget=Select2Widget,
)
room = forms.ModelChoiceField(
queryset=Room.objects.annotate(lessons_count=Count(
'lesson_periods')).filter(lessons_count__gt=0),
label=_('Room'), required=False, widget=Select2Widget)
queryset=Room.objects.annotate(lessons_count=Count("lesson_periods")).filter(
lessons_count__gt=0
),
label=_("Room"),
required=False,
widget=Select2Widget,
)
PersonalNoteFormSet = forms.modelformset_factory(
PersonalNote, form=PersonalNoteForm, max_num=0, extra=0)
PersonalNote, form=PersonalNoteForm, max_num=0, extra=0
)
class RegisterAbsenceForm(forms.Form):
date_start = forms.DateField(label=_('Start date'), widget=forms.SelectDateWidget, initial=datetime.today)
date_end = forms.DateField(label=_('End date'), widget=forms.SelectDateWidget, initial=datetime.today)
from_period = forms.IntegerField(label=_('From period'), initial=0, min_value=0)
person = forms.ModelChoiceField(label=_('Person'), queryset=Person.objects.all(), widget=Select2Widget)
absent = forms.BooleanField(label=_('Absent'), initial=True, required=False)
excused = forms.BooleanField(label=_('Excused'), initial=True, required=False)
remarks = forms.CharField(label=_('Remarks'), max_length=30, required=False)
date_start = forms.DateField(
label=_("Start date"), widget=forms.SelectDateWidget, initial=datetime.today
)
date_end = forms.DateField(
label=_("End date"), widget=forms.SelectDateWidget, initial=datetime.today
)
from_period = forms.IntegerField(label=_("From period"), initial=0, min_value=0)
person = forms.ModelChoiceField(
label=_("Person"), queryset=Person.objects.all(), widget=Select2Widget
)
absent = forms.BooleanField(label=_("Absent"), initial=True, required=False)
excused = forms.BooleanField(label=_("Excused"), initial=True, required=False)
remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False)
class PersonalNoteFilterForm(forms.ModelForm):
class Meta:
model = PersonalNoteFilter
fields = ['identifier', 'description', 'regex']
fields = ["identifier", "description", "regex"]
from django.utils.translation import ugettext_lazy as _
MENUS = {
'NAV_MENU_CORE': [
"NAV_MENU_CORE": [
{
'name': _('Class register'),
'url': '#',
'root': True,
'validators': ['menu_generator.validators.is_authenticated', 'biscuit.core.util.core_helpers.has_person'],
'submenu': [
"name": _("Class register"),
"url": "#",
"root": True,
"validators": [
"menu_generator.validators.is_authenticated",
"biscuit.core.util.core_helpers.has_person",
],
"submenu": [
{
'name': _('Current lesson'),
'url': 'lesson',
'validators': ['menu_generator.validators.is_authenticated']
"name": _("Current lesson"),
"url": "lesson",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
'name': _('Current week'),
'url': 'week_view',
'validators': ['menu_generator.validators.is_authenticated']
"name": _("Current week"),
"url": "week_view",
"validators": ["menu_generator.validators.is_authenticated"],
},
{
'name': _('Register absence'),
'url': 'register_absence',
'validators': ['menu_generator.validators.is_superuser']
"name": _("Register absence"),
"url": "register_absence",
"validators": ["menu_generator.validators.is_superuser"],
},
{
'name': _('Personal note filters'),
'url': 'list_personal_note_filters',
'validators': ['menu_generator.validators.is_superuser']
}
]
"name": _("Personal note filters"),
"url": "list_personal_note_filters",
"validators": ["menu_generator.validators.is_superuser"],
},
],
}
]
}
# Generated by Django 2.2.5 on 2019-09-03 18:30
import biscuit.core.util.core_helpers
from django.db import migrations, models
import django.db.models.deletion
from django.db import migrations, models
import biscuit.core.util.core_helpers
class Migration(migrations.Migration):
......@@ -10,42 +11,114 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('chronos', '0001_initial'),
('core', '0001_initial'),
("chronos", "0001_initial"),
("core", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='PersonalNote',
name="PersonalNote",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('week', models.IntegerField()),
('absent', models.BooleanField(default=False)),
('late', models.IntegerField(default=0)),
('excused', models.BooleanField(default=False)),
('remarks', models.CharField(blank=True, max_length=200)),
('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chronos.LessonPeriod')),
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='personal_notes', to='core.Person')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("week", models.IntegerField()),
("absent", models.BooleanField(default=False)),
("late", models.IntegerField(default=0)),
("excused", models.BooleanField(default=False)),
("remarks", models.CharField(blank=True, max_length=200)),
(
"lesson_period",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="chronos.LessonPeriod",
),
),
(
"person",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="personal_notes",
to="core.Person",
),
),
(
"school",
models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="core.School",
),
),
],
options={
'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period', 'person__last_name', 'person__first_name'],
'unique_together': {('school', 'lesson_period', 'week', 'person')},
"ordering": [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
"person__last_name",
"person__first_name",
],
"unique_together": {("school", "lesson_period", "week", "person")},
},
),
migrations.CreateModel(
name='LessonDocumentation',
name="LessonDocumentation",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('week', models.IntegerField()),
('topic', models.CharField(blank=True, max_length=200, verbose_name='Lesson topic')),
('homework', models.CharField(blank=True, max_length=200, verbose_name='Homework')),
('lesson_period', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='documentations', to='chronos.LessonPeriod')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("week", models.IntegerField()),
(
"topic",
models.CharField(
blank=True, max_length=200, verbose_name="Lesson topic"
),
),
(
"homework",
models.CharField(
blank=True, max_length=200, verbose_name="Homework"
),
),
(
"lesson_period",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="documentations",
to="chronos.LessonPeriod",
),
),
(
"school",
models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="core.School",
),
),
],
options={
'ordering': ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday', 'lesson_period__period__period'],
'unique_together': {('school', 'lesson_period', 'week')},
"ordering": [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
],
"unique_together": {("school", "lesson_period", "week")},
},
),
]
# Generated by Django 2.2.5 on 2019-11-20 14:21
import django.db.models.deletion
from django.db import migrations, models
import biscuit.apps.alsijil.models
import biscuit.core.util.core_helpers
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('core', '0003_school_logo'),
('alsijil', '0001_initial'),
("core", "0003_school_logo"),
("alsijil", "0001_initial"),
]
operations = [
migrations.CreateModel(
name='PersonalNoteFilter',
name="PersonalNoteFilter",
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('identifier', models.CharField(max_length=30, validators=[biscuit.apps.alsijil.models.isidentifier], verbose_name='Identifier')),
('description', models.CharField(blank=True, max_length=60, verbose_name='Description')),
('regex', models.CharField(max_length=100, verbose_name='Match expression')),
('school', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='core.School')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"identifier",
models.CharField(
max_length=30,
validators=[biscuit.apps.alsijil.models.isidentifier],
verbose_name="Identifier",
),
),
(
"description",
models.CharField(
blank=True, max_length=60, verbose_name="Description"
),
),
(
"regex",
models.CharField(max_length=100, verbose_name="Match expression"),
),
(
"school",
models.ForeignKey(
default=1,
on_delete=django.db.models.deletion.CASCADE,
to="core.School",
),
),
],
options={
'ordering': ['identifier'],
'unique_together': {('school', 'regex'), ('school', 'description'), ('school', 'identifier')},
"ordering": ["identifier"],
"unique_together": {
("school", "regex"),
("school", "description"),
("school", "identifier"),
},
},
),
]
......@@ -10,7 +10,14 @@ from .models import PersonalNote
@Person.method
def mark_absent(self, day: date, from_period: int = 0, absent: bool = True, excused: bool = False, remarks: str = ''):
def mark_absent(
self,
day: date,
from_period: int = 0,
absent: bool = True,
excused: bool = False,
remarks: str = "",
):
""" Mark a person absent for all lessons in a day, optionally starting with
a selected period number.
......@@ -27,9 +34,7 @@ def mark_absent(self, day: date, from_period: int = 0, absent: bool = True, excu
wanted_week = CalendarWeek.from_date(day)
# Get all lessons of this person on the specified day
lesson_periods = self.lesson_periods_as_participant.on_day(
day
).filter(
lesson_periods = self.lesson_periods_as_participant.on_day(day).filter(
period__period__gte=from_period
)
......@@ -39,15 +44,12 @@ def mark_absent(self, day: date, from_period: int = 0, absent: bool = True, excu
person=self,
lesson_period=lesson_period,
week=wanted_week.week,
defaults={
'absent': absent,
'excused': excused
}
defaults={"absent": absent, "excused": excused},
)
if remarks:
if personal_note.remarks:
personal_note.remarks += '; %s' % remarks
personal_note.remarks += "; %s" % remarks
else:
personal_note.remarks = remarks
personal_note.save()
......@@ -69,22 +71,25 @@ def get_personal_notes(self, wanted_week: CalendarWeek):
# Find all persons in the associated groups that do not yet have a personal note for this lesson
missing_persons = Person.objects.annotate(
no_personal_notes=~Exists(PersonalNote.objects.filter(
week=wanted_week.week,
lesson_period=self,
person__pk=OuterRef('pk')
))
no_personal_notes=~Exists(
PersonalNote.objects.filter(
week=wanted_week.week, lesson_period=self, person__pk=OuterRef("pk")
)
)
).filter(
member_of__in=Group.objects.filter(pk__in=self.lesson.groups.all()),
is_active=True,
no_personal_notes=True
no_personal_notes=True,
)
# Create all missing personal notes
PersonalNote.objects.bulk_create([
PersonalNote(person=person, lesson_period=self,
week=wanted_week.week) for person in missing_persons
])
PersonalNote.objects.bulk_create(
[
PersonalNote(person=person, lesson_period=self, week=wanted_week.week)
for person in missing_persons
]
)
return PersonalNote.objects.select_related('person').filter(
lesson_period=self, week=wanted_week.week)
return PersonalNote.objects.select_related("person").filter(
lesson_period=self, week=wanted_week.week
)
......@@ -13,10 +13,14 @@ class PersonalNote(models.Model):
absences, excuses and remarks about a student in a single lesson period.
"""
person = models.ForeignKey('core.Person', models.CASCADE, related_name='personal_notes')
person = models.ForeignKey(
"core.Person", models.CASCADE, related_name="personal_notes"
)
week = models.IntegerField()
lesson_period = models.ForeignKey('chronos.LessonPeriod', models.CASCADE, related_name='personal_notes')
lesson_period = models.ForeignKey(
"chronos.LessonPeriod", models.CASCADE, related_name="personal_notes"
)
absent = models.BooleanField(default=False)
late = models.IntegerField(default=0)
......@@ -25,9 +29,15 @@ class PersonalNote(models.Model):
remarks = models.CharField(max_length=200, blank=True)
class Meta:
unique_together = [['lesson_period', 'week', 'person']]
ordering = ['lesson_period__lesson__date_start', 'week', 'lesson_period__period__weekday',
'lesson_period__period__period', 'person__last_name', 'person__first_name']
unique_together = [["lesson_period", "week", "person"]]
ordering = [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
"person__last_name",
"person__first_name",
]
class LessonDocumentation(CRUDMixin):
......@@ -37,27 +47,38 @@ class LessonDocumentation(CRUDMixin):
week = models.IntegerField()
lesson_period = models.ForeignKey(
'chronos.LessonPeriod', models.CASCADE, related_name='documentations')
"chronos.LessonPeriod", models.CASCADE, related_name="documentations"
)
topic = models.CharField(verbose_name=_('Lesson topic'), max_length=200, blank=True)
homework = models.CharField(verbose_name=_('Homework'), max_length=200, blank=True)
topic = models.CharField(verbose_name=_("Lesson topic"), max_length=200, blank=True)
homework = models.CharField(verbose_name=_("Homework"), max_length=200, blank=True)
class Meta:
unique_together = [['lesson_period', 'week']]
ordering = ['lesson_period__lesson__date_start', 'week',
'lesson_period__period__weekday', 'lesson_period__period__period']
unique_together = [["lesson_period", "week"]]
ordering = [
"lesson_period__lesson__date_start",
"week",
"lesson_period__period__weekday",
"lesson_period__period__period",
]
class PersonalNoteFilter(models.Model):
""" A filter definition that can generate statistics on personal note texts. """
identifier = models.CharField(verbose_name=_('Identifier'), max_length=30,
validators=[isidentifier], unique=True)
description = models.CharField(verbose_name=_('Description'), max_length=60,
blank=True, unique=True)
identifier = models.CharField(
verbose_name=_("Identifier"),
max_length=30,
validators=[isidentifier],
unique=True,
)
description = models.CharField(
verbose_name=_("Description"), max_length=60, blank=True, unique=True
)
regex = models.CharField(verbose_name=_('Match expression'), max_length=100,
unique=True)
regex = models.CharField(
verbose_name=_("Match expression"), max_length=100, unique=True
)
class Meta:
ordering = ['identifier']
ordering = ["identifier"]
from django.utils.translation import ugettext_lazy as _
import django_tables2 as tables
from django_tables2.utils import A
class PersonalNoteFilterTable(tables.Table):
class Meta:
attrs = {'class': 'table table-striped table-bordered table-hover table-responsive-xl'}
attrs = {
"class": "table table-striped table-bordered table-hover table-responsive-xl"
}
identifier = tables.Column()
description = tables.Column()
regex = tables.Column()
edit_filter = tables.LinkColumn(
'edit_personal_note_filter', args=[A('id')], text=_('Edit'))
"edit_personal_note_filter", args=[A("id")], text=_("Edit")
)
......@@ -2,24 +2,37 @@ from django.urls import path
from . import views
urlpatterns = [
path('lesson', views.lesson, name='lesson'),
path('lesson/<int:year>/<int:week>/<int:period_id>', views.lesson,
name='lesson_by_week_and_period'),
path('week', views.week_view, name='week_view'),
path('week/<int:year>/<int:week>', views.week_view,
name='week_view_by_week'),
path('print/group/<int:id_>', views.full_register_group,
name='full_register_group'),
path('absence/new', views.register_absence,
name='register_absence'),
path('filters/list', views.list_personal_note_filters,
name='list_personal_note_filters'),
path('filters/create', views.edit_personal_note_filter,
name='create_personal_note_filter'),
path('filters/edit/<int:id>', views.edit_personal_note_filter,
name='edit_personal_note_filter'),
path('filters/delete/<int:id_>', views.delete_personal_note_filter,
name='delete_personal_note_filter')
path("lesson", views.lesson, name="lesson"),
path(
"lesson/<int:year>/<int:week>/<int:period_id>",
views.lesson,
name="lesson_by_week_and_period",
),
path("week", views.week_view, name="week_view"),
path("week/<int:year>/<int:week>", views.week_view, name="week_view_by_week"),
path(
"print/group/<int:id_>", views.full_register_group, name="full_register_group"
),
path("absence/new", views.register_absence, name="register_absence"),
path(
"filters/list",
views.list_personal_note_filters,
name="list_personal_note_filters",
),
path(
"filters/create",
views.edit_personal_note_filter,
name="create_personal_note_filter",
),
path(
"filters/edit/<int:id>",
views.edit_personal_note_filter,
name="edit_personal_note_filter",
),
path(
"filters/delete/<int:id_>",
views.delete_personal_note_filter,
name="delete_personal_note_filter",
),
]
......@@ -13,17 +13,28 @@ from django_tables2 import RequestConfig
from biscuit.apps.chronos.models import LessonPeriod
from biscuit.apps.chronos.util import CalendarWeek
from biscuit.core.models import Group, Person, School
from biscuit.core.decorators import admin_required
from biscuit.core.models import Group, Person, School
from biscuit.core.util import messages
from .forms import LessonDocumentationForm, PersonalNoteFormSet, RegisterAbsenceForm, SelectForm, PersonalNoteFilterForm
from .forms import (
LessonDocumentationForm,
PersonalNoteFilterForm,
PersonalNoteFormSet,
RegisterAbsenceForm,
SelectForm,
)
from .models import LessonDocumentation, PersonalNoteFilter
from .tables import PersonalNoteFilterTable
@login_required
def lesson(request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None, period_id: Optional[int] = None) -> HttpResponse:
def lesson(
request: HttpRequest,
year: Optional[int] = None,
week: Optional[int] = None,
period_id: Optional[int] = None,
) -> HttpResponse:
context = {}
if year and week and period_id:
......@@ -32,34 +43,60 @@ def lesson(request: HttpRequest, year: Optional[int] = None, week: Optional[int]
wanted_week = CalendarWeek(year=year, week=week)
else:
# Determine current lesson by current date and time
lesson_period = LessonPeriod.objects.at_time().filter_teacher(request.user.person).first()
lesson_period = (
LessonPeriod.objects.at_time().filter_teacher(request.user.person).first()
)
wanted_week = CalendarWeek()
if lesson_period:
return redirect('lesson_by_week_and_period', wanted_week.year, wanted_week.week, lesson_period.pk)
return redirect(
"lesson_by_week_and_period",
wanted_week.year,
wanted_week.week,
lesson_period.pk,
)
else:
raise Http404(_('You either selected an invalid lesson or there is currently no lesson in progress.'))
raise Http404(
_(
"You either selected an invalid lesson or there is currently no lesson in progress."
)
)
if datetime.combine(wanted_week[lesson_period.period.weekday - 1], lesson_period.period.time_start) > datetime.now() and not request.user.is_superuser:
raise PermissionDenied(_('You are not allowed to create a lesson documentation for a lesson in the future.'))
if (
datetime.combine(
wanted_week[lesson_period.period.weekday - 1],
lesson_period.period.time_start,
)
> datetime.now()
and not request.user.is_superuser
):
raise PermissionDenied(
_(
"You are not allowed to create a lesson documentation for a lesson in the future."
)
)
context['lesson_period'] = lesson_period
context['week'] = wanted_week
context['day'] = wanted_week[lesson_period.period.weekday - 1]
context["lesson_period"] = lesson_period
context["week"] = wanted_week
context["day"] = wanted_week[lesson_period.period.weekday - 1]
# Create or get lesson documentation object; can be empty when first opening lesson
lesson_documentation, created = LessonDocumentation.objects.get_or_create(
lesson_period=lesson_period, week=wanted_week.week)
lesson_period=lesson_period, week=wanted_week.week
)
lesson_documentation_form = LessonDocumentationForm(
request.POST or None, instance=lesson_documentation, prefix='leson_documentation')
request.POST or None,
instance=lesson_documentation,
prefix="leson_documentation",
)
# Create a formset that holds all personal notes for all persons in this lesson
persons_qs = lesson_period.get_personal_notes(wanted_week)
personal_note_formset = PersonalNoteFormSet(
request.POST or None, queryset=persons_qs, prefix='personal_notes')
request.POST or None, queryset=persons_qs, prefix="personal_notes"
)
if request.method == 'POST':
if request.method == "POST":
if lesson_documentation_form.is_valid():
lesson_documentation_form.save()
......@@ -69,21 +106,23 @@ def lesson(request: HttpRequest, year: Optional[int] = None, week: Optional[int]
# Iterate over personal notes and carry changed absences to following lessons
for instance in instances:
instance.person.mark_absent(
wanted_week[lesson_period.period.weekday-1],
lesson_period.period.period+1,
wanted_week[lesson_period.period.weekday - 1],
lesson_period.period.period + 1,
instance.absent,
instance.excused
instance.excused,
)
context['lesson_documentation'] = lesson_documentation
context['lesson_documentation_form'] = lesson_documentation_form
context['personal_note_formset'] = personal_note_formset
context["lesson_documentation"] = lesson_documentation
context["lesson_documentation_form"] = lesson_documentation_form
context["personal_note_formset"] = personal_note_formset
return render(request, 'alsijil/lesson.html', context)
return render(request, "alsijil/lesson.html", context)
@login_required
def week_view(request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None) -> HttpResponse:
def week_view(
request: HttpRequest, year: Optional[int] = None, week: Optional[int] = None
) -> HttpResponse:
context = {}
if year and week:
......@@ -92,21 +131,25 @@ def week_view(request: HttpRequest, year: Optional[int] = None, week: Optional[i
wanted_week = CalendarWeek()
lesson_periods = LessonPeriod.objects.annotate(
has_documentation=Exists(LessonDocumentation.objects.filter(
~Q(topic__exact=''),
lesson_period=OuterRef('pk'),
week=wanted_week.week
))
has_documentation=Exists(
LessonDocumentation.objects.filter(
~Q(topic__exact=""), lesson_period=OuterRef("pk"), week=wanted_week.week
)
)
).in_week(wanted_week)
group = None # FIXME workaround for #38
if request.GET.get('group', None) or request.GET.get('teacher', None) or request.GET.get('room', None):
if (
request.GET.get("group", None)
or request.GET.get("teacher", None)
or request.GET.get("room", None)
):
lesson_periods = lesson_periods.filter_from_query(request.GET)
if 'group' in request.GET and request.GET['group']:
group = Group.objects.get(pk=request.GET['group'])
if "group" in request.GET and request.GET["group"]:
group = Group.objects.get(pk=request.GET["group"])
else:
group = None
elif hasattr(request, 'user') and hasattr(request.user, 'person'):
elif hasattr(request, "user") and hasattr(request.user, "person"):
group = request.user.person.owner_of.first()
if group:
lesson_periods = lesson_periods.filter_group(group)
......@@ -119,28 +162,37 @@ def week_view(request: HttpRequest, year: Optional[int] = None, week: Optional[i
if lesson_periods:
# Aggregate all personal notes for this group and week
persons = Person.objects.filter(
is_active=True
).filter(
member_of__lessons__lesson_periods__in=lesson_periods
).distinct().prefetch_related(
'personal_notes'
).annotate(
absences=Count('personal_notes__absent', filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True
)),
unexcused=Count('personal_notes__absent', filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
personal_notes__excused=False
)),
tardiness=Sum('personal_notes__late', filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week
))
persons = (
Person.objects.filter(is_active=True)
.filter(member_of__lessons__lesson_periods__in=lesson_periods)
.distinct()
.prefetch_related("personal_notes")
.annotate(
absences=Count(
"personal_notes__absent",
filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
),
),
unexcused=Count(
"personal_notes__absent",
filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
personal_notes__absent=True,
personal_notes__excused=False,
),
),
tardiness=Sum(
"personal_notes__late",
filter=Q(
personal_notes__lesson_period__in=lesson_periods,
personal_notes__week=wanted_week.week,
),
),
)
)
else:
persons = None
......@@ -148,19 +200,25 @@ def week_view(request: HttpRequest, year: Optional[int] = None, week: Optional[i
# Add a form to filter the view
select_form = SelectForm(request.GET or None)
context['current_head'] = str(wanted_week)
context['week'] = wanted_week
context['lesson_periods'] = lesson_periods
context['persons'] = persons
context['group'] = group
context['select_form'] = select_form
context["current_head"] = str(wanted_week)
context["week"] = wanted_week
context["lesson_periods"] = lesson_periods
context["persons"] = persons
context["group"] = group
context["select_form"] = select_form
week_prev = wanted_week - 1
week_next = wanted_week + 1
context['url_prev'] = '%s?%s' % (reverse('week_view_by_week', args=[week_prev.year, week_prev.week]), request.GET.urlencode())
context['url_next'] = '%s?%s' % (reverse('week_view_by_week', args=[week_next.year, week_next.week]), request.GET.urlencode())
context["url_prev"] = "%s?%s" % (
reverse("week_view_by_week", args=[week_prev.year, week_prev.week]),
request.GET.urlencode(),
)
context["url_next"] = "%s?%s" % (
reverse("week_view_by_week", args=[week_next.year, week_next.week]),
request.GET.urlencode(),
)
return render(request, 'alsijil/week_view.html', context)
return render(request, "alsijil/week_view.html", context)
@login_required
......@@ -170,55 +228,76 @@ def full_register_group(request: HttpRequest, id_: int) -> HttpResponse:
group = get_object_or_404(Group, pk=id_)
# Get all lesson periods for the selected group
lesson_periods = LessonPeriod.objects.filter_group(
group
).distinct().prefetch_related(
'documentations', 'personal_notes'
lesson_periods = (
LessonPeriod.objects.filter_group(group)
.distinct()
.prefetch_related("documentations", "personal_notes")
)
weeks = CalendarWeek.weeks_within(School.objects.first().current_term.date_start, School.objects.first().current_term.date_end)
weeks = CalendarWeek.weeks_within(
School.objects.first().current_term.date_start,
School.objects.first().current_term.date_end,
)
periods_by_day = {}
for lesson_period in lesson_periods:
for week in weeks:
day = week[lesson_period.period.weekday - 1]
if lesson_period.lesson.date_start <= day and lesson_period.lesson.date_end >= day:
documentations = list(filter(lambda d: d.week == week.week, lesson_period.documentations.all()))
notes = list(filter(lambda d: d.week == week.week, lesson_period.personal_notes.all()))
if (
lesson_period.lesson.date_start <= day
and lesson_period.lesson.date_end >= day
):
documentations = list(
filter(
lambda d: d.week == week.week,
lesson_period.documentations.all(),
)
)
notes = list(
filter(
lambda d: d.week == week.week,
lesson_period.personal_notes.all(),
)
)
substitution = lesson_period.get_substitution(week.week)
periods_by_day.setdefault(day, []).append((lesson_period, documentations, notes, substitution))
periods_by_day.setdefault(day, []).append(
(lesson_period, documentations, notes, substitution)
)
persons = group.members.annotate(
absences=Count('personal_notes__absent', filter=Q(
personal_notes__absent=True
)),
unexcused=Count('personal_notes__absent', filter=Q(
personal_notes__absent=True,
personal_notes__excused=False
)),
tardiness=Sum('personal_notes__late')
absences=Count("personal_notes__absent", filter=Q(personal_notes__absent=True)),
unexcused=Count(
"personal_notes__absent",
filter=Q(personal_notes__absent=True, personal_notes__excused=False),
),
tardiness=Sum("personal_notes__late"),
)
# FIXME Move to manager
personal_note_filters = PersonalNoteFilter.objects.all()
for personal_note_filter in personal_note_filters:
persons = persons.annotate(
**{'_personal_notes_with_%s' % personal_note_filter.identifier: Count(
'personal_notes__remarks',
filter=Q(personal_notes__remarks__iregex=personal_note_filter.regex)
)}
**{
"_personal_notes_with_%s"
% personal_note_filter.identifier: Count(
"personal_notes__remarks",
filter=Q(
personal_notes__remarks__iregex=personal_note_filter.regex
),
)
}
)
context['persons'] = persons
context['personal_note_filters'] = personal_note_filters
context['group'] = group
context['weeks'] = weeks
context['periods_by_day'] = periods_by_day
context['today'] = date.today()
context['school'] = School.objects.first()
context["persons"] = persons
context["personal_note_filters"] = personal_note_filters
context["group"] = group
context["weeks"] = weeks
context["periods_by_day"] = periods_by_day
context["today"] = date.today()
context["school"] = School.objects.first()
return render(request, 'alsijil/print/full_register.html', context)
return render(request, "alsijil/print/full_register.html", context)
@admin_required
......@@ -227,30 +306,30 @@ def register_absence(request: HttpRequest) -> HttpResponse:
register_absence_form = RegisterAbsenceForm(request.POST or None)
if request.method == 'POST':
if request.method == "POST":
if register_absence_form.is_valid():
# Get data from form
person = register_absence_form.cleaned_data['person']
start_date = register_absence_form.cleaned_data['date_start']
end_date = register_absence_form.cleaned_data['date_end']
from_period = register_absence_form.cleaned_data['from_period']
absent = register_absence_form.cleaned_data['absent']
excused = register_absence_form.cleaned_data['excused']
remarks = register_absence_form.cleaned_data['remarks']
person = register_absence_form.cleaned_data["person"]
start_date = register_absence_form.cleaned_data["date_start"]
end_date = register_absence_form.cleaned_data["date_end"]
from_period = register_absence_form.cleaned_data["from_period"]
absent = register_absence_form.cleaned_data["absent"]
excused = register_absence_form.cleaned_data["excused"]
remarks = register_absence_form.cleaned_data["remarks"]
# Mark person as absent
delta = end_date - start_date
for i in range(delta.days+1):
for i in range(delta.days + 1):
from_period = from_period if i == 0 else 0
day = start_date + timedelta(days=i)
person.mark_absent(day, from_period, absent, excused, remarks)
messages.success(request, _('The absence has been saved.'))
return redirect('index')
messages.success(request, _("The absence has been saved."))
return redirect("index")
context['register_absence_form'] = register_absence_form
context["register_absence_form"] = register_absence_form
return render(request, 'alsijil/register_absence.html', context)
return render(request, "alsijil/register_absence.html", context)
def list_personal_note_filters(request: HttpRequest) -> HttpResponse:
......@@ -262,33 +341,36 @@ def list_personal_note_filters(request: HttpRequest) -> HttpResponse:
personal_note_filters_table = PersonalNoteFilterTable(personal_note_filters)
RequestConfig(request).configure(personal_note_filters_table)
context['personal_note_filters_table'] = personal_note_filters_table
context["personal_note_filters_table"] = personal_note_filters_table
return render(request, 'alsijil/personal_note_filters.html', context)
return render(request, "alsijil/personal_note_filters.html", context)
def edit_personal_note_filter(request: HttpRequest, id: Optional['int'] = None) -> HttpResponse:
def edit_personal_note_filter(
request: HttpRequest, id: Optional["int"] = None
) -> HttpResponse:
context = {}
if id:
personal_note_filter = PersonalNoteFilter.objects.get(id=id)
context['personal_note_filter'] = personal_note_filter
context["personal_note_filter"] = personal_note_filter
personal_note_filter_form = PersonalNoteFilterForm(
request.POST or None, instance=personal_note_filter)
request.POST or None, instance=personal_note_filter
)
else:
personal_note_filter_form = PersonalNoteFilterForm(
request.POST or None)
if request.method == 'POST':
personal_note_filter_form = PersonalNoteFilterForm(request.POST or None)
if request.method == "POST":
if personal_note_filter_form.is_valid():
personal_note_filter_form.save(commit=True)
messages.success(request, _('The filter has been saved'))
return redirect('list_personal_note_filters')
messages.success(request, _("The filter has been saved"))
return redirect("list_personal_note_filters")
context['personal_note_filter_form'] = personal_note_filter_form
context["personal_note_filter_form"] = personal_note_filter_form
return render(request, "alsijil/manage_personal_note_filter.html", context)
return render(request, 'alsijil/manage_personal_note_filter.html', context)
@admin_required
def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse:
......@@ -297,8 +379,8 @@ def delete_personal_note_filter(request: HttpRequest, id_: int) -> HttpResponse:
personal_note_filter = get_object_or_404(PersonalNoteFilter, pk=id_)
PersonalNoteFilter.objects.filter(pk=id_).delete()
messages.success(request, _('The filter has been deleted.'))
context['personal_note_filter'] = personal_note_filter
return redirect('list_personal_note_filters')
messages.success(request, _("The filter has been deleted."))
context["personal_note_filter"] = personal_note_filter
return redirect("list_personal_note_filters")