joeni

Add new template and change view

Author
Maarten 'Vngngdn' Vangeneugden
Date
Nov. 23, 2017, 1:11 p.m.
Hash
07e85b805e87d9e192d097a329645ca554d9fd37
Parent
4c969f3a0dea77b4409e9cf91891ddf1c75181db
Modified files
courses/models.py
courses/templates/courses/course.djhtml
courses/templates/courses/index.djhtml
courses/views.py

courses/models.py

5 additions and 0 deletions.

View changes Hide changes
1
1
from django.utils.translation import ugettext_lazy as _
2
2
3
3
class Course(models.Model):
4
4
    """ Represents a course that is taught at the university. """
5
5
    number = models.PositiveSmallIntegerField(
6
6
        primary_key=True,
7
7
        blank=False,
8
8
        help_text=_("The number associated with this course. A leading '0' will be added if the number is smaller than 1000."),
9
9
        )
10
10
    name = models.CharField(
11
11
        max_length=64,
12
12
        blank=False,
13
13
        help_text=_("The name of this course, in the language that it is taught. Translations are for the appropriate template."),
14
14
        )
15
15
    color = models.CharField(
16
16
        max_length=6,
17
17
        blank=False,
18
18
        default=constants.COLORS['uhasselt-default'],
19
19
        help_text=_("The color for this course. Must be an hexadecimal code."),
20
20
        validators=['validate_hex_color'],
21
21
        )
22
22
    slug_name = models.SlugField(
23
23
        blank=False,
24
24
        allow_unicode=True,
25
25
        unique=True,
26
26
        help_text=_("A so-called 'slug name' for this course."),
27
27
        )
28
28
    # TODO: Add a potential thingy magicky to auto fill the slug name on the course name
29
29
    contact_person = models.ForeignKey(
30
30
        "administration.User",
31
31
        on_delete=models.PROTECT,  # A course must have a contact person
32
32
        limit_choices_to={'is_staff': True},
33
33
        null=False,
34
34
        help_text=_("The person to contact regarding this course."),
35
35
        related_name="contact_person",
36
36
        )
37
37
    coordinator = models.ForeignKey(
38
38
        "administration.User",
39
39
        on_delete=models.PROTECT,  # A course must have a coordinator
40
40
        limit_choices_to={'is_staff': True},
41
41
        null=False,
42
42
        help_text=_("The person whom's the coordinator of this course."),
43
43
        related_name="coordinator",
44
44
        )
45
45
    educating_team = models.ManyToManyField(
46
46
        "administration.User",
47
47
        # No on_delete, since M->M cannot be required at database level
48
48
        limit_choices_to={'is_staff': True},
49
49
        #null=False,  # Useless on a M->M
50
50
        help_text=_("The team members of this course."),
51
51
        related_name="educating_team",
52
52
        )
53
53
    language = models.CharField(
54
54
        max_length=64,
55
55
        choices = (
56
56
            ('NL', _("Dutch")),
57
57
            ('EN', _("English")),
58
58
            ('FR', _("French")),
59
59
            ),
60
60
        null=False,
61
61
        help_text=_("The language in which this course is given."),
62
62
        )
63
63
64
64
    def __str__(self):
65
65
        number = str(self.number)
66
66
        for i in [10,100,1000]:
67
67
            if self.number < i:
68
68
                number = "0" + number
69
69
        return "(" + number + ") " + self.name
70
70
71
71
72
72
class Prerequisite(models.Model):
73
73
    """ Represents a collection of prerequisites a student must have obtained
74
74
    before being allowed to partake in this course.
75
75
    It's possible that, if a student has obtained credits in a certain set of
76
76
    courses, a certain part of the prerequisites do not have to be obtained.
77
77
    Because of this, make a different record for each different set. In other
78
78
    words: If one set of prerequisites is obtained, and another one isn't, BUT
79
79
    they point to the same course, the student is allowed to partake. """
80
80
    course = models.ForeignKey(
81
81
        "Course",
82
82
        on_delete=models.CASCADE,
83
83
        null=False,
84
84
        help_text=_("The course that these prerequisites are for."),
85
85
        related_name="prerequisite_course",
86
86
        )
87
87
    name = models.CharField(
88
88
        max_length=64,
89
89
        blank=True,
90
90
        help_text=_("To specify a name for this set, if necessary."),
91
91
        )
92
92
    sequentialities = models.ManyToManyField(
93
93
        "Course",
94
94
        help_text=_("All courses for which a credit must've been received in order to follow the course."),
95
95
        related_name="sequentialities",
96
96
        )
97
97
    in_curriculum = models.ManyToManyField(
98
98
        "Course",
99
99
        help_text=_("All courses that have to be in the curriculum to follow this. If a credit was achieved, that course can be omitted."),
100
100
        related_name="in_curriculum",
101
101
        )
102
102
    required_study = models.ForeignKey(
103
103
        "Study",
104
104
        on_delete=models.CASCADE,
105
105
        null=True,
106
106
        help_text=_("If one must have a certain amount of obtained ECTS points for a particular course, state that course here."),
107
107
        )
108
108
    ECTS_for_required_study = models.PositiveSmallIntegerField(
109
109
        null=True,
110
110
        help_text=_("The amount of obtained ECTS points for the required course, if any."),
111
111
        )
112
112
113
113
    def __str__(self):
114
114
        if self.name == "":
115
115
            return _("Prerequisites for %(course)s") % {'course': str(self.course)}
116
116
        else:
117
117
            return self.name + " | " + str(self.course)
118
118
119
119
120
120
class CourseProgramme(models.Model):
121
121
    """ It's possible that a course is taught in multiple degree programmes; For
122
122
    example: Calculus can easily be taught to physics and mathematics students
123
123
    alike. In this table, these relations are set up, and the related properties
124
124
    are defined as well. """
125
125
    study = models.ForeignKey(
126
126
        "Study",
127
127
        on_delete=models.CASCADE,
128
128
        null=False,
129
129
        help_text=_("The study in which the course is taught."),
130
130
        )
131
131
    course = models.ForeignKey(
132
132
        "Course",
133
133
        on_delete=models.CASCADE,
134
134
        null=False,
135
135
        help_text=_("The course that this programme is for."),
136
136
        )
137
137
    study_programme = models.ForeignKey(
138
138
        "StudyProgramme",
139
139
        on_delete=models.CASCADE,
140
140
        null=False,
141
141
        help_text=_("The study programme that this course belongs to."),
142
142
        )
143
143
    programme_type = models.CharField(
144
144
        max_length=1,
145
145
        blank=False,
146
146
        choices = (
147
147
            ('C', _("Compulsory")),
148
148
            ('O', _("Optional")),
149
149
            ),
150
150
        help_text=_("Type of this course for this study."),
151
151
        )
152
152
    study_hours = models.PositiveSmallIntegerField(
153
153
        blank=False,
154
154
        help_text=_("The required amount of hours to study this course."),
155
155
        )
156
156
    ECTS = models.PositiveSmallIntegerField(
157
157
        blank=False,
158
158
        help_text=_("The amount of ECTS points attached to this course."),
159
159
        )
160
160
    semester = models.PositiveSmallIntegerField(
161
161
        blank=False,
162
162
        choices = (
163
163
            (1, _("First semester")),
164
164
            (2, _("Second semester")),
165
165
            (3, _("Full year course")),
166
166
            (4, _("Taught in first quarter")),
167
167
            (5, _("Taught in second quarter")),
168
168
            (6, _("Taught in third quarter")),
169
169
            (7, _("Taught in fourth quarter")),
170
170
            ),
171
171
        help_text=_("The period in which this course is being taught in this study."),
172
172
        )
173
173
    year = models.PositiveSmallIntegerField(
174
174
        blank=False,
175
175
        help_text=_("The year in which this course is taught for this study."),
176
176
        )
177
177
    second_chance = models.BooleanField(
178
178
        default=True,
179
179
        help_text=_("Defines if a second chance exam is planned for this course."),
180
180
        )
181
181
    tolerable = models.BooleanField(
182
182
        default=True,
183
183
        help_text=_("Defines if a failed result can be tolerated."),
184
184
        )
185
185
    scoring = models.CharField(
186
186
        max_length=2,
187
187
        choices = (
188
188
            ('N', _("Numerical")),
189
189
            ('FP', _("Fail/Pass")),
190
190
            ),
191
191
        default='N',
192
192
        blank=False,
193
193
        help_text=_("How the obtained score for this course is given."),
194
194
        )
