joeni

Start isolation process of roster template

The roster template can easily be used in other templates as well, given that the necessary variables are sent to the template. In order to achieve this, the roster functionality is being isolated to make it available for inclusion in other templates.
The prototype CSS file has had a little update for conflicting events.

Author
Maarten 'Vngngdn' Vangeneugden
Date
March 8, 2018, 1:17 p.m.
Hash
c4000e6b0bd9ab87a54f5bfe85b0e261c0028630
Parent
8dbdd43fe6eb108520faa6b8ddf1be04ece74f0a
Modified files
administration/new_roster.py
administration/templates/administration/roster.djhtml
administration/templates/administration/roster_t.djhtml
administration/views.py
joeni/templates/joeni/base.djhtml
static/css/base.css

administration/new_roster.py

19 additions and 7 deletions.

View changes Hide changes
1
1
building of the roster. """
2
2
from django.shortcuts import render
3
3
from collections import OrderedDict
4
4
import datetime
5
5
from django.urls import reverse
6
6
from django.utils.translation import gettext as _
7
7
from .models import *
8
8
import administration
9
9
10
10
def same_day(datetime_a, datetime_b):
11
11
    """True if both a and b are on the same day, false otherwise."""
12
12
    return (
13
13
        datetime_a.day == datetime_b.day and
14
14
        datetime_a.month == datetime_b.month and
15
15
        datetime_a.year == datetime_b.year)
16
16
17
17
def same_daytime(moment, hour, minute):
18
18
    return (moment.minute == minute and moment.hour == hour)
19
19
20
20
def conflicting_events(events):
21
21
    """Finds conflicting events in the given set, and returns a list of all
22
22
    conflicting events, grouped according to their conflicts in lists."""
23
23
    conflicts = list()
24
24
    test_conflict = list()
25
25
    #for event in events.order_by("begin_time"):
26
26
    for event in events:
27
27
        if len(test_conflict) == 0:
28
28
            test_conflict.append(event)
29
29
        else:
30
30
            possible_conflict = test_conflict.pop()
31
31
            #print("comparing" + str(possible_conflict) + " - " + str(event))
32
32
            if possible_conflict.end_time > event.begin_time:  # New conflict!
33
33
                test_conflict.append(possible_conflict)
34
34
                test_conflict.append(event)
35
35
                #print(test_conflict)
36
36
            elif len(test_conflict) == 0:  # No conflict
37
37
                test_conflict.append(event)
38
38
            else:  # No conflict, but previous conflicting events exist
39
39
                test_conflict.append(possible_conflict)
40
40
                conflicts.append(test_conflict.copy())
41
41
                test_conflict.clear()
42
42
    if len(test_conflict) >= 2:
43
43
        conflicts.append(test_conflict.copy())
44
44
    return conflicts
45
45
46
46
def replace_conflict(events, conflicts):
47
47
    """Removes the conflicts from events, and replaces them with a new
48
48
    event. Returns the key number for this conflict set. Expects the conflicts
49
49
    to be in chronological order based on begin_time."""
50
50
    for conflict in conflicts:
51
51
        events.remove(conflict)
52
52
    conflict_number = 0
53
53
    for event in events:
54
54
        if event.note == "Conflict " + str(conflict_number):
55
55
            conflict_number += 1
56
56
57
57
    replacement = Event(
58
58
        begin_time = conflicts[0].begin_time,
59
59
        end_time = conflicts[-1].end_time,
60
60
        note = "Conflict " + str(conflict_number),
61
61
        )
62
62
    events.append(replacement)
63
63
    return conflict_number
64
64
65
65
def replace_conflicts(events):
66
66
    """Searches for all events that are in conflict regarding timespans, and
67
67
    replaces them with a general event. Returns a dictionary with keys referencing
68
68
    a specific conflict block, and the values being a list of the events that are
