Commit b5053f4f authored by Martin Mareš's avatar Martin Mareš
Browse files

Export of points in CSV and JSON for teachers

parent 2093834f
......@@ -28,6 +28,8 @@ from collections import defaultdict
import urllib.parse
from html import escape
from markupsafe import Markup
import csv
import io
### Flask app object ###
......@@ -895,6 +897,97 @@ def serve_file(name):
return "No such file", HTTPStatus.NOT_FOUND
### Exports ###
@app.route('/teacher/c/<cident>/results.json')
def results_json(cident):
err = course_init(cident) or must_be_teacher()
if err:
return err
return export_points('json')
@app.route('/teacher/c/<cident>/results.csv')
def results_csv(cident):
err = course_init(cident) or must_be_teacher()
if err:
return err
return export_points('csv')
def export_points(format):
db_query("""
SELECT u.uid, u.ukco, u.full_name
FROM owl_users u
JOIN owl_enroll e USING(uid)
WHERE e.cid = %s
AND e.is_teacher = false
ORDER BY uid
""", (g.course.cid,))
students = db.fetchall()
db_query("""
SELECT *
FROM owl_topics
WHERE cid=%s
AND type IN ('A', 'T')
ORDER BY rank, tid
""", (g.course.cid,))
topics = db.fetchall()
db_query("""
SELECT t.ident AS tident, p.target_uid, p.points
FROM owl_topics t
JOIN owl_posts p USING(tid)
WHERE t.cid = %s
AND p.points IS NOT NULL
ORDER BY p.created
""", (g.course.cid,))
awards = db.fetchall()
points = { s.uid: {} for s in students }
for a in awards:
if a.target_uid == -1:
for uid in points.keys():
points[uid][a.tident] = float(a.points)
elif a.target_uid in points:
points[a.target_uid][a.tident] = float(a.points)
if format == "json":
out = []
for s in students:
out.append({
"uid": s.uid,
"ukco": s.ukco,
"name": s.full_name,
"tasks": points[s.uid],
"points": sum(points[s.uid].values()),
})
enc = JSONEncoder(ensure_ascii=False, sort_keys=True, indent=4)
resp = make_response(enc.encode(out))
resp.headers.add('Content-type', 'application/json')
return resp
elif format == "csv":
out = io.StringIO()
writer = csv.writer(out, delimiter=',', quoting=csv.QUOTE_MINIMAL)
writer.writerow(['uid', 'ukco', 'name', 'points'] + [t.ident for t in topics])
for s in students:
writer.writerow([
s.uid,
s.ukco,
s.full_name,
sum(points[s.uid].values()),
] + [points[s.uid].get(t.ident, "") for t in topics])
resp = make_response(out.getvalue())
resp.headers.add('Content-type', 'text/csv; charset=utf-8')
return resp
else:
raise NotImplementedError()
### User settings ###
......@@ -1365,47 +1458,7 @@ def api_points():
if not api_auth():
return "Unauthorized\n", HTTPStatus.UNAUTHORIZED
db_query("""
SELECT u.uid, u.ukco, u.full_name
FROM owl_users u
JOIN owl_enroll e USING(uid)
WHERE e.cid = %s
AND e.is_teacher = false
""", (g.course.cid,))
students = db.fetchall()
db_query("""
SELECT t.ident AS tident, p.target_uid, p.points
FROM owl_topics t
JOIN owl_posts p USING(tid)
WHERE t.cid = %s
AND p.points IS NOT NULL
ORDER BY p.created
""", (g.course.cid,))
awards = db.fetchall()
points = { s.uid: {} for s in students }
for a in awards:
if a.target_uid == -1:
for uid in points.keys():
points[uid][a.tident] = float(a.points)
elif a.target_uid in points:
points[a.target_uid][a.tident] = float(a.points)
out = []
for s in students:
out.append({
"uid": s.uid,
"ukco": s.ukco,
"name": s.full_name,
"tasks": points[s.uid],
"points": sum(points[s.uid].values()),
})
enc = JSONEncoder(ensure_ascii=False, sort_keys=True, indent=4)
resp = make_response(enc.encode(out))
resp.headers.add('Content-type', 'application/json')
return resp
return export_points('json')
### Administrative commands ###
......
......@@ -28,6 +28,7 @@
<h2>News</h2>
<table class=news>
<tr><td>2021-05-21<td>Teachers can now download results in CSV or JSON.
<tr><td>2021-04-20<td>Added a Reply button for quoting of posts.
<tr><td>2021-03-13<td>Added syntax highlighting for C, C++, Python, Haskell, and Prolog.
Use <code>```python</code> in Markdown to start a code block. Ask for more languages
......
......@@ -73,6 +73,10 @@
<td class=pts>{{ totals[c.cid][s.uid] }}
{% endfor %}
</table>
<p>Download as
<a href='{{ url_for('results_json', cident=c.ident) }}'>JSON</a>,
<a href='{{ url_for('results_csv', cident=c.ident) }}'>CSV</a>.
{% endfor %}
{% endblock %}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment