joeni

Add attendance form page

Author
Maarten Vangeneugden
Date
Aug. 24, 2018, 4:09 a.m.
Hash
3571706c34739595c87d79818f1a79b60f751911
Parent
1968b80302e3b9f87ccdaa1b9cebd8fe6eb8b09e
Modified files
courses/templates/courses/attendance.djhtml
courses/templates/courses/course.djhtml
courses/urls.py
courses/views.py
static/css/tables.scss

courses/templates/courses/attendance.djhtml

27 additions and 0 deletions.

View changes Hide changes
+
1
{% load static %}
+
2
{% load i18n %}
+
3
{% load humanize %}
+
4
{% load joeni_org %}
+
5
+
6
{% block title %}
+
7
    {{ course.name }} | {{ block.super }}
+
8
{% endblock %}
+
9
+
10
{% block main %}
+
11
    <h1>{{ course.name }} - {% trans "Student attendance form" %}</h1>
+
12
    <table class="attendance">
+
13
        <tr>
+
14
            <th>{% trans "Student number" %}</th>
+
15
            <th>{% trans "Student name" %}</th>
+
16
            <th>{% trans "Signature" %}</th>
+
17
        </tr>
+
18
            {% for student in student_list %}
+
19
                <tr>
+
20
                <td>{{ student.student.number }}</td>
+
21
                <td>{{ student.student }}</td>
+
22
                <td></td>
+
23
        </tr>
+
24
        {% endfor %}
+
25
        </table>
+
26
{% endblock main %}
+
27

courses/templates/courses/course.djhtml

3 additions and 0 deletions.

View changes Hide changes
1
1
{% load static %}
2
2
{% load i18n %}
3
3
{% load humanize %}
4
4
{% load joeni_org %}
5
5
6
6
{% block title %}
7
7
    {{ course.name }} | {{ block.super }}
8
8
{% endblock %}
9
9
10
10
{% block main %}
11
11
    <h1>{{ course.name }}</h1>
12
12
13
13
    <h2 id="{% trans "announcements" %}">{% trans "Announcements" %}</h2>
14
14
    <div class="flex-container">
15
15
    {% for announcement in announcements %}
16
16
        <div style="border-color: #{{ course.color }};" class="flex-item">
17
17
            <h3 id="{{ announcement.title|slugify }}">{{ announcement.title }}</h3>
18
18
            <time datetime="{{ announcement.posted|date:'c' }}">
19
19
                {% trans "Posted:" %} {{ announcement.posted|naturaltime }}
20
20
            </time>
21
21
            <p>{{ announcement.text|org }}</p>
22
22
        </div>
23
23
    {% empty %}
24
24
        {% trans "No announcements have been made for this course." %}
25
25
    {% endfor %}
26
26
    </div>
27
27
28
28
29
29
    <h2 id="{% trans "course-items" %}">{% trans "Course items" %}</h2>
30
30
    <div class="flex-container">
31
31
    {% for item in course_items %}
32
32
        <div style="border-color: #{{ course.color }};" class="flex-item">
33
33
            <a href="{{ item.file.url }}" download>{{ item.canonical }}</a><br />
34
34
            <time datetime="{{ item.timestamp|date:'c' }}">
35
35
                {% trans "Posted:" %} {{ item.timestamp|naturaltime }}
36
36
            </time>
37
37
            {% if item.note %}
38
38
            <p>{{ item.note|org }}</p>
39
39
            {% endif %}
40
40
        </div>
41
41
    {% empty %}
42
42
        {% trans "There is no course material available for this course." %}
43
43
    {% endfor %}
44
44
    </div>
45
45
46
46
47
47
    <h2 id="{% trans "assignments" %}">{% trans "Assignments" %}</h2>
48
48
    <div class="flex-container">
49
49
    {% for assignment in assignments %}
50
50
        <div style="border-color: #{{ course.color }};" class="flex-item">
51
51
            <h3 id="{{ assignment.title|slugify }}">{{ assignment.title }}</h3>
52
52
            <time datetime="{{ assignment.posted|date:'c' }}">