69
69
    in conflict with each other."""
70
70
    all_conflicts = conflicting_events(events)
71
71
    conflict_dict = dict()
72
72
    for conflict_list in all_conflicts:
73
73
        conflict_number = replace_conflict(events, conflict_list)
74
74
        conflict_dict[conflict_number] = conflict_list
75
75
    return conflict_dict
76
76
77
77
def create_roster_event(event_type, quarters, content, style="", note=""):
78
78
    return '<td class="{event_type}" style="{style}" {title} rowspan="{quarters}">{content}</td>'.format(
79
79
        event_type=event_type,
80
80
        style=style,
81
81
        quarters=quarters,
82
82
        content=content,
83
83
        title=note)
84
84
85
85
def create_roster_conflict_event(event):
86
86
    quarters = (event.end_time - event.begin_time).seconds // 60 // 15
87
87
    content = _('<strong>Conflicting events!<br />See <a href="#'+event.note+'">'+event.note+'</a> for more information.</strong>')
88
88
    return create_roster_event("event-conflict", quarters, content)
89
89
90
90
def create_roster_study_event(event):
91
91
    pass  # TODO
92
92
def create_roster_university_event(event):
93
93
    pass  # TODO
94
94
def create_roster_course_event(event):
95
95
    # FIXME: Currently not all users are equipped with a user_data object.
96
96
    # Because of this, reversing the link to the docent's page may throw a
97
97
    # RelatedObjectDoesNotExist error. Until that's resolved, I've wrapped
98
98
    # creating that link in a try catch.
99
99
    docent_link = ""
100
100
    try:
101
101
        docent_link = reverse('administration-user', args=(event.docent.user_data.slug_name(),))
102
102
    except :
103
103
        pass
104
104
105
105
    quarters = (event.end_time - event.begin_time).seconds // 60 // 15
106
106
    course_link = reverse('courses-course-index', args=(event.course.course.slug_name,))
107
107
    room_link = reverse('administration-room-detail', args=(str(event.room),))
108
108
    event_type = "event"
109
109
110
110
    content = "{course}<br /> {docent}<br />{begin} - {end}<br /> {room} ({subject})".format(
111
111
        course = '<a href="'+course_link+'">'+str(event.course)+'</a>',
112
112
        docent = '<a href="'+docent_link+'">'+str(event.docent)+'</a>',
113
113
        begin  = event.begin_time.strftime("%H:%M"),
114
114
        end    = event.end_time.strftime("%H:%M"),
115
115
        room   = '<a href="'+room_link+'">'+str(event.room)+'</a>',
116
116
        subject = event.subject,
117
117
        )
118
118
119
119
    style = "background-color: #"+event.course.course.color+"; color: white;"
120
120
121
121
    if event.recently_created():
122
122
        event_type = "event-new"
123
123
        style = ""
124
124
    elif event.recently_updated():
125
125
        event_type = "event-update"
126
126
        style = ""
127
127
    elif event.note != "":
128
128
        event_type = "event-note"
129
129
130
130
    return create_roster_event(event_type, quarters, content, style, event.note)
131
131
132
132
133
133
134
134
135
135
136
136
def first_last_day(events):
137
137
    """Returns the first and last day of the starting times of the given events."""
138
138
    earliest = events[0].begin_time
139
139
    latest = events[0].begin_time
140
140
    for event in events:
141
141
        if event.begin_time < earliest:
142
142
            earliest = event.begin_time
143
143
        if event.begin_time > latest:
144
144
            latest = event.begin_time
145
145
    first = datetime.date(earliest.year, earliest.month, earliest.day)
146
146
    last = datetime.date(latest.year, latest.month, latest.day)
147
147
    return first, last
148
148
149
149
150
150
151
151
152
152
def create_roster_for_event(event, conflict=False):
153
153
    """Determines which function to call to build the roster entry for the given event."""
154
154
    if conflict and isinstance(event, Event):
155
155
        return create_roster_conflict_event(event)
156
156
    elif isinstance(event, CourseEvent):
157
157
        return create_roster_course_event(event)
158
158
    elif isinstance(event, UniversityEvent):
159
159
        return create_roster_university_event(event)
160
160
    elif isinstance(event, StudyEvent):
161
161
        return create_roster_study_event(event)
162
162
    elif isinstance(event, Event):
163
163
        return create_roster_event(event)
164
164
    else:
165
165
        raise TypeError("Given object is not of any Event type")
166
166
167
167
def make_quarter_row(events, hour, quarter):
168
-
    """Creates an HTML row for a quarter. Expects *NO conflicts!*"""
169
-
    if len(conflicting_events(events)) != 0:
170
-
        print(conflicting_events(events))
171
-
        #raise ValueError("events contains conflicts!")
172
-
173
-
    quarter_line = "<tr><td style='font-size: xx-small;'>"
174
-
    if hour < 10:
+
168
    """Creates and returns the first part of the quarter row, which is the
