Skip to content

Commit 01dadfc

Browse files
torsavamikicz
authored andcommitted
Kurzy: Add pagination
1 parent 4f9b467 commit 01dadfc

File tree

4 files changed

+212
-31
lines changed

4 files changed

+212
-31
lines changed

naucse/models.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222

2323
_TIMEZONE = 'Europe/Prague'
24+
_TODAY = datetime.date.today()
2425
allowed_elements_parser = AllowedElementsParser()
2526

2627

@@ -484,6 +485,10 @@ def is_link(self):
484485
def is_derived(self):
485486
return self.base_course is not None
486487

488+
@reify
489+
def is_ongoing(self):
490+
return self.end_date >= _TODAY
491+
487492

488493
class Course(CourseMixin, Model):
489494
"""A course – ordered collection of sessions"""
@@ -815,10 +820,53 @@ def runs(self):
815820
for slug, run in run_year.runs.items()
816821
}
817822

823+
@reify
824+
def safe_runs(self):
825+
return {
826+
(year, run.slug): run
827+
for year, run_year in self.safe_run_years.items()
828+
for run in run_year
829+
}
830+
818831
@reify
819832
def meta(self):
820833
return MetaInfo()
821834

835+
@reify
836+
def safe_run_years(self):
837+
# since even the basic info about the forked runs can be broken, we need to make sure the required info
838+
# is provided. If ``RAISE_FORK_ERRORS`` is set, exceptions are raised here, otherwise the run is
839+
# ignored completely.
840+
safe_years = {}
841+
for year, run_years in self.run_years.items():
842+
safe_run_years = []
843+
844+
for run in run_years.runs.values():
845+
if not run.is_link():
846+
safe_run_years.append(run)
847+
elif (naucse.utils.routes.forks_enabled() and
848+
naucse.utils.routes.does_course_return_info(run, extra_required=["start_date", "end_date"])):
849+
safe_run_years.append(run)
850+
851+
safe_years[year] = safe_run_years
852+
853+
return safe_years
854+
855+
@reify
856+
def ongoing_and_recent_runs(self):
857+
"""Get runs that are either ongoing or ended in the last 3 months."""
858+
ongoing = [run for run in self.safe_runs.values()
859+
if run.is_ongoing]
860+
cutoff = _TODAY - datetime.timedelta(days=3*31)
861+
recent = [run for run in self.safe_runs.values()
862+
if not run.is_ongoing and run.end_date > cutoff]
863+
return {"ongoing": ongoing, "recent": recent}
864+
865+
def runs_from_year(self, year):
866+
"""Get all runs that either started or ended in a given year."""
867+
return [run for run in self.safe_runs.values()
868+
if run.start_date.year <= year and run.end_date.year >= year]
869+
822870
def get_lesson(self, name):
823871
if isinstance(name, Lesson):
824872
return name

naucse/routes.py

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import ics
99
from arca.exceptions import PullError, BuildError, RequirementsMismatch
1010
from arca.utils import is_dirty
11-
from flask import Flask, render_template, url_for, send_from_directory, request
11+
from flask import Flask, render_template, url_for, send_from_directory, request, redirect
1212
from flask import abort, Response
1313
from git import Repo
1414
from jinja2 import StrictUndefined
@@ -100,27 +100,61 @@ def index():
100100

101101

102102
@app.route('/runs/')
103-
def runs():
104-
# since even the basic info about the forked runs can be broken, we need to make sure the required info
105-
# is provided. If ``RAISE_FORK_ERRORS`` is set, exceptions are raised here, otherwise the run is
106-
# ignored completely.
107-
safe_years = {}
108-
for year, run_years in model.run_years.items():
109-
safe_run_years = []
103+
@app.route('/runs/<int:year>/')
104+
@app.route('/runs/<any(all):all>/')
105+
def runs(year=None, all=None):
106+
today = datetime.date.today()
107+
108+
# List of years to show in the pagination
109+
# If the current year is not there (no runs that start in the current year
110+
# yet), add it manually
111+
all_years = model.safe_run_years.keys()
112+
if today.year not in all_years:
113+
all_years.append(today.year)
114+
first_year, last_year = min(all_years), max(all_years)
115+
116+
if year is not None:
117+
if year > last_year:
118+
# Instead of showing a future year, redirect to the 'Current' page
119+
return redirect(url_for('runs'))
120+
if year not in all_years:
121+
# Otherwise, if there are no runs in requested year, return 404.
122+
abort(404)
123+
124+
if all is not None:
125+
run_data = model.safe_run_years
126+
127+
paginate_prev = {'year': first_year}
128+
paginate_next = {'all': 'all'}
129+
elif year is None:
130+
run_data = model.ongoing_and_recent_runs
110131

111-
for run in run_years.runs.values():
112-
if not run.is_link():
113-
safe_run_years.append(run)
114-
elif naucse.utils.routes.forks_enabled() and does_course_return_info(run, extra_required=["start_date",
115-
"end_date"]):
116-
safe_run_years.append(run)
132+
paginate_prev = {'year': None}
133+
paginate_next = {'year': last_year}
134+
else:
135+
run_data = model.runs_from_year(year)
136+
137+
past_years = [y for y in all_years if y < year]
138+
if past_years:
139+
paginate_next = {'year': max(past_years)}
140+
else:
141+
paginate_next = {'all': 'all'}
117142

118-
safe_years[year] = safe_run_years
143+
future_years = [y for y in all_years if y > year]
144+
if future_years:
145+
paginate_prev = {'year': min(future_years)}
146+
else:
147+
paginate_prev = {'year': None}
119148

120149
return render_template("run_list.html",
121-
run_years=safe_years,
150+
run_data=run_data,
122151
title="Seznam offline kurzů Pythonu",
123152
today=datetime.date.today(),
153+
year=year,
154+
all=all,
155+
all_years=all_years,
156+
paginate_next=paginate_next,
157+
paginate_prev=paginate_prev,
124158
edit_info=get_edit_info(model.runs_edit_path))
125159

126160

naucse/static/css/naucse.css

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,10 @@ a:hover {
132132
margin-top: 1em;
133133
}
134134

135+
div.empty-run-list {
136+
padding-bottom: 2rem;
137+
}
138+
135139
svg.icon {
136140
width: 1em;
137141
height: 1em;
@@ -212,3 +216,41 @@ ul.material-list > li svg.icon {
212216
box-sizing: content-box;
213217
color: black;
214218
}
219+
220+
/*** Pagination ***/
221+
222+
ul.paginate {
223+
list-style-type: none;
224+
text-align: center;
225+
margin-bottom: 2em;
226+
margin-left: 0;
227+
margin-right: 0;
228+
padding: 0;
229+
}
230+
231+
ul.paginate > li {
232+
display: inline-block;
233+
background-color: #F0F0F0;
234+
padding: 0;
235+
}
236+
237+
ul.paginate > li.current {
238+
display: inline-block;
239+
background-color: #FFFFFF;
240+
}
241+
242+
ul.paginate > li:hover {
243+
background-color: #E0E0E0;
244+
}
245+
246+
ul.paginate > li a {
247+
color: #000;
248+
display: inline-block;
249+
padding: 0.5ex 1ex;
250+
text-decoration: none;
251+
}
252+
253+
ul.paginate > li a.disabled {
254+
color: #999;
255+
pointer-events: none;
256+
}

naucse/templates/run_list.html

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,81 @@ <h4>
2121
</div>
2222
{% endmacro %}
2323

24+
{% macro pagination() %}
25+
<div>
26+
<ul class="paginate">
27+
<li class="paginate-prev">
28+
<a {% if year==None and all==None %}class="disabled"{% endif %}
29+
href="{{ url_for('runs', **paginate_prev) }}">&lt;</a>
30+
</li>
31+
<li class="paginate-link {% if year==None and all==None %}current{% endif %}">
32+
<a href="{{ url_for('runs', year=None) }}">Aktuální</a>
33+
</li>
34+
{% for page in all_years | reverse %}
35+
<li class="paginate-link {% if page==year %}current{% endif %}">
36+
<a href="{{ url_for('runs', year=page) }}">{{ page }}</a>
37+
</li>
38+
{% endfor %}
39+
<li class="paginate-link {% if all!=None %}current{% endif %}">
40+
<a href="{{ url_for('runs', year=None, all='all') }}">Vše</a>
41+
</li>
42+
<li class="paginate-next">
43+
<a {% if all!=None %}class="disabled"{% endif %}
44+
href="{{ url_for('runs', **paginate_next) }}">&gt;</a>
45+
</li>
46+
</ul>
47+
</div>
48+
{% endmacro %}
2449

2550
{% block list %}
2651

27-
<h1>Aktuální kurzy</h1>
28-
{% for year, run_year in run_years.items() %}
29-
{% for run in run_year | sort(attribute='start_date', reverse=True)
30-
if run.end_date >= today %}
31-
{{ show_run(run) }}
32-
{% endfor %}
33-
{% endfor %}
34-
35-
<h1>Proběhlé kurzy</h1>
36-
{% for year, run_year in run_years.items() | sort(reverse=True) %}
37-
<h2>{{ year }}</h2>
38-
{% for run in run_year | sort(attribute='start_date', reverse=True)
39-
if run.end_date < today %}
40-
{{ show_run(run) }}
52+
{% if all is none %}
53+
{% if year is none %}
54+
<h1>Aktuální kurzy</h1>
55+
{% if run_data["ongoing"] | length > 0 %}
56+
{% for run in run_data["ongoing"] | sort(attribute='start_date', reverse=True) %}
57+
{{ show_run(run) }}
58+
{% endfor %}
59+
{% else %}
60+
<div class="empty-run-list">
61+
Nemáme žádné aktuální kurzy.
62+
</div>
63+
{% endif %}
64+
65+
{% if run_data["recent"] | length > 0 %}
66+
{{ pagination() }}
67+
68+
<h1>Nedávno proběhlé kurzy</h1>
69+
{% for run in run_data["recent"] | sort(attribute='start_date', reverse=True) %}
70+
{{ show_run(run) }}
71+
{% endfor %}
72+
{% endif %}
73+
{% else %}
74+
{{ pagination() }}
75+
76+
<h1>{{ year }}</h1>
77+
{% if run_data | length > 0 %}
78+
{% for run in run_data | sort(attribute='start_date', reverse=True) %}
79+
{{ show_run(run) }}
80+
{% endfor %}
81+
{% else %}
82+
<div class="empty-run-list">
83+
V tomto roce neproběhly žádné kurzy.
84+
</div>
85+
{% endif %}
86+
{% endif %}
87+
{% else %}
88+
{{ pagination() }}
89+
90+
<h1>Všechny kurzy</h1>
91+
{% for year, run_year in run_data.items() | sort(reverse=True) %}
92+
<h2>{{ year }}</h2>
93+
{% for run in run_year | sort(attribute='start_date', reverse=True) %}
94+
{{ show_run(run) }}
95+
{% endfor %}
4196
{% endfor %}
42-
{% endfor %}
97+
{% endif %}
98+
99+
{{ pagination() }}
43100

44101
{% endblock %}

0 commit comments

Comments
 (0)