195
195
196
196
    def __str__(self):
197
197
        return str(self.study) + " - " + str(self.course)
198
198
199
199
class Study(models.Model):
200
200
    """ Defines a certain study that can be followed at the university.
201
201
    This also includes abridged study programmes, like transition programmes.
202
202
    Other information, such as descriptions, are kept in the template file
203
203
    of this study, which can be manually edited. Joeni searches for a file
204
204
    with the exact name as the study + ".html". So if the study is called
205
205
    "Bachelor of Informatics", it will search for "Bachelor of Informatics.html".
206
206
    """
207
207
    # Degree types
208
208
    BSc = _("Bachelor of Science")
209
209
    MSc = _("Master of Science")
210
210
    LLB = _("Bachelor of Laws")
211
211
    LLM = _("Master of Laws")
212
212
    ir  = _("Engineer")
213
213
    ing = _("Technological Engineer")
214
214
    # Faculties
215
215
    FoMaLS = _("Faculty of Medicine and Life Sciences")
216
216
    FoS    = _("Faculty of Sciences")
217
217
    FoTS   = _("Faculty of Transportation Sciences")
218
218
    FoAaA  = _("Faculty of Architecture and Arts")
219
219
    FoBE   = _("Faculty of Business Economics")
220
220
    FoET   = _("Faculty of Engineering Technology")
221
221
    FoL    = _("Faculty of Law")
222
222
223
223
    name = models.CharField(
224
224
        max_length=128,
225
225
        blank=False,
226
226
        unique=True,
227
227
        help_text=_("The full name of this study, in the language it's taught in."),
228
228
        )
229
229
    degree_type = models.CharField(
230
230
        max_length=64,
231
231
        choices = (
232
232
            ('BSc', BSc),
233
233
            ('MSc', MSc),
234
234
            ('LL.B', LLB),
235
235
            ('LL.M', LLM),
236
236
            ('ir.', ir ),
237
237
            ('ing.',ing),
238
238
            ),
239
239
        blank=False,
240
240
        help_text=_("The type of degree one obtains upon passing this study."),
241
241
        )
242
242
    language = models.CharField(
243
243
        max_length=64,
244
244
        choices = (
245
245
            ('NL', _("Dutch")),
246
246
            ('EN', _("English")),
247
247
            ('FR', _("French")),
248
248
            ),
249
249
        null=False,
250
250
        help_text=_("The language in which this study is given."),
251
251
        )
252
252
    # Information about exam committee
253
253
    chairman = models.ForeignKey(
254
254
        "administration.User",
255
255
        on_delete=models.PROTECT,
256
256
        null=False,
257
257
        limit_choices_to={'is_staff': True},
258
258
        help_text=_("The chairman of this study."),
259
259
        related_name="chairman",
260
260
        )
261
261
    vice_chairman = models.ForeignKey(
262
262
        "administration.User",
263
263
        on_delete=models.PROTECT,
264
264
        null=False,
265
265
        help_text=_("The vice-chairman of this study."),
266
266
        limit_choices_to={'is_staff': True},
267
267
        related_name="vice_chairman",
268
268
        )
269
269
    secretary = models.ForeignKey(
270
270
        "administration.User",
271
271
        on_delete=models.PROTECT,
272
272
        null=False,
273
273
        help_text=_("The secretary of this study."),
274
274
        limit_choices_to={'is_staff': True},
275
275
        related_name="secretary",
276
276
        )
277
277
    ombuds = models.ForeignKey(
278
278
        "administration.User",
279
279
        on_delete=models.PROTECT,
280
280
        null=False,
281
281
        help_text=_("The ombuds person of this study."),
282
282
        limit_choices_to={'is_staff': True},
283
283
        related_name="ombuds",
284
284
        )
285
285
    vice_ombuds = models.ForeignKey(
286
286
        "administration.User",
287
287
        on_delete=models.PROTECT,
288
288
        null=False,
289
289
        help_text=_("The (replacing) ombuds person of this study."),
290
290
        limit_choices_to={'is_staff': True},
291
291
        related_name="vice_ombuds",
292
292
        )
