school (School management)

A tested document

This is a tested document. The following instructions are used for initialization:

>>> import lino
>>> lino.startup('lino_prima.projects.prima1.settings')
>>> from lino.api.doctest import *

Glossary

enrolment

When a given pupil is member of a given group in a given school year.

Class reference

class lino_prima.lib.school.Group

Django model to represent a group of pupils working together during an academic year (a class).

class lino_prima.lib.school.Enrolment

Django model to represent an enrolment of a given pupil in a given group.

Subjects, groups and courses

>>> rt.show(school.Grades)
... 
==== ============== ================== =========== ====================== =================== ========================
 ID   Designation    Designation (de)   Reference   Certificate template   Rating conditions   Rating conditions (de)
---- -------------- ------------------ ----------- ---------------------- ------------------- ------------------------
 1    First grade    Erstes Jahr        1           Junior
 2    Second grade   Zweites Jahr       2           Junior
 3    Third grade    Drittes Jahr       3           Senior
 4    Fourth grade   Viertes Jahr       4           Senior
 5    Fifth grade    Fünftes Jahr       5           Senior
 6    Sixth grade    Sechstes Jahr      6           Senior
 7    Alumni         Alumni             X
==== ============== ================== =========== ====================== =================== ========================

The lino.mixins.ref.Referrable.get_next_row() method returns the “next” row:

>>> for obj in school.Grade.objects.all():
...     print(f"{obj} --> {obj.get_next_row()}")
First grade --> Second grade
Second grade --> Third grade
Third grade --> Fourth grade
Fourth grade --> Fifth grade
Fifth grade --> Sixth grade
Sixth grade --> None
Alumni --> None

The lino.modlib.periods.StoredYear model has an overridden lino.mixins.ref.Referrable.get_next_row() method:

>>> for obj in periods.StoredYear.objects.all():
...     print(f"{obj} --> {obj.get_next_row()}")
2023/24 --> 2024/25
2024/25 --> 2025/26
2025/26 --> 2026/27
2026/27 --> 2027/28
2027/28 --> 2028/29
2028/29 --> 2029/30
2029/30 --> None
>>> rt.show(school.Subjects)
... 
===== ================= ===================== ========== ====== ============= ==================
 No.   Designation       Designation (de)      Advanced   Icon   Rating type   Image
----- ----------------- --------------------- ---------- ------ ------------- ------------------
 1     Geography         Erdkunde              No
 2     History           Geschichte            No
 3     Natural science   Naturwissenschaften   No
 4     French            Französisch           Yes        🥐                   1f347_grapes.png
 5     Mathematics       Mathematik            Yes        🖩
 6     German            Deutsch               Yes        🥨                   1f34e_apple.png
 7     Religion          Religion              Yes        🕊
 8     Music             Musik                 No         🎜      Predicates
 9     Sport             Sport                 No         ⛹      Predicates    26bd_soccer.png
 10    Art               Kunst                 No         🎨     Smilies
===== ================= ===================== ========== ====== ============= ==================

The MyGroups table shows only groups of the currently opened school year while Groups shows them all:

>>> rt.show(school.MyGroups, display_mode="grid")
... 
============= ================== ============== =============== ====
 Designation   Designation (de)   Grade          Academic year   ID
------------- ------------------ -------------- --------------- ----
 2A                               Second grade   2024/25         15
 2B                               Second grade   2024/25         16
 3A                               Third grade    2024/25         17
 3B                               Third grade    2024/25         18
 3C                               Third grade    2024/25         19
 4A                               Fourth grade   2024/25         20
 4B                               Fourth grade   2024/25         21
 5A                               Fifth grade    2024/25         22
 5B                               Fifth grade    2024/25         23
 5C                               Fifth grade    2024/25         24
 6A                               Sixth grade    2024/25         25
 6B                               Sixth grade    2024/25         26
 7A                                              2024/25         27
 7B                                              2024/25         28
============= ================== ============== =============== ====
>>> rt.show(school.AllGroups)
... 
============= ================== ============== =============== ====
 Designation   Designation (de)   Grade          Academic year   ID