53
53
                {% trans "Posted:" %} {{ assignment.posted|date:"DATE_FORMAT" }} {# {{ assignment.posted|naturaltime }}#}
54
54
            </time><br />
55
55
            <time datetime="{{ assignment.deadline|date:'c' }}">
56
56
                {% trans "Deadline:" %} {{ assignment.deadline|date:"DATE_FORMAT" }}
57
57
            </time>
58
58
59
59
            {% if assignment.information %}
60
60
                <p>{{ assignment.information|org }}</p>
61
61
            {% endif %}
62
62
            {#{% trans "Posted" %}: {{ assignment.posted|date:"DATE_FORMAT" }}#}
63
63
            {% if assignment.digital_task %}
64
64
                <h4>{% trans "Your uploads" %}</h4>
65
65
                {% for upload in uploads %}
66
66
                    {% if upload.assignment ==  assignment %}
67
67
                        {% trans "Uploaded:"%} {{ upload.upload_time|date:"SHORT_DATETIME_FORMAT" }}<br />
68
68
                        {% if upload.comment %}
69
69
                            <p>{{ upload.comment }}</p>
70
70
                        {% endif %}
71
71
                        {% if upload.upload_time > assignment.deadline %}
72
72
                            <strong>{% trans "This upload is overdue." %}</strong>
73
73
                        {% endif %}
74
74
                    {% endif %}
75
75
                {% empty %}
76
76
                    {% with now as current_time %}
77
77
                    {% if current_time > assignment.deadline %}
78
78
                        <p>
79
79
                            <strong>
80
80
                                {% blocktrans %}
81
81
                                    You have failed to provide an upload for this
82
82
                                    assignment. Any future uploads will be automatically
83
83
                                    overdue.
84
84
                                {% endblocktrans %}
85
85
                            </strong>
86
86
                        </p>
87
87
                    {% else %}
88
88
                        <p>
89
89
                            {% blocktrans %}
90
90
                                You haven't uploaded anything for this assignment
91
91
                                yet.
92
92
                            {% endblocktrans %}
93
93
                        </p>
94
94
                    {% endif %}
95
95
                    {% endwith %}
96
96
                {% endfor %}
97
97
                <h5>{% trans "Upload a task" %}</h5>
98
98
                <form enctype="multipart/form-data" action="{% url "courses-course-index" course.slug_name %}" method="post">
99
99
                    {% csrf_token %} {# todo i don't think that's necessary here #}
100
100
                    {% include "joeni/form.djhtml" with form=upload_form %}
101
101
                    {# TODO Enable submit button when this works #}
102
102
                    <!--<input type="submit" value="{% trans "Submit" %}" />-->
103
103
                </form>
104
104
            {% endif %}
105
105
        </div>
106
106
    {% endfor %}
107
107
    </div>
108
108
    <h1 id="{% trans "management" %}">{% trans "Course management" %}</h1>
109
109
    <style>
110
110
        a.btn {
111
111
        color: #{{ course.color }};
112
112
        border-color: #{{ course.color }};
113
113
        }
114
114
        a.btn:hover {
115
115
        color: white;
116
116
        background-color: #{{ course.color }};
117
117
        }
118
118
    </style>
119
119
    <a class="btn" href="{% url "courses-eci" course_slug=course.slug_name %}">
120
120
        {% trans "Edit course page" %}
121
121
    </a>
122
122
    <a class="btn" href="{% url "courses-results" course_slug=course.slug_name %}">
123
123
        {% trans "Student results" %}
124
124
    </a>
125
125
    <h2 id="{% trans "students" %}">{% trans "Students" %}</h2>
+
126
        {% trans "Student attendance form" %}
+
127
    </a>
+
128
    <h2 id="{% trans "students" %}">{% trans "Students" %}</h2>
126
129
    <table>
127
130
        <tr>
128
131
            <th>{% trans "Student name" %}</th>
129
132
            <th>{% trans "Student number" %}</th>
130
133
            <th>{% trans "First result" %}</th>
131
134
            <th>{% trans "Second result" %}</th>
132
135
            <th>{% trans "Decision" %}</th>
133
136
        </tr>
134
137
            {% for student in student_list %}
135
138
                <tr>
136
139
                <td>{{ student.student }}</td>
137
140
                <td>{{ student.student.number }}</td>
138
141
                <td>{{ student.first_score|default_if_none:"-" }}</td>
139
142
                <td>{{ student.second_score|default_if_none:"-" }}</td>
140
143
                <td>{% with result=student.result %}
141
144
                    {% if result == "CRED" or result == "VRST" or result == "TLRD" or result == "ITLR"%}
142
145
                        <span style="color:green;">
143
146
                    {% elif result == "FAIL" %}
144
147
                        <span style="color:red;">
145
148
                    {% elif result == "BDRG" %}
146
149
                        <span style="background-color:red; color:white;">
147
150
                    {% elif result == "STOP" %}
148
151
                        <span style="color:black;">
149
152
                    {% endif %}
150
153
                    {{ student.get_result_display }}</span>
151
154
                {% endwith %}</td>
152
155
        </tr>
153
156
        {% endfor %}
154
157
        </table>
155
158
{% endblock main %}
156
159

courses/urls.py

1 addition and 0 deletions.

View changes Hide changes
1
1
from . import views
2
2
from django.utils.translation import ugettext_lazy as _
3
3
4
4
urlpatterns = [
5
5
    path('', views.index, name='courses-index'),
6
6
    path('<slug:course_slug>', views.course, name='courses-course-index'),
7
7
    path(_('<slug:course_slug>/<int:assignment_id>/upload'), views.upload, name='courses-upload'),
8
8
    path(_('<slug:course_slug>/new-item'), views.new_item, name='courses-new-item'),
9
9
    path(_('<slug:course_slug>/groups'), views.groups, name='courses-groups'),
10
10
    path(_('<slug:course_slug>/edit-course-items'), views.edit_course_items, name='courses-eci'),
11
11
    path(_('<slug:course_slug>/course-results'), views.course_results, name='courses-results'),
12
12
    ]
+
13
    ]
13
14

courses/views.py

9 additions and 0 deletions.

View changes Hide changes
1
1
import datetime
2
2
from django.urls import reverse # Why?
3
3
from django.utils.translation import gettext as _
4
4
from .models import *
5
5
from .forms import *
6
6
import administration
7
7
from django.contrib.auth.decorators import login_required
8
8
from joeni.constants import current_academic_year
9
9
10
10
@login_required
11
11
def index(request):
12
12
    """ Starting page regarding the courses. This serves two specific groups:
13
13
    - Students: Displays all courses that this student has in his/her curriculum
14
14
                for this academic year. Requires the curriculum to be accepted.
15
15
    - Staff: Displays all courses in which the staff member is part of the
16
16
             educating team, or is otherwise related to the course.
17
17
    Users who are not logged in will be sent to the login page.
18
18
    """
19
19
    template = "courses/index.djhtml"
20
20
    courses = request.user.user_data.current_courses()
21
21
22
22
    context = {
23
23
        'courses': courses,
24
24
        }
25
25
26
26
    return render(request, template, context)
27
27
28
28
@login_required
29
29
def course(request, course_slug):
30
30
    template = "courses/course.djhtml"
31
31
    course_ = Course.objects.get(slug_name=course_slug)
32
32
33
33
    if request.method == 'POST':
34
34
        uploads = UploadFormSet(request.POST, request.FILES, prefix='uploads')
35
35
        if uploads.is_valid():
36
36
            uploads.save(commit=False)
37
37
            for new_upload in uploads.new_objects:
38
38
                new_upload.course = course_
39
39
            uploads.save()
40
40
            request.method = 'GET'
41
41
            return course(request, course_slug)
42
42
    else:
43
43
        uploads = UploadFormSet(
44
44
            queryset=Upload.objects.filter(course=course_).filter(student=request.user),
45
45
            prefix="uploads",
46
46
            )
47
47
    # Check if user can see this page
48
48
    if request.user.user_data.is_student:
49
49
        if course_ not in request.user.user_data.current_courses():
50
50
            """ I'm currently just redirecting to the index page, but maybe it's
51
51
            just as good to make an announcement that this course cannot be
52
52
            used by this user. """
53
53
            return index(request)
54
54
55
55
    context = {
56
56
        'course': course_,
57
57
        'main_color': course_.color,
58
58
        'announcements': Announcement.objects.filter(course=course_),
59
59
        'assignments': Assignment.objects.filter(course=course_),
60
60
        'course_items': CourseItem.objects.filter(course=course_),
61
61
        'study-groups': StudyGroup.objects.filter(course=course_),
62
62
        #'uploads': Upload.objects.filter(course=course).filter(student=request.user)
63
63
        }
64
64
    if request.user.user_data.is_student:
65
65
        context['upload_form'] = UploadForm()
66
66
    #else:
67
67
        context['student_list'] = administration.models.CourseResult.objects.filter(course_programme__course=course_)#.filter(year=current_academic_year()),
68
68
        # FIXME I disabled the year filter for testing purposes. Enable in deployment.
69
69
70
70
    return render(request, template, context)
71
71
72
72
# TODO: Find a way to see if it's possible to require some permissions and to
73
73
# put them in a decorator
74
74
#@permission_required
75
75
@login_required
76
76
def new_item(request, course_slug):
77
77
    template = "courses/new_item.djhtml"
78
78
    course = Course.objects.get(slug_name=course_slug)
79
79
80
80
    if request.user.user_data.is_student or request.user not in course.course_team:
81
81
        # Students can't add new items. Redirect to index
82
82
        # Also redirect people who are not part of the course team
83
83
        redirect('courses-index')
84
84
    # Now able to assume user is allowed to add items to this course
85
85
86
86
    context = {
87
87
        'course': course,
88
88
        'announcements': Announcement.objects.filter(course=course),
89
89
        'assignments': Assignment.objects.filter(course=course),
90
90
        'course-items': CourseItem.objects.filter(course=course),
91
91
        'study-groups': StudyGroup.objects.filter(course=course),
92
92
        'uploads': Upload.objects.filter(course=course)
93
93
        }
94
94
    return render(request, template, context)
95
95
96
96
def upload(request):
97
97
    return render(request, template, context)
98
98
99
99
#@create_context  # TODO
100
100
@login_required
101
101
def course_results(request, course_slug):
102
102
    template = "courses/course_results.djhtml"
103
103
    context = dict()
104
104
    course_ = Course.objects.get(slug_name=course_slug)
105
105
    if request.method == 'POST':
106
106
        course_results = CourseResultFormSet(request.POST, prefix='course_results')
107
107
        if course_results.is_valid():
108
108
            course_results.save()
109
109
            request.method = 'GET'
110
110
            return course(request, course_slug)
111
111
    else:
112
112
        course_results = CourseResultFormSet(
113
113
            #queryset=administration.models.CourseResult.objects.filter(course_programme__course=course_).filter(year=current_academic_year()),
114
114
            queryset=administration.models.CourseResult.objects.filter(course_programme__course=course_),
115
115
            prefix="course_results",
116
116
            )
117
117
118
118
    context['course'] = course_
119
119
    context['course_results'] = course_results
120
120
    return render(request, template, context)
121
121
122
122
@login_required
+
123
def course_attendance(request, course_slug):
+
124
    template = "courses/attendance.djhtml"
+
125
    context = dict()
+
126
    course = Course.objects.get(slug_name=course_slug)
+
127
    context['course'] = course
+
128
    context['student_list'] = administration.models.CourseResult.objects.filter(course_programme__course=course)#.filter(year=current_academic_year()),
+
129
    return render(request, template, context)
+
130
+
131
@login_required
123
132
def edit_course_items(request, course_slug):
124
133
    # TODO Only allow people on the course team to this page!
125
134
    template = "courses/edit_course_items.djhtml"
126
135
    context = dict()
127
136
    course_ = Course.objects.get(slug_name=course_slug)
128
137
    if request.method == 'POST':
129
138
        assignments = AssignmentFormSet(request.POST, prefix='assignments')
130
139
        announcements = AnnouncementFormSet(request.POST, prefix='announcements')
131
140
        course_items = CourseItemFormSet(request.POST, request.FILES, prefix='course_items')
132
141
        #course_results = CourseResultFormSet(request.POST, prefix='course_results')
133
142
        if assignments.is_valid() and announcements.is_valid() and course_items.is_valid(): #and course_results.is_valid():
134
143
            assignments.save(commit=False)
135
144
            announcements.save(commit=False)
136
145
            course_items.save(commit=False)
137
146
            #course_results.save(commit=False)
138
147
            for new_assignment in assignments.new_objects:
139
148
                new_assignment.course = course_
140
149
            for new_announcement in announcements.new_objects:
141
150
                new_announcement.course = course_
142
151
            for new_course_item in course_items.new_objects:
143
152
                new_course_item.course = course_
144
153
            #for new_course_result in course_results.new_objects:
145
154
                #new_coutse_result.course = course_
146
155
            assignments.save()
147
156
            announcements.save()
148
157
            course_items.save()
149
158
            #course_results.save()
150
159
            request.method == 'GET'
151
160
            return course(request, course_slug)
152
161
    else:
153
162
        assignments = AssignmentFormSet(
154
163
            queryset=Assignment.objects.filter(course=course_),
155
164
            prefix="assignments",
156
165
            )
157
166
        announcements = AnnouncementFormSet(
158
167
            queryset=Announcement.objects.filter(course=course_),
159
168
            prefix="announcements",
160
169
            )
161
170
        course_items = CourseItemFormSet(
162
171
            queryset=CourseItem.objects.filter(course=course_),
163
172
            prefix="course_items",
164
173
            )
165
174
        #course_results = CourseResultFormSet(
166
175
            #queryset=administration.models.CourseResult.objects.filter(course_programme__course=course_).filter(year=current_academic_year()),
167
176
            #prefix="course_results",
168
177
            #)
169
178
170
179
    context['course'] = course_
171
180
    context['assignments'] = assignments
172
181
    context['announcements'] = announcements
173
182
    context['course_items'] = course_items
174
183
    #context['course_results'] = course_results
175
184
    return render(request, template, context)
176
185
177
186
178
187
179
188
@login_required
180
189
def groups(request):
181
190
    pass
182
191
183
192
def fiche(request, course_slug):
184
193
    """Displays the fiche for the given course. Includes information about all
185
194
    course programs."""
186
195
    template = "courses/fiche.djhtml"
187
196
    context = dict()
188
197
    course = Course.objects.get(slug_name=course_slug)
189
198

static/css/tables.scss

12 additions and 0 deletions.

View changes Hide changes
1
1
    /*border-width: 0.5em;
2
2
    border-style: solid;
3
3
    border-radius: 1em;
4
4
    padding: 0.8em;
5
5
    margin: 1em;*/
6
6
    border-width: 0.4em;
7
7
    border-style: solid;
8
8
    border-radius: 0.4em 1em 1em 1em;
9
9
    padding: 0;
10
10
    padding-left: 1em;
11
11
    color: inherit;
12
12
    padding-top: 0.5em;
13
13
    &:hover {
14
14
        text-decoration: none;
15
15
    }
16
16
    span.number {
17
17
        color: white;
18
18
        padding: 0.5em;
19
19
        margin-left: -1em;
20
20
        top: -1em;
21
21
        font-weight: bold;
22
22
        border-bottom-right-radius: 1em;
23
23
    }
24
24
    div.timetable-under {
25
25
        padding-left: 0.5em;
26
26
        margin-top: 0.5em;
27
27
        margin-bottom: 0.3em;
28
28
        margin-left: -1em;
29
29
    }
30
30
}
31
31
+
32
table.attendance {
+
33
    border: 1px solid black;
+
34
    border-collapse: collapse;
+
35
    td {
+
36
        padding: 1em;
+
37
        border: 1px solid black;
+
38
    }
+
39
    th {
+
40
        padding: 1em;
+
41
    }
+
42
}
+
43