293
293
    additional_members = models.ManyToManyField(
294
294
        "administration.User",
295
295
        help_text=_("All the other members of the exam committee."),
296
296
        limit_choices_to={'is_staff': True},
297
297
        related_name="additional_members",
298
298
        )
299
299
    faculty = models.CharField(
300
300
        max_length=6,
301
301
        choices = (
302
302
            ('FoS', FoS),
303
303
            ('FoTS', FoTS),
304
304
            ('FoAaA', FoAaA),
305
305
            ('FoBE', FoBE),
306
306
            ('FoMaLS', FoMaLS),
307
307
            ('FoET', FoET),
308
308
            ('FoL', FoL),
309
309
            ),
310
310
        blank=False,
311
311
        help_text=_("The faculty where this study belongs to."),
312
312
        )
313
313
314
314
    #def study_points(self):
315
315
    """ Returns the amount of study points for this year.
316
316
        This value is inferred based on the study programme information
317
317
        records that lists this study as their foreign key. """
318
318
        #total_ECTS = 0
319
319
        #for course in CourseProgramme.objects.filter(study=self):
320
320
            #total_ECTS += course.ECTS
321
321
        #return total_ECTS
322
322
    # XXX: Commented because this is actually something for the StudyProgramme
323
323
    def years(self):
324
324
        """ Returns the amount of years this study takes.
325
325
        This value is inferred based on the study programme information
326
326
        records that lists this study as their foreign key. """
327
327
        highest_year = 0
328
328
        for course in CourseProgramme.objects.filter(study=self):
329
329
            highest_year = max(highest_year, course.year)
330
330
        return highest_year
331
331
332
332
    def students(self):
333
333
        """ Cross references the information stored in the database, and
334
334
        returns all the students that are following this study in this
335
335
        academic year. """
336
336
        return 0  # TODO
337
337
338
338
339
339
    def __str__(self):
340
340
        return self.name
341
341
342
342
class StudyProgramme(models.Model):
343
343
    """ Represents a programme within a certain study.
344
344
    A good example for this is the different specializations, minors, majors, ...
345
345
    one can follow within the same study. Nevertheless, they're all made of
346
346
    a certain set of courses. This table collects all these, and allows one to name
347
347
    them, so they're distinct from one another. """
348
348
    name = models.CharField(
349
349
            max_length=64,
350
350
            blank=False,
351
351
            help_text=_("The name of this programme."),
352
352
            )
353
353
354
354
    def courses(self):
355
355
        """ All courses that are part of this study programme. """
356
356
        programmes = CourseProgramme.objects.filter(study_programme=self)
357
357
        courses = {}
358
358
        for program in programmes:
359
359
            courses.add(program.course)
360
360
        return courses
361
361
362
362
    def study_points(self, year=None):
363
363
        """ Returns the amount of study points this programme contains.
364
364
        Accepts year as an optional argument. If not given, the study points
365
365
        of all years are returned. """
366
366
        programmes = CourseProgramme.objects.filter(study_programme=self)
367
367
        ECTS = 0
368
368
        for program in programmes:
369
369
            if year is None or program.year == year:
370
370
                # XXX: This only works if the used implementation does lazy
371
371
                # evaluation, otherwise this is a type error!
372
372
                ECTS += program.ECTS
373
373
        return ECTS
374
374
375
375
    def __str__(self):
376
376
        return self.name
377
377
378
378
# Tables about things related to the courses:
379
379
380
380
class Assignment(models.Model):
381
381
    """ For courses, it's possible to set up tasks. These tasks are recorded
382
382
    here. """
383
383
    # TODO: Require that only the course team can create assignments for a team.
384
384
    course = models.ForeignKey(
385
385
        "Course",
386
386
        on_delete=models.CASCADE,
387
387
        null=False,
388
388
        #editable=False,
389
389
        db_index=True,
390
390
        help_text=_("The course for which this task is assigned."),
391
391
        )
392
392
    information = models.TextField(
+
393
        max_length=32,
+
394
        blank=False,
+
395
        help_text=_("The title of this assignment."),
+
396
        )
+
397
    information = models.TextField(
393
398
        help_text=_("Any additional information regarding the assignment. Orgmode syntax available."),
394
399
        )