------------- ------------------ -------------- --------------- ----
 1A                               First grade    2023/24         1
 1B                               First grade    2023/24         7
 2A                               Second grade   2023/24         2
 2A                               Second grade   2024/25         15
 2B                               Second grade   2023/24         8
 2B                               Second grade   2024/25         16
 2C                               Second grade   2023/24         13
 3A                               Third grade    2023/24         3
 3A                               Third grade    2024/25         17
 3B                               Third grade    2023/24         9
 3B                               Third grade    2024/25         18
 3C                               Third grade    2024/25         19
 4A                               Fourth grade   2023/24         4
 4A                               Fourth grade   2024/25         20
 4B                               Fourth grade   2023/24         10
 4B                               Fourth grade   2024/25         21
 4C                               Fourth grade   2023/24         14
 5A                               Fifth grade    2023/24         5
 5A                               Fifth grade    2024/25         22
 5B                               Fifth grade    2023/24         11
 5B                               Fifth grade    2024/25         23
 5C                               Fifth grade    2024/25         24
 6A                               Sixth grade    2023/24         6
 6A                               Sixth grade    2024/25         25
 6B                               Sixth grade    2023/24         12
 6B                               Sixth grade    2024/25         26
 7A                                              2024/25         27
 7B                                              2024/25         28
============= ================== ============== =============== ====

Lino automatically creates a course for every subject for which there is a section in the certificate template.

>>> grp = school.Group.objects.filter(designation="5B").last()
>>> rt.show(school.CoursesByGroup, grp)
`⏏ <…>`__ | `Geography <…>`__, `History <…>`__, `Natural science <…>`__, `French <…>`__, `Mathematics <…>`__, `German <…>`__, `Religion <…>`__
>>> grp = school.Group.objects.filter(designation="6A").last()
>>> rt.show(school.CoursesByGroup, grp)
`⏏ <…>`__ | `Geography <…>`__, `History <…>`__, `Natural science <…>`__, `French <…>`__, `Mathematics <…>`__, `German <…>`__, `Religion <…>`__

Don’t read this

Exploring #5835 (Link to detail of an enrolment fails for normal teachers)

This issue was fixed 20250107. Here is how to reproduce it:

  • Sign in as madeleine.carbonez on prima1.

  • Click on “6A” in the “My groups” dashboard item.

  • In the “Projects” panel of 6A, click on the first pupil (Léopold Brouck). Lino opens prima/EnrolmentsByGroup/61, which causes a BadRequest.

  • There is no error when you do the same as robin.

Explanation:

When madeleine (a simple teacher) calls obj2html() on an enrolment, Lino uses another actor than when robin calls it because a simple teacher cannot see all enrolments. That’s normal. Robin has access to Enrolments, Madeleine only to EnrolmentsByGroup. But Madeleine’s link then failed because EnrolmentsByGroup requires a master instance (the group), which Lino didn’t specify. Until 20250107 Lino added mk and mt for specifying the master instance only when the target link was on the same actor as the incoming request.

>>> renderer = settings.SITE.kernel.default_renderer
>>> grp = school.Group.objects.get(designation="6A", year__ref="2023/24")
>>> enr = school.Enrolment.objects.filter(group=grp).first()
>>> grp
Group #6 ('6A (2023/24)')
>>> enr
Enrolment #61 ('Léopold Brouck (6A (2023/24))')
>>> ses = rt.login("robin", show_urls=True, renderer=settings.SITE.kernel.default_renderer)
>>> print(ses.permalink_uris)
None
>>> print(ses.obj2htmls(enr).replace("&quot;", "'"))
<a href="javascript:window.App.runAction({ 'action_full_name': 'school.Enrolments.detail', 'actorId': 'school.Enrolments', 'rp': null, 'status': { 'record_id': 61 } })" style="text-decoration:none">Léopold Brouck (6A (2023/24))</a>
>>> ses = rt.login("abel.adam", show_urls=True, renderer=settings.SITE.kernel.default_renderer)
>>> ar = school.EnrolmentsByGroup.create_request(master_instance=grp, renderer=renderer, user=ses.get_user())
>>> print(ar.obj2url(enr))  
javascript:window.App.runAction({ "action_full_name":
"school.Enrolments.detail", "actorId": "school.EnrolmentsByGroup", "rp": null, "status": {
"base_params": { "mk": 6, "mt": 9 }, "record_id": 61 } })

Note: After fixing the bug, PupilsAndProjectsByGroup changed and finally was replaced by Group.pupils_and_projects.

The ratings.RatingsSummary model has allow_cascaded_copy explicitly set to an empty set because we want Lino to delete these rows in cascade with their master, but we do not want them to get duplicated when the master gets duplicated.

>>> ratings.RatingsSummary.allow_cascaded_delete
{'master'}
>>> ratings.RatingsSummary.allow_cascaded_copy
set()
>>> school.Enrolment.allow_cascaded_delete
{'group'}
>>> school.Enrolment.allow_cascaded_copy
{'group'}