+
169
    column with the current time in it."""
+
170
    quarter_line = "<tr><td style='font-size:"
+
171
    if quarter != 0:
+
172
        quarter_line += "xx-small; color: grey;"
+
173
    else:
+
174
        quarter_line += "x-small;"
+
175
    quarter_line += "'>"
+
176
    if hour < 10:
175
177
        quarter_line += "0"
176
178
    quarter_line +=  str(hour) +":"
177
179
    if quarter == 0:
178
180
        quarter_line += "0"
179
181
    quarter_line +=  str(quarter)
180
182
    quarter_line += "</td>"
181
183
+
184
+
185
+
186
def make_quarter_row(events, hour, quarter):
+
187
    """Creates an HTML row for a quarter. Expects *NO conflicts!*"""
+
188
    if len(conflicting_events(events)) != 0:
+
189
        print(conflicting_events(events))
+
190
        raise ValueError("events contains conflicts!")
+
191
+
192
    quarter_line = make_first_quarter_column(hour, quarter)
+
193
182
194
    first_day, last_day = first_last_day(events)
183
195
    for i in range((first_day - last_day).days, 1):
184
196
        column_added = False
185
197
        current_day = (last_day + datetime.timedelta(days=i))
186
198
        current_daytime = datetime.datetime(
187
199
            current_day.year,
188
200
            current_day.month,
189
201
            current_day.day,
190
202
            hour=hour,
191
203
            minute=quarter,
192
204
            tzinfo = datetime.timezone.utc)
193
205
        for event in events:
194
206
            if (same_day(event.begin_time, current_day) and
195
207
                same_daytime(event.begin_time, hour, quarter)):  # Event starts on this quarter
196
208
                quarter_line += create_roster_for_event(
197
209
                    event,
198
210
                    conflict = (event.note.startswith("Conflict ")))
199
211
                column_added = True
200
212
            elif (same_day(event.begin_time, current_day) and
201
213
                  event.begin_time < current_daytime and
202
214
                  event.end_time >= current_daytime):
203
215
                column_added = True
204
216
                break
205
217
        if not column_added:
206
218
            quarter_line += "<td></td>"
207
219
    quarter_line += "</tr>"
208
220
    return quarter_line
209
221
210
222
211
223
212
224
213
225
def create_roster_rows(events):
214
226
    if len(events) == 0:
215
227
        return {}, []
216
228
    events = list(events.order_by("begin_time"))
217
229
    conflict_dict = replace_conflicts(events)
218
230
    print(conflict_dict)
219
231
    for event in events:
220
232
        print(event.begin_time)
221
233
    table_code = []
222
234
    current_quarter = datetime.datetime.now().replace(hour=8, minute=0)
223
235
    while current_quarter.hour != 20 or current_quarter.minute != 00:
224
236
        table_code.append(make_quarter_row(events, current_quarter.hour, current_quarter.minute))
225
237
        current_quarter += datetime.timedelta(minutes=15)
226
238
    return conflict_dict, table_code
227
239

administration/templates/administration/roster.djhtml

1 addition and 49 deletions.

View changes Hide changes
1
1
{% cycle "hour" "first quarter" "half" "last quarter" as hour silent %}
2
2
{# "silent" blocks the cycle operator from printing the cycler, and in subsequent calls #}
3
3
{% load i18n %}
4
4
5
5
{% block title %}
6
6
    {% trans "Roster" %} | {{ block.super }}
7
7
{% endblock %}
8
8
9
9
{% block main %}
10
10
    {% include "administration/nav.djhtml" %}
11
11
    <h1>{% trans "Personal timetable" %}</h1>
12
12
    <h2>{% trans "Main hour roster" %}</h2>
13
13
    <style>
14
-
        table td {
15
-
            border-width: 0px 0px 1px 0px;
16
-
            border-bottom-style: solid;
17
-
            border-style: solid;
18
-
            border-color: red;
19
-
        }
20
-
    </style>
21
-
    <table>
22
-
        <th>
23
-
            {#<td></td> {# Empty row for hours #} {# Apparantly this isn't necessary with <th /> #}
24
-
            {% for day in days %}
25
-
                <td>{{ day|date:"l (d/m)" }}</td>
26
-
            {% endfor %}
27
-
        </th>
28
-
        {% for element in time_blocks %}
29
-
            {{ element|safe }}
30
-
            <!--<tr>
31
-
                {% if hour == "hour" %}
32
-
                    <td>{{ time }}</td>
33
-
                {% else %}
34
-
                    <td></td>
35
-
                {% endif %}
36
-
                {% cycle hour %}
37
-
                <td>{{ time }}</td>
38
-
                <td>{{ event }}</td>
39
-
            </tr>-->
40
-
        {% endfor %}
41
-
    </table>
42
-
    {% for number, conflict_list in conflicts.items %}
43
-
        <h2 id="Conflict {{ number }}">Conflict {{ number }}</h2>
44
-
        {% for conflict in conflict_list %}
45
-
            {% if conflict.recently_created %}
46
-
                <div class="event-new">
47
-
            {% elif conflict.recently_updated %}
48
-
                <div class="event-update">
49
-
            {% endif %}
50
-
            <a href="{% url "courses-course-index" conflict.course.course.slug_name %}">
51
-
                {{ conflict.course }}</a><br />
52
-
            {# FIXME Temporarily disabled until all users have an associated user_data #}
53
-
            {#<a href="{% url "administration-user" conflict.docent.user_data.slug_name %}">#}
54
-
            {#{{ conflict.docent }}</a><br />#}
55
-
            {{ conflict.docent }}<br />
56
-
            {{ conflict.begin_time|date:"H:i" }} - {{ conflict.end_time|date:"H:i" }}<br />
57
-
            <a href="{% url "administration-room-detail" conflict.room %}">
58
-
                {{ conflict.room }}</a> ({{ conflict.subject }})
59
-
            </div>
60
-
        {% endfor %}
61
-
    {% endfor %}
62
-
63
14
64
15
+
16
65
17
66
18
        <a class="btn" href="{% url "administration-roster" begin=prev_begin end=prev_end %}">
67
19
            {% trans "Previous week" %}
68
20
        </a>
69
21
        <a class="btn" href="{% url "administration-roster" %}">
70
22
            {% trans "Current week" %}
71
23
        </a>
72
24
        <a class="btn" href="{% url "administration-roster" begin=next_begin end=next_end %}">
73
25
            {% trans "Next week" %}
74
26
        </a>
75
27
        {# TODO: Add links to "previous week", "next week" and "current week" with large buttons #}
76
28
77
29
    <h2>{% trans "Explanation" %}</h2>
78
30
    <p>
79
31
        {% trans "Personal roster from" %} {{ begin|date }} {% trans "to" %} {{ end|date }}
80
32
    </p>
81
33
    <p>
82
34
        {% blocktrans %}
83
35
            Some fields may have additional information that might be of interest
84
36
            to you. This information is shown in different ways with colour codes.
85
37
        {% endblocktrans %}
86
38
    </p>
87
39
88
40
    <dl>
89
41
        <dt><span class="event-update">
90
42
            {% trans "Recent event update" %}
91
43
        </span></dt>
92
44
        <dd>
93
45
            {% blocktrans %}
94
46
                This event had one or more of its properties changed
95
47
                in the last five days. This can be the room, the hours, the subject, ...
96
48
                You're encouraged to take note of that.
97
49
            {% endblocktrans %}
98
50
        </dd>
99
51
        <dt><span class="event-new">
100
52
            {% trans "New event" %}
101
53
        </span></dt>
102
54
        <dd>
103
55
            {% blocktrans %}
104
56
                This is a new event, added in the last five days.
105
57
            {% endblocktrans %}
106
58
        </dd>
107
59
        <dt><span class="event-note">
108
60
            {% trans "Notification available" %}
109
61
        </span></dt>
110
62
        <dd>
111
63
            {% blocktrans %}
112
64
                This event has a note attached to it by the docent. Hover over
113
65
                the event to display the note.
114
66
            {% endblocktrans %}
115
67
        </dd>
116
68
    </dl>
117
69
118
70
{% endblock main %}
119
71

administration/templates/administration/roster_t.djhtml

50 additions and 0 deletions.

View changes Hide changes
+
1
This is the roster template. You can use this to display a hourly roster,
+
2
spread over a series of days. To function properly, this template requires
+
3
the following variables:
+
4
- days :: A list of all days for which a column must be made.
+
5
- time_blocks :: A list of all rows with their respective timings, that must
+
6
                 be displayed in the roster. These elements must *not* contain
+
7
                 any unsafe elements!
+
8
- conflicts :: A dictionary with all conflicting events that need to be listed.
+
9
{% endcomment %}
+
10
+
11
<style>
+
12
    table td {
+
13
        border-width: 0px 0px 1px 0px;
+
14
        border-bottom-style: solid;
+
15
        border-style: solid;
+
16
        border-color: red;
+
17
    }
+
18
</style>
+
19
<table>
+
20
    <th>
+
21
        {#<td></td> {# Empty row for hours #} {# Apparantly this isn't necessary with <th /> #}
+
22
        {% for day in days %}
+
23
            <td>{{ day|date:"l (d/m)" }}</td>
+
24
        {% endfor %}
+
25
    </th>
+
26
    {% for element in time_blocks %}
+
27
        {{ element|safe }}
+
28
    {% endfor %}
+
29
</table>
+
30
{% for number, conflict_list in conflicts.items %}
+
31
    <h2 id="Conflict {{ number }}">Conflict {{ number }}</h2>
+
32
    {% for conflict in conflict_list %}
+
33
        {% if conflict.recently_created %}
+
34
            <div class="event-new">
+
35
        {% elif conflict.recently_updated %}
+
36
            <div class="event-update">
+
37
        {% endif %}
+
38
        <a href="{% url "courses-course-index" conflict.course.course.slug_name %}">
+
39
            {{ conflict.course }}</a><br />
+
40
        {# FIXME Temporarily disabled until all users have an associated user_data #}
+
41
        {#<a href="{% url "administration-user" conflict.docent.user_data.slug_name %}">#}
+
42
        {#{{ conflict.docent }}</a><br />#}
+
43
        {{ conflict.docent }}<br />
+
44
        {{ conflict.begin_time|date:"H:i" }} - {{ conflict.end_time|date:"H:i" }}<br />
+
45
        <a href="{% url "administration-room-detail" conflict.room %}">
+
46
            {{ conflict.room }}</a> ({{ conflict.subject }})
+
47
        </div>
+
48
    {% endfor %}
+
49
{% endfor %}
+
50

administration/views.py

18 additions and 0 deletions.

View changes Hide changes
1
1
from collections import OrderedDict
2
2
from django.http import HttpResponseRedirect
3
3
import datetime
4
4
from django.urls import reverse # Why?
5
5
from django.utils.translation import gettext as _
6
6
from .models import *
7
7
from .forms import UserDataForm
8
8
from .new_roster import create_roster_rows
9
9
from .pingping import update_balance
10
10
import administration
11
11
from django.contrib.auth.decorators import login_required
12
12
from django.contrib.auth import authenticate
13
13
14
14
@login_required
15
15
def roster(request, begin=None, end=None):
16
16
    """Collects and renders the data that has to be displayed in the roster.