395
400
    deadline = models.DateTimeField(
396
401
        null=False,
397
402
        help_text=_("The date and time this task is due."),
398
403
        )
399
404
    posted = models.DateField(auto_now_add=True)
400
405
    digital_task = models.BooleanField(
401
406
        default=True,
402
407
        help_text=_("This determines whether this assignment requires handing "
403
408
                    "in a digital file."),
404
409
        )
405
410
406
411
    def __str__(self):
407
412
        return str(self.course) +" | "+ str(self.posted)
408
413
409
414
class Announcement(models.Model):
410
415
    """ Courses sometimes have to make announcements for the students. """
411
416
    course = models.ForeignKey(
412
417
        "Course",
413
418
        on_delete=models.CASCADE,
414
419
        null=False,
415
420
        #editable=False,
416
421
        db_index=True,
417
422
        help_text=_("The course for which this announcement is made."),
418
423
        )
419
424
    title = models.CharField(
420
425
        max_length=20,  # Keep It Short & Simple®
421
426
        help_text=_("A quick title for what this is about."),
422
427
        )
423
428
    text = models.TextField(
424
429
        blank=False,
425
430
        help_text=_("The announcement itself. Orgmode syntax available."),
426
431
        )
427
432
    posted = models.DateTimeField(auto_now_add=True)
428
433
429
434
    def __str__(self):
430
435
        return str(self.course) +" | "+ self.posted.strftime("%m/%d")
431
436
432
437
class Upload(models.Model):
433
438
    """ For certain assignments, digital hand-ins may be required. These hand
434
439
    ins are recorded per student in this table. """
435
440
    assignment = models.ForeignKey(
436
441
        "Assignment",
437
442
        on_delete=models.CASCADE,
438
443
        null=False,
439
444
        #editable=False,
440
445
        db_index=True,
441
446
        limit_choices_to={"digital_task": True},
442
447
        help_text=_("For which assignment this upload is."),
443
448
        )
444
449
    # TODO: Try to find a way to require that, if the upload is made,
445
450
    # only students that have this course in their curriculum can upload.
446
451
    student = models.ForeignKey(
447
452
        "administration.User",
448
453
        on_delete=models.CASCADE,
449
454
        null=False,
450
455
        #editable=False,
451
456
        limit_choices_to={"is_student": True},
452
457
        help_text=_("The student who handed this in."),
453
458
        )
454
459
    upload_time = models.DateTimeField(auto_now_add=True)
455
460
    comment = models.TextField(
456
461
        blank=True,
457
462
        help_text=_("If you wish to add an additional comment, state it here."),
458
463
        )
459
464
    file = models.FileField(
460
465
        upload_to="assignments/uploads/%Y/%m/",
461
466
        null=False,
462
467
        #editable=False,
463
468
        help_text=_("The file you want to upload for this assignment."),
464
469
        )
465
470
466
471
467
472
    def __str__(self):
468
473
        deadline = self.assignment.deadline
469
474
        if deadline < self.upload_time:
470
475
            return str(self.assignment.course) +" | "+ str(self.student.number) + _("(OVERDUE)")
471
476
        else:
472
477
            return str(self.assignment.course) +" | "+ str(self.student.number)
473
478
474
479
def item_upload_directory(instance, filename):
475
480
    return "courses/" + instance.course.slug_name + "/"
476
481
class CourseItem(models.Model):
477
482
    """ Reprensents study material for a course that is being shared by the
478
483
    course's education team. """
479
484
    course = models.ForeignKey(
480
485
        Course,
481
486
        on_delete=models.CASCADE,
482
487
        null=False,
483
488
        #editable=False,
484
489
        )
485
490
    file = models.FileField(
486
491
        upload_to=item_upload_directory,
487
492
        null=False,
488
493
        #editable=False,
489
494
        help_text=_("The file you wish to upload."),
490
495
        )
491
496
    timestamp = models.DateTimeField(auto_now_add=True)
492
497
    note = models.TextField(
493
498
        blank=True,
494
499
        help_text=_("If you want to state some additional information about "
495
500
                    "this upload, state it here."),
496
501
        )