17
17
18
18
    The begin and end date can be specified. Only roster points in that range
19
19
    will be included in the response. If no begin and end are specified, it will
20
20
    take the current week as begin and end point. If it's
21
21
    weekend, it will take next week."""
22
22
23
23
    # TODO Handle given begin and end
24
24
    context = dict()
25
25
    context = {'money' : update_balance(None)}
26
26
    template = "administration/roster.djhtml"
27
27
28
28
    if begin is None or end is None:
29
29
        today = datetime.date.today()
30
30
        if today.isoweekday() in {6,7}:  # Weekend
31
31
            begin = today + datetime.timedelta(days=8-today.isoweekday())
32
32
            end = today + datetime.timedelta(days=13-today.isoweekday())
33
33
        else:  # Same week
34
34
            begin = today - datetime.timedelta(days=today.weekday())
35
35
            end = today + datetime.timedelta(days=5-today.isoweekday())
36
36
    else:  # Changing regexes to date objects
37
37
        b = begin.split("-")
38
38
        e = end.split("-")
39
39
        begin = datetime.datetime(int(b[2]),int(b[1]),int(b[0]))
40
40
        end = datetime.datetime(int(e[2]),int(e[1]),int(e[0]))
41
41
42
42
    context['begin'] = begin
43
43
    context['end'] = end
44
44
45
45
    context['prev_begin'] = (begin - datetime.timedelta(days=7)).strftime("%d-%m-%Y")
46
46
    context['prev_end'] = (begin - datetime.timedelta(days=2)).strftime("%d-%m-%Y")
47
47
    context['next_begin'] = (end + datetime.timedelta(days=2)).strftime("%d-%m-%Y")
48
48
    context['next_end'] = (end + datetime.timedelta(days=7)).strftime("%d-%m-%Y")
49
49
50
50
    days = [begin]
51
51
    while (end-days[-1]).days != 0:
52
52
        # Human translation: Keep adding days until the last day in the array of
53
53
        # days is the same day as the last day the user wants to see the roster for.
54
54
        days.append(days[-1] + datetime.timedelta(days=1))
55
55
    context['days'] = days
56
56
57
57
    # Collecting events
58
58
    course_events = CourseEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end).order_by("begin_time")
59
59
    #university_events = UniversityEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
60
60
    #study_events = StudyEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
61
61
    #events = Event.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
62
62
    conflicts, table_code = create_roster_rows(course_events)
63
63
64
64
    context['time_blocks'] = table_code
65
65
    context['conflicts'] = conflicts
66
66
    #print(time_blocks)
67
67
    return render(request, template, context)
68
68
    # TODO Finish!
69
69
70
70
def index(request):
71
71
    template = "administration/index.djhtml"
72
72
    context = {'money': update_balance(None)}
73
73
    return render(request, template, context)
74
74
75
75
    pass
76
76
77
77
def pre_registration(request):
78
78
    user_data_form = UserDataForm()
79
79
    template = "administration/pre_registration.djhtml"
80
80
    context = dict()
81
81
82
82
    if request.method == 'POST':
83
83
        user_data_form = UserDataForm(request.POST)
84
84
        context['user_data_form'] = user_data_form
85
85
        if user_data_form.is_valid():
86
86
            user_data_form.save()
87
87
            context['messsage'] = _("Your registration has been completed. You will receive an e-mail shortly.")
88
88
        else:
89
89
            context['messsage'] = _("The data you supplied had errors. Please review your submission.")
90
90
    else:
91
91
        context['user_data_form'] = UserDataForm(instance = user_data_form)
92
92
93
93
    return render(request, template, context)
94
94
    pass
95
95
96
96
@login_required
97
97
def settings(request):
98
98
    user_data = UserData.objects.get(user=request.user)
99
99
    user_data_form = UserDataForm(instance = user_data)
100
100
    template = "administration/settings.djhtml"
101
101
    context = {'money' : update_balance(None)}
102
102
103
103
    if request.method == 'POST':
104
104
        user_data_form = UserDataForm(request.POST, instance = user_data)
105
105
        context['user_data_form'] = user_data_form
106
106
        if user_data_form.is_valid():
107
107
            user_data_form.save()
108
108
            context['messsage'] = _("Your settings were successfully updated.")
109
109
        else:
110
110
            context['messsage'] = _("The data you supplied had errors. Please review your submission.")
111
111
    else:
112
112
        context['user_data_form'] = UserDataForm(instance = user_data)
113
113
114
114
    return render(request, template, context)
115
115
116
116
@login_required
117
117
def bulletin_board(request):
118
118
    context = dict()
119
119
    context = {'money' : update_balance(None)}
120
120
    context['exam_commission_decisions'] = ExamCommissionDecision.objects.filter(user=request.user)
121
121
    context['education_department_messages'] = EducationDepartmentMessages.objects.all()
122
122
    for item in context['education_department_messages']:
123
123
        print(item.text)
124
124
    template = "administration/bulletin_board.djhtml"
125
125
    return render(request, template, context)
126
126
127
127
def jobs(request):
128
128
    context = dict()
129
129
    context = {'money' : update_balance(None)}
130
130
    template = "administration/jobs.djhtml"
131
131
    #@context['decisions'] = ExamCommissionDecision.objects.filter(user=request.user)
132
132
    return render(request, template, context)
133
133
134
134
135
135
@login_required
136
136
def curriculum(request):
137
137
    context = dict()
138
138
    context = {'money' : update_balance(None)}
139
139
    template = "administration/curriculum.djhtml"
140
140
    context['curricula'] = Curriculum.objects.filter(student=request.user)
141
141
    context['cource_results'] = CourseResult.objects.filter(student=request.user)
142
142
    return render(request, template, context)
143
143
144
144
def result(request):
145
145
    return render(request, template, context)
146
146
147
147
@login_required
148
148
def results(request):
149
149
    results = CourseResult.objects.filter(student=request.user)
150
150
    template = "administration/results.djhtml"
151
151
    # TODO
152
152
    return render(request, template, context)
153
153
154
154
def forms(request):
155
155
    context = dict()
156
156
    context = {'money' : update_balance(None)}
157
157
    template = "administration/forms.djhtml"
158
158
    return render(request, template, context)
159
159
160
160
def user(request, slug_name):
161
161
    pass
162
162
163
163
def rooms(request):
164
164
    context = dict()
165
165
    context = {'money' : update_balance(None)}
166
166
    context['rooms'] = Room.objects.all()
167
167
    context['room_reservations'] = RoomReservation.objects.all()
168
168
    context['course_events'] = CourseEvent.objects.all()
169
169
    context['blocks'] = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
170
170
171
171
172
172
173
173
    template = "administration/rooms.djhtml"
174
174
    return render(request, template, context)
175
175
176
176
def room_detail(request, room):
177
177
    template = "administration/room_detail.djhtml"
178
178
    context = dict()
179
179
    context = {'money' : update_balance(None)}
180
180
    room = Room.objects.get(name=room)
181
181
    context['room'] = room
182
182
    context['reservations'] = RoomReservation.objects.filter(room=room).filter(begin_time__gte=datetime.datetime.now())
183
183
    context['course_events'] = CourseEvent.objects.filter(room=room).filter(begin_time__gte=datetime.datetime.now())
184
184
    return render(request, template, context)
+
185
    today = datetime.date.today()
+
186
    if today.isoweekday() in {6,7}:  # Weekend
+
187
        today = today + datetime.timedelta(days=8-today.isoweekday())
+
188
+
189
    context['days'] = [today]
+
190
+
191
    # Collecting events
+
192
    course_events = CourseEvent.objects.filter(room=room).filter(begin_time__date=today)
+
193
    print(course_events)
+
194
    #university_events = UniversityEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
+
195
    #study_events = StudyEvent.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
+
196
    #events = Event.objects.filter(begin_time__gte=begin).filter(end_time__lte=end)
+
197
+
198
    conflicts, table_code = create_roster_rows(course_events)
+
199
    context['time_blocks'] = table_code
+
200
    context['conflicts'] = conflicts
+
201
    print(context['time_blocks'])
+
202
    return render(request, template, context)
185
203
186
204
def login(request):
187
205
    context = dict()
188
206
    context = {'money' : update_balance(None)}
189
207
    if request.method == "POST":
190
208
        name = request.POST['name']
191
209
        passphrase = request.POST['pass']
192
210
        user = authenticate(username=name, password=passphrase)
193
211
        if user is not None: # The user was successfully authenticated
194
212
            print("YA")
195
213
            return HttpResponseRedirect(request.POST['next'])
196
214
        else: # User credentials were wrong
197
215
            context['next'] = request.POST['next']
198
216
            context['message'] = _("The given credentials were not correct.")
199
217
    else:
200
218
        context['next'] = request.GET.get('next', None)
201
219
        if context['next'] is None:
202
220
            context['next'] = reverse('administration-index')
203
221
204
222
    template = 'administration/login.djhtml'
205
223
206
224
    return render(request, template, context)
207
225

joeni/templates/joeni/base.djhtml

1 addition and 1 deletion.

View changes Hide changes
1
1
{% load i18n %}
2
2
{% get_current_language as LANGUAGE_CODE %}
3
3
{% get_language_info for LANGUAGE_CODE as lang %}
4
4
{% load static %}
5
5
{% get_media_prefix as media %}
6
6
7
7
<!DOCTYPE html>
8
8
<html lang="{{ lang.code }}">
9
9
    <head>
10
10
        <title>
11
11
            {% block title %}
12
12
                {#◀ Joeni /▶ | ▶▶ UHasselt#}
13
13
                ▶▶ UHasselt
14
14
            {% endblock title %}
15
15
        </title>
16
16
17
17
        {% block stylesheets %}
18
18
            <link href="{% static "css/header.css" %}" rel="stylesheet" media="screen, projection" />
19
19
            <link href="{% static "css/footer.css" %}" rel="stylesheet" media="screen, projection" />
20
20
            <link href="{% static "css/base.css" %}" rel="stylesheet" media="screen, projection" />
21
21
            <style type="text/css">
22
22
                header, footer {
23
23
                    background-color: #{{ user.account.settings.color|default:"UHASSELT" }};
24
24
                }
25
25
            </style>
26
26
        {% endblock stylesheets %}
27
27
28
28
        {% block metaflags %}
29
29
            {# This is standard for all web pages and doesn't require changing. #}
30
30
            {# UTF-8, always #}
31
31
            <meta charset="utf-8" />
32
32
            {#<meta name="author" content="Maarten Vangeneugden">#}
33
33
            {# Indicates this page is suited for mobile devices #}
34
34
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
35
35
            <meta
36
36
                name="description"
37
37
                content="{% block description %}
38
38
                    {% trans "The digital platform of Hasselt University" %}
39
39
                {% endblock description %}" />
40
40
        {% endblock metaflags %}
41
41
    </head>
42
42
43
43
    <body>
44
44
        <header>
45
45
                {% block header %}
46
46
                    {% include "joeni/header.djhtml" %}
47
47
                {% endblock header %}
48
48
        </header>
49
49
50
50
        <main>
51
51
            {% block main %}
52
52
            {% endblock main %}
53
53
        </main>
54
54
55
55
        <footer>
56
56
            {% block footer %}
57
57
                {% include "joeni/footer.djhtml" %}
58
58
            {% endblock footer %}
59
59
        </footer>
60
60
61
61
    </body>
62
62
    {% block JavaScript %}
63
63
    {% comment JavaScript %}
64
-
    My website does not require JavaScript for basic actions. However, it may be
+
64
    My website does not require JavaScript for basic actions. However, it may be
65
65
    used to make certain things look better, or to spice up some actions for the
66
66
    user. Should it be necessary, add the appropriate JavaScript code in this
67
67
    block.
68
68
    The reason this block is at the bottom?
69
69
	Remember to always put JavaScript on the bottom to reduce load time.
70
70
    Otherwise it just errors like a fucktard, which is really the only thing
71
71
    you can always expect from JavaScript.
72
72
    {% endcomment %}
73
73
    {% endblock JavaScript %}
74
74
</html>
75
75

static/css/base.css

6 additions and 1 deletion.

View changes Hide changes
1
1
    font-family: Verdana, Futura, Arial, sans-serif;
2
2
}
3
3
4
4
a.btn {
5
5
    border-style: solid;
6
6
    text-transform: uppercase;
7
7
    border-size: 3em;
8
8
    border-color: blue;
9
9
    padding: 0.5em;
10
10
    text-decoration: none;
11
11
    color: blue;
12
12
    font-weight: bold;
13
13
}
14
14
a.btn:hover {
15
15
    background-color: blue;
16
16
    color: white;
17
17
}
18
18
19
19
dl dt {
20
20
    margin: 5px;
21
21
}
22
22
23
23
.event {
24
24
    padding: 5px;
25
25
}
26
26
27
27
.event-update {
28
28
    padding: 5px;
29
29
    background-color: yellow;
30
30
    color: red;
31
31
    border: medium dotted red;
32
32
}
33
33
.event-new {
34
34
    padding: 5px;
35
35
    background-color: white;
36
36
    color: black;
37
37
    border: medium dashed black;
38
38
}
39
39
.event-note {
40
40
    padding: 5px;
41
41
    color: purple;
42
42
    border: medium double purple;
43
43
}
44
44
.event-conflict {
45
45
    padding: 5px;
46
46
    background-color: red;
47
47
    color: white;
48
48
    border: medium solid maroon;
49
-
}
+
49
    border-color: inherit;
+
50
}
+
51
.event a {
+
52
    text-decoration: none;
+
53
    color: inherit;
+
54
}
50
55