497
502
498
503
class StudyGroup(models.Model):
499
504
    """ It may be necessary to make study groups regarding a course. These
500
505
    are recorded here, and blend in seamlessly with the Groups from Agora.
501
506
    Groups that are recorded as a StudyGroup, are given official course status,
502
507
    and thus, cannot be removed until the status of StudyGroup is lifted. """
503
508
    course = models.ForeignKey(
504
509
        "Course",
505
510
        on_delete=models.CASCADE,
506
511
        null=False,
507
512
        #editable=False,
508
513
        db_index=True,
509
514
        help_text=_("The course for which this group is."),
510
515
        )
511
516
    group = models.ForeignKey(
512
517
        "agora.Group",
513
518
        on_delete=models.PROTECT,  # See class documentation
514
519
        null=False,
515
520
        #editable=False,  # Keep the same group
516
521
        help_text=_("The group that will be seen as the study group."),
517
522
        )
518
523
519
524
    def __str__(self):
520
525
        return str(self.course) +" | "+ str(self.group)
521
526

courses/templates/courses/course.djhtml

77 additions and 0 deletions.

View changes Hide changes
+
1
{% load static %}
+
2
{% load i18n %}
+
3
{% load humanize %}
+
4
{% load crispy_forms_tags %}
+
5
+
6
{% block title %}
+
7
    {{ course.name }} | ◀ Joeni /▶
+
8
{% endblock %}
+
9
+
10
{% block main %}
+
11
    

{{ course.name }}

+
12
+
13
    

{% trans "Announcements" %}

+
14
    {% for announcement in announcements %}
+
15
        

{{ announcement.title}}

+
16
        
+
17
            {{ announcent.posted|naturaltime }}
+
18
        
+
19
        

{{ annoucement.text }}

+
20
    {% empty %}
+
21
        {% trans "No announcements have been made for this course." %}
+
22
    {% endfor %}
+
23
+
24
    

{% trans "Assignments" %}

+
25
    {% for assignment in assignments %}
+
26
        

{{ assignment.title }}

+
27
        {% if assignment.information %}
+
28
            

{{ assignment.information }}

+
29
        {% endif %}
+
30
        {% trans "Posted" %}: {{ assignment.posted|naturaltime }}
+
31
        {% if assignment.digital_task %}
+
32
            

{% trans "Your uploads" %}

+
33
            {% for upload in uploads %}
+
34
                {% if upload.assignment ==  assignment %}
+
35
                    {% trans "Uploaded:"%} {{ upload.upload_time|date:"SHORT_DATETIME_FORMAT" }}
+
36
                    {% if upload.comment %}
+
37
                        

{{ upload.comment }}

+
38
                    {% endif %}
+
39
                    {% if upload.upload_time > assignment.deadline %}
+
40
                        {% trans "This upload is overdue." %}
+
41
                    {% endif %}
+
42
                {% endif %}
+
43
            {% empty %}
+
44
                {% now as current_time %}
+
45
                {% if now > assignment.deadline %}
+
46
                    

+
47
                        
+
48
                            {% blocktrans %}
+
49
                                You have failed to provide an upload for this
+
50
                                assignment. Any future uploads will be automatically
+
51
                                overdue.
+
52
                            {% endblocktrans %}
+
53
                        
+
54
                    

+
55
                {% else %}
+
56
                    

+
57
                        {% blocktrans %}
+
58
                            You haven't uploaded anything for this assignment
+
59
                            yet.
+
60
                        {% endblocktrans %}
+
61
                    

+
62
                {% endif %}
+
63
            {% endfor %}
+
64
            
{% trans "Upload a task" %}
+
65
            {% crispy assignment-upload-form %}
+
66
        {% endif %}
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77

courses/templates/courses/index.djhtml

1 addition and 1 deletion.

View changes Hide changes
1
1
{% load i18n %}
2
2
3
3
{% block title %}
4
4
    {% trans "Joeni | Courses" %}
5
-
{% endblock %}
+
5
{% endblock %}
6
6
7
7
{% block main %}
8
8
    

{% trans "Your courses" %}

9
9
    
    10
    10
            {% for course in courses %}
    11
    11
                
  • 12
    12
                    
    13
    13
                        {{ course.name|lower|capfirst }}
    14
    14
                        {% if course.number < 10 %}(000{{ course.number }})
    15
    15
                        {% elif course.number < 100 %}(00{{ course.number }})
    16
    16
                        {% elif course.number < 1000 %}(0{{ course.number }})
    17
    17
                        {% else %}({{ course.number }})
    18
    18
                    
    19
    19
                
    20
    20
            {% endfor %}
    21
    21
        
    22
    22
    {% endblock main %}
    23
    23

    courses/views.py

    28 additions and 0 deletions.

    View changes Hide changes
    1
    1
    import datetime
    2
    2
    from django.core.urlresolvers import reverse # Why?
    3
    3
    from django.utils.translation import ugettext as _
    4
    4
    from .models import *
    5
    5
    import joeni.administration
    6
    6
    7
    7
    def current_academic_year():
    8
    8
        """ Returns the current academic year. The year is determined as follows:
    9
    9
        - If today is before September 15 of the current year, the returned value
    10
    10
          is the current year - 1.
    11
    11
        - If today is after September 15 of the current year, but before January 1
    12
    12
          of the next year, it returns the current year as is.
    13
    13
        """
    14
    14
        today = datetime.datetime.now()
    15
    15
        switch = datetime.datetime.date(datetime.datetime.year, 9, 15)
    16
    16
        if today < switch:
    17
    17
            return today.year - 1
    18
    18
        else:
    19
    19
            return today.year
    20
    20
    21
    21
    @login_required
    22
    22
    def index(request):
    23
    23
        """ Starting page regarding the courses. This serves two specific groups:
    24
    24
        - Students: Displays all courses that this student has in his/her curriculum
    25
    25
                    for this academic year. Requires the curriculum to be accepted.
    26
    26
        - Staff: Displays all courses in which the staff member is part of the
    27
    27
                 educating team, or is otherwise related to the course.
    28
    28
        Users who are not logged in will be sent to the login page.
    29
    29
        """
    30
    30
        template = "courses/index.djhtml"
    31
    31
        courses = set()
    32
    32
        if request.user.is_student:
    33
    33
            curricula = administration.models.Curriculum.objects.filter(student=request.user)
    34
    34
            current_curriculum = curricula.filter(year__year=current_academic_year())
    35
    35
            courses = current_curriculum.courses
    36
    36
        elif request.user.is_staff:
    37
    37
            courses += adminstration.models.Course.filter(contact_person=request.user)
    38
    38
            courses += adminstration.models.Course.filter(coordinator=request.user)
    39
    39
            courses += adminstration.models.Course.filter(educating_team__contains=request.user)
    40
    40
        else:
    41
    41
            raise django.exceptions.FieldError("User "+request.user.number+" is neither staff nor student")
    42
    42
    43
    43
        context = {
    44
    44
            'courses': courses,
    45
    45
            }
    46
    46
    47
    47
        return render(request, template, context)
    48
    48
    +
    49
    @login_required
    +
    50
    def course(request, course_slug):
    +
    51
        template = "courses/course.djhtml"
    +
    52
        course = Course.objects.get(slug_name=course_slug)
    +
    53
    +
    54
        # Check if user can see this page
    +
    55
        if request.user.is_student:
    +
    56
            curricula = administration.models.Curriculum.objects.filter(student=request.user)
    +
    57
            current_curriculum = curricula.filter(year__year=current_academic_year())
    +
    58
            if course not in current_curriculum.courses:
    +
    59
                """ I'm currently just redirecting to the index page, but maybe it's
    +
    60
                just as good to make an announcement that this course cannot be
    +
    61
                used by this user. """
    +
    62
                return index(request)
    +
    63
    +
    64
    +
    65
    +
    66
        context = {
    +
    67
            'course': course,
    +
    68
            'announcements': Announcement.objects.filter(course=course),
    +
    69
            'assignments': Assignment.objects.filter(course=course),
    +
    70
            'course-items': CourseItem.objects.filter(course=course),
    +
    71
            'study-groups': StudyGroup.objects.filter(course=course),
    +
    72
            'uploads': Upload.objects.filter(course=course).filter(student=request.user)
    +
    73
            }
    +
    74
    +
    75
        return render(request, template, context)
    +
    76