ladok3 4.19__py3-none-any.whl → 5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ladok3 might be problematic. Click here for more details.
- doc/ltxobj/ladok3.pdf +0 -0
- ladok3/.gitignore +1 -0
- ladok3/Makefile +6 -0
- ladok3/data.nw +26 -8
- ladok3/data.py +23 -3
- ladok3/report.nw +17 -2
- ladok3/report.py +11 -3
- ladok3/scripts.nw +244 -0
- {ladok3-4.19.dist-info → ladok3-5.1.dist-info}/METADATA +1 -1
- ladok3-5.1.dist-info/RECORD +22 -0
- ladok3-4.19.dist-info/RECORD +0 -21
- {ladok3-4.19.dist-info → ladok3-5.1.dist-info}/LICENSE +0 -0
- {ladok3-4.19.dist-info → ladok3-5.1.dist-info}/WHEEL +0 -0
- {ladok3-4.19.dist-info → ladok3-5.1.dist-info}/entry_points.txt +0 -0
doc/ltxobj/ladok3.pdf
CHANGED
|
Binary file
|
ladok3/.gitignore
CHANGED
ladok3/Makefile
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
LIB= ladok3.nw api.nw undoc.nw
|
|
2
2
|
CLI= cli.nw data.nw report.nw student.nw
|
|
3
|
+
SCRIPTS=$(shell noroots scripts.nw | sed -E "s/(<<|>>)//g")
|
|
3
4
|
|
|
4
5
|
.PHONY: all
|
|
5
6
|
all: ${LIB:.nw=.tex} __init__.py
|
|
6
7
|
all: ${CLI:.nw=.tex} ${CLI:.nw=.py} ladok.bash
|
|
8
|
+
all: scripts.tex ${SCRIPTS}
|
|
7
9
|
|
|
8
10
|
__init__.py: ladok3.py
|
|
9
11
|
mv $^ $@
|
|
@@ -13,6 +15,9 @@ ladok3.py: ${LIB}
|
|
|
13
15
|
ladok.bash: __init__.py ${CLI:.nw=.py}
|
|
14
16
|
register-python-argcomplete ladok > $@
|
|
15
17
|
|
|
18
|
+
${SCRIPTS}: scripts.nw
|
|
19
|
+
${NOTANGLE.sh}
|
|
20
|
+
|
|
16
21
|
|
|
17
22
|
.PHONY: clean
|
|
18
23
|
clean:
|
|
@@ -20,6 +25,7 @@ clean:
|
|
|
20
25
|
${RM} ${LIB:.nw=.tex}
|
|
21
26
|
${RM} ${CLI:.nw=.tex}
|
|
22
27
|
${RM} ${CLI:.nw=.py} ladok.bash
|
|
28
|
+
${RM} scripts.tex ${SCRIPTS}
|
|
23
29
|
|
|
24
30
|
.PHONY: distclean
|
|
25
31
|
distclean:
|
ladok3/data.nw
CHANGED
|
@@ -16,7 +16,7 @@ from LADOK (\cref{DataCommand}).
|
|
|
16
16
|
This is a subcommand for the [[ladok]] command-line interface.
|
|
17
17
|
It can be used like this:
|
|
18
18
|
\begin{minted}{bash}
|
|
19
|
-
ladok
|
|
19
|
+
ladok course DD1315 > DD1315.csv
|
|
20
20
|
\end{minted}
|
|
21
21
|
This program will produce CSV-formated data to answer the questions above.
|
|
22
22
|
The data is formated like this:
|
|
@@ -26,11 +26,11 @@ The data is formated like this:
|
|
|
26
26
|
\item component,
|
|
27
27
|
\item student (pseudonym),
|
|
28
28
|
\item grade,
|
|
29
|
-
\item normalized
|
|
29
|
+
\item either absolute date or normalized to the course start and finish.
|
|
30
30
|
\end{itemize}
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
\section{The [[
|
|
33
|
+
\section{The [[course]] subcommand}\label{DataCommand}
|
|
34
34
|
|
|
35
35
|
This is a subcommand run as part of the [[ladok3.cli]] module.
|
|
36
36
|
We provide a function [[add_command_options]] that adds the subcommand options
|
|
@@ -59,7 +59,7 @@ def command(ladok, args):
|
|
|
59
59
|
We add a subparser.
|
|
60
60
|
We set it up to use the function [[command]].
|
|
61
61
|
<<add data parser to parser>>=
|
|
62
|
-
data_parser = parser.add_parser("
|
|
62
|
+
data_parser = parser.add_parser("course",
|
|
63
63
|
help="Returns course results data in CSV form",
|
|
64
64
|
description="""
|
|
65
65
|
Returns the results in CSV form for all first-time registered students.
|
|
@@ -79,9 +79,10 @@ course_rounds = filter_rounds(
|
|
|
79
79
|
ladok.search_course_rounds(code=args.course_code),
|
|
80
80
|
args.rounds)
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
if args.header:
|
|
83
|
+
data_writer.writerow([
|
|
84
|
+
"Course", "Round", "Component", "Student", "Grade", "Time"
|
|
85
|
+
])
|
|
85
86
|
for course_round in course_rounds:
|
|
86
87
|
data = extract_data_for_round(ladok, course_round, args)
|
|
87
88
|
|
|
@@ -91,6 +92,7 @@ for course_round in course_rounds:
|
|
|
91
92
|
student, grade, time]
|
|
92
93
|
)
|
|
93
94
|
@ We must take a course code and a delimiter as arguments.
|
|
95
|
+
We also want to know if we want a header or not.
|
|
94
96
|
<<add data command arguments to data parser>>=
|
|
95
97
|
data_parser.add_argument("course_code",
|
|
96
98
|
help="The course code of the course for which to export data")
|
|
@@ -100,6 +102,9 @@ data_parser.add_argument("-d", "--delimiter",
|
|
|
100
102
|
help="The delimiter for the CSV output; "
|
|
101
103
|
"default is a tab character to be compatible with POSIX commands, "
|
|
102
104
|
"use `-d,` or `-d ,` to get comma-separated values.")
|
|
105
|
+
|
|
106
|
+
data_parser.add_argument("-H", "--header", action="store_true",
|
|
107
|
+
help="Print a header line with the column names.")
|
|
103
108
|
@
|
|
104
109
|
|
|
105
110
|
We filter the rounds.
|
|
@@ -154,7 +159,20 @@ def extract_data_for_round(ladok, course_round, args):
|
|
|
154
159
|
else:
|
|
155
160
|
<<extract grade and normalized date from result data>>
|
|
156
161
|
|
|
157
|
-
yield student, component, grade
|
|
162
|
+
<<yield [[student, component, grade]] and date>>
|
|
163
|
+
@
|
|
164
|
+
|
|
165
|
+
We want to yield the data in CSV form, so we simply yield a tuple.
|
|
166
|
+
The date is either the normalized date or the date from the result data.
|
|
167
|
+
<<yield [[student, component, grade]] and date>>=
|
|
168
|
+
if args.normalize_date:
|
|
169
|
+
yield student, component, grade, normalized_date
|
|
170
|
+
elif result_data:
|
|
171
|
+
yield student, component, grade, result_data["Examinationsdatum"]
|
|
172
|
+
<<add data command arguments to data parser>>=
|
|
173
|
+
data_parser.add_argument("-n", "--normalize-date", action="store_true",
|
|
174
|
+
help="Normalize the date to the start of the course, "
|
|
175
|
+
"otherwise the date is printed as is.")
|
|
158
176
|
@
|
|
159
177
|
|
|
160
178
|
\subsection{Get round data}
|
ladok3/data.py
CHANGED
|
@@ -60,7 +60,10 @@ def extract_data_for_round(ladok, course_round, args):
|
|
|
60
60
|
grade = "-"
|
|
61
61
|
normalized_date = None
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
if args.normalize_date:
|
|
64
|
+
yield student, component, grade, normalized_date
|
|
65
|
+
elif result_data:
|
|
66
|
+
yield student, component, grade, result_data["Examinationsdatum"]
|
|
64
67
|
|
|
65
68
|
|
|
66
69
|
def filter_student_results(student, results):
|
|
@@ -132,7 +135,7 @@ def has_credit_transfer(results):
|
|
|
132
135
|
|
|
133
136
|
def add_command_options(parser):
|
|
134
137
|
data_parser = parser.add_parser(
|
|
135
|
-
"
|
|
138
|
+
"course",
|
|
136
139
|
help="Returns course results data in CSV form",
|
|
137
140
|
description="""
|
|
138
141
|
Returns the results in CSV form for all first-time registered students.
|
|
@@ -151,6 +154,13 @@ def add_command_options(parser):
|
|
|
151
154
|
"default is a tab character to be compatible with POSIX commands, "
|
|
152
155
|
"use `-d,` or `-d ,` to get comma-separated values.",
|
|
153
156
|
)
|
|
157
|
+
|
|
158
|
+
data_parser.add_argument(
|
|
159
|
+
"-H",
|
|
160
|
+
"--header",
|
|
161
|
+
action="store_true",
|
|
162
|
+
help="Print a header line with the column names.",
|
|
163
|
+
)
|
|
154
164
|
data_parser.add_argument(
|
|
155
165
|
"-r",
|
|
156
166
|
"--rounds",
|
|
@@ -158,6 +168,13 @@ def add_command_options(parser):
|
|
|
158
168
|
help="The round codes for the rounds to include, "
|
|
159
169
|
"otherwise all rounds will be included.",
|
|
160
170
|
)
|
|
171
|
+
data_parser.add_argument(
|
|
172
|
+
"-n",
|
|
173
|
+
"--normalize-date",
|
|
174
|
+
action="store_true",
|
|
175
|
+
help="Normalize the date to the start of the course, "
|
|
176
|
+
"otherwise the date is printed as is.",
|
|
177
|
+
)
|
|
161
178
|
data_parser.add_argument(
|
|
162
179
|
"-t",
|
|
163
180
|
"--time-limit",
|
|
@@ -188,7 +205,10 @@ def command(ladok, args):
|
|
|
188
205
|
ladok.search_course_rounds(code=args.course_code), args.rounds
|
|
189
206
|
)
|
|
190
207
|
|
|
191
|
-
|
|
208
|
+
if args.header:
|
|
209
|
+
data_writer.writerow(
|
|
210
|
+
["Course", "Round", "Component", "Student", "Grade", "Time"]
|
|
211
|
+
)
|
|
192
212
|
for course_round in course_rounds:
|
|
193
213
|
data = extract_data_for_round(ladok, course_round, args)
|
|
194
214
|
|
ladok3/report.nw
CHANGED
|
@@ -115,11 +115,24 @@ try:
|
|
|
115
115
|
set_grade(ladok, args,
|
|
116
116
|
student_id, course_code, component_code, grade, date, graders)
|
|
117
117
|
except Exception as err:
|
|
118
|
-
|
|
118
|
+
<<try to resolve [[student]] from [[ladok]] using [[student_id]]>>
|
|
119
|
+
print(f"{course_code} {component_code}={grade} ({date}) {student}: "
|
|
119
120
|
f"{err}",
|
|
120
121
|
file=sys.stderr)
|
|
121
122
|
@
|
|
122
123
|
|
|
124
|
+
The reason we want to resolve the student from LADOK is that the [[student_id]]
|
|
125
|
+
might be a personnummer or a LADOK ID---if the latter, it's not particularly
|
|
126
|
+
readable for a human and we can't use the LADOK ID in the LADOK web interface
|
|
127
|
+
when we want to deal with the errors manually.
|
|
128
|
+
But if we resolve the student, then we get a readable name.
|
|
129
|
+
<<try to resolve [[student]] from [[ladok]] using [[student_id]]>>=
|
|
130
|
+
try:
|
|
131
|
+
student = ladok.get_student(student_id)
|
|
132
|
+
except Exception:
|
|
133
|
+
student = student_id
|
|
134
|
+
@
|
|
135
|
+
|
|
123
136
|
When we set the grade, there are a few cases that should be handled.
|
|
124
137
|
If the grade isn't attested, we try to change it.
|
|
125
138
|
(This might still fail if the grade is finalized but not attested.)
|
|
@@ -254,8 +267,10 @@ try:
|
|
|
254
267
|
args.student_id, args.course_code, args.component_code,
|
|
255
268
|
args.grade, args.date, args.graders)
|
|
256
269
|
except Exception as err:
|
|
270
|
+
student_id = args.student_id
|
|
271
|
+
<<try to resolve [[student]] from [[ladok]] using [[student_id]]>>
|
|
257
272
|
print(f"{args.course_code} {args.component_code}={args.grade} ({args.date}) "
|
|
258
|
-
f"{
|
|
273
|
+
f"{student}: {err}",
|
|
259
274
|
file=sys.stderr)
|
|
260
275
|
@
|
|
261
276
|
|
ladok3/report.py
CHANGED
|
@@ -27,9 +27,14 @@ def report_one_result(ladok, args):
|
|
|
27
27
|
args.graders,
|
|
28
28
|
)
|
|
29
29
|
except Exception as err:
|
|
30
|
+
student_id = args.student_id
|
|
31
|
+
try:
|
|
32
|
+
student = ladok.get_student(student_id)
|
|
33
|
+
except Exception:
|
|
34
|
+
student = student_id
|
|
30
35
|
print(
|
|
31
36
|
f"{args.course_code} {args.component_code}={args.grade} ({args.date}) "
|
|
32
|
-
f"{
|
|
37
|
+
f"{student}: {err}",
|
|
33
38
|
file=sys.stderr,
|
|
34
39
|
)
|
|
35
40
|
|
|
@@ -49,9 +54,12 @@ def report_many_results(ladok, args):
|
|
|
49
54
|
graders,
|
|
50
55
|
)
|
|
51
56
|
except Exception as err:
|
|
57
|
+
try:
|
|
58
|
+
student = ladok.get_student(student_id)
|
|
59
|
+
except Exception:
|
|
60
|
+
student = student_id
|
|
52
61
|
print(
|
|
53
|
-
f"{course_code} {component_code}={grade} ({date}) {
|
|
54
|
-
f"{err}",
|
|
62
|
+
f"{course_code} {component_code}={grade} ({date}) {student}: " f"{err}",
|
|
55
63
|
file=sys.stderr,
|
|
56
64
|
)
|
|
57
65
|
|
ladok3/scripts.nw
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
In this chapter we'll see some scripts for reporting results from Canvas to
|
|
2
|
+
LADOK.
|
|
3
|
+
These scripts are run as cronjobs.
|
|
4
|
+
This means that they run unattended and only produce output when they need
|
|
5
|
+
attention.
|
|
6
|
+
|
|
7
|
+
We want to report results for courses that are titled \enquote{DD2520 VT25},
|
|
8
|
+
\enquote{DD1310 HT24}, \enquote{DD1317 HT24}, and similar in Canvas.
|
|
9
|
+
The advantage to using this command is that it will automatically report the
|
|
10
|
+
correct dates and everyone who has participated in the grading of each
|
|
11
|
+
student---as required by regulation.
|
|
12
|
+
The official tools, like KTH Transfer to Ladok or SUNET's version of the same,
|
|
13
|
+
don't do this.
|
|
14
|
+
They don't set the dates correctly, meaning that each individual should have a
|
|
15
|
+
separate date (date of submission).
|
|
16
|
+
They also don't register the graders in LADOK.
|
|
17
|
+
For each results, everyone who participated in the grading process should be
|
|
18
|
+
registered in LADOK.
|
|
19
|
+
|
|
20
|
+
We'll have a script [[<<ladok.sh>>]] that is run by [[cron]].
|
|
21
|
+
It's useful to load our profile, so that we have our normal environment.
|
|
22
|
+
<<ladok.sh>>=
|
|
23
|
+
#!/bin/bash
|
|
24
|
+
. ${HOME}/.profile
|
|
25
|
+
@
|
|
26
|
+
|
|
27
|
+
We also want some helper functions and a main function that is only run when
|
|
28
|
+
the script is run.
|
|
29
|
+
If the script is sourced, the main function is not run.
|
|
30
|
+
This way we can use the helper functions in our terminal.
|
|
31
|
+
<<ladok.sh>>=
|
|
32
|
+
<<constants>>
|
|
33
|
+
<<helper functions>>
|
|
34
|
+
|
|
35
|
+
main() {
|
|
36
|
+
<<main script>>
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Only run if this is the main script
|
|
40
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
41
|
+
main "$@"
|
|
42
|
+
fi
|
|
43
|
+
@
|
|
44
|
+
|
|
45
|
+
We need to report more than once for a course.
|
|
46
|
+
All students might not be done by the time of reporting.
|
|
47
|
+
When they're done, we must report the results again\footnote{%
|
|
48
|
+
And students have a right to do this without having to reregister on a later
|
|
49
|
+
course round.
|
|
50
|
+
}.
|
|
51
|
+
Sometimes a student finishes a course years after it ended.
|
|
52
|
+
To speed up execution it's better to report results for a sliding window of
|
|
53
|
+
courses, instead of all courses.
|
|
54
|
+
For those rare cases when a student finishes a course years after it ended, we
|
|
55
|
+
can report the results manually for that course again---by importing and
|
|
56
|
+
invoking the functions below in the terminal.
|
|
57
|
+
|
|
58
|
+
We'll let [[YEARS]] be a regex for the years that we're interested in reporting
|
|
59
|
+
for.
|
|
60
|
+
We'll use the current year and the previous year.
|
|
61
|
+
The advantage of this window is that when we pass new year's eve, the autumn
|
|
62
|
+
courses are still current for a few weeks---but the year is the wrong one.
|
|
63
|
+
As a side effect, during autumn we report results for any late results for the
|
|
64
|
+
previous year too.
|
|
65
|
+
|
|
66
|
+
To construct the regex for years, we simply take a sequence of years
|
|
67
|
+
([[24 25]]) and make a regex ([[(24|25)]]) out of it.
|
|
68
|
+
<<constants>>=
|
|
69
|
+
YEAR=$(date +%y)
|
|
70
|
+
YEARS=$(echo -n "("; \
|
|
71
|
+
seq $((YEAR - 1)) $YEAR \
|
|
72
|
+
| sed -zE "s/\s/|/g" \
|
|
73
|
+
| sed "s/|$/)/")
|
|
74
|
+
@
|
|
75
|
+
|
|
76
|
+
\section{Reporting results on course components}
|
|
77
|
+
|
|
78
|
+
We want a script that reports the results for all courses, including all
|
|
79
|
+
previous years, to LADOK.
|
|
80
|
+
We want to do this for previous years as sometimes they finish assignments
|
|
81
|
+
years after the course ended.
|
|
82
|
+
|
|
83
|
+
We'll add a function that takes a course regex and a component regex and
|
|
84
|
+
reports the results to LADOK.
|
|
85
|
+
To report the modules, we sometimes need to override the default summary
|
|
86
|
+
function of [[canvaslms]]\footnote{\label{canvaslms-doc}%
|
|
87
|
+
For details on [[canvaslms results]], see Chapters 10 and 11 in its
|
|
88
|
+
documentation, found on
|
|
89
|
+
\url{https://github.com/dbosk/canvaslms/releases/tag/v4.7}.
|
|
90
|
+
Particularly Chapter 11 discusses the summary modules.
|
|
91
|
+
You can also read [[pydoc canvaslms.grades]] for a more brief summary.
|
|
92
|
+
}.
|
|
93
|
+
We want something like this:
|
|
94
|
+
<<main script>>=
|
|
95
|
+
report_components "DD1301 HT${YEARS}" \
|
|
96
|
+
LAB1
|
|
97
|
+
report_components "DD131[057] HT${YEARS}" \
|
|
98
|
+
"(LAB|MAT|KAL)[1-3]"
|
|
99
|
+
report_components "DA2215 [HV]T${YEARS}" \
|
|
100
|
+
INL1
|
|
101
|
+
|
|
102
|
+
report_components "DD2520 VT${YEARS}" \
|
|
103
|
+
INL1
|
|
104
|
+
report_components "DD2520 VT${YEARS}" \
|
|
105
|
+
LAB1 \
|
|
106
|
+
canvaslms.grades.tilkryLAB1
|
|
107
|
+
@ Note that the line reporting for DD1310 reports for \emph{all} instances as
|
|
108
|
+
well---that course is given five times in parallel.
|
|
109
|
+
But the assessment should be the same so it is sufficient that one of the
|
|
110
|
+
examiners run this script to report all the results.
|
|
111
|
+
|
|
112
|
+
To get the results out of Canvas we'll use the [[canvaslms results]] command.
|
|
113
|
+
We must install the [[canvaslms]] tool.
|
|
114
|
+
We can do this by running:
|
|
115
|
+
\begin{minted}{bash}
|
|
116
|
+
python3 -m pip install canvaslms # to use it with Python, or
|
|
117
|
+
pipx install canvaslms # to only use the CLI
|
|
118
|
+
canvaslms login # or read `canvaslms login --help`
|
|
119
|
+
\end{minted}
|
|
120
|
+
For details on how to extract the results, read
|
|
121
|
+
\mintinline{bash}{canvaslms results --help}
|
|
122
|
+
(also read \cref{canvaslms-doc} on page~\pageref{canvaslms-doc}).
|
|
123
|
+
<<helper functions>>=
|
|
124
|
+
report_components() {
|
|
125
|
+
local course="$1"
|
|
126
|
+
local component="$2"
|
|
127
|
+
local summary_module="$3"
|
|
128
|
+
|
|
129
|
+
local summary_opt=""
|
|
130
|
+
if [[ -n "$summary_module" ]]; then
|
|
131
|
+
summary_opt="-S $summary_module"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
# Get the course results from Canvas.
|
|
135
|
+
canvaslms results -c "$course" -A "$component" $summary_opt \
|
|
136
|
+
| sed -E "s/ ?[HV]T[0-9]{2}( \(.*\))?//" \
|
|
137
|
+
| <<[[tee]] the component results to use for course grades>> \
|
|
138
|
+
| ladok report -fv # Report them to LADOK.
|
|
139
|
+
}
|
|
140
|
+
@
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
\section{Reporting course grade}
|
|
144
|
+
|
|
145
|
+
Now we can set the course grades based on the reported components.
|
|
146
|
+
We'll have to report one course at a time since the course grade is based on
|
|
147
|
+
different components in different courses.
|
|
148
|
+
<<main script>>=
|
|
149
|
+
report_course "DD131[057] HT${YEARS}" \
|
|
150
|
+
LAB3
|
|
151
|
+
report_course "DD2520 VT${YEARS}" \
|
|
152
|
+
LAB1
|
|
153
|
+
@ We don't need to report the course grade for the other courses since they
|
|
154
|
+
have only one component.
|
|
155
|
+
For courses with only one component, LADOK will automatically set the course
|
|
156
|
+
grade based on the grade of the single component.
|
|
157
|
+
|
|
158
|
+
Setting the course grade can be done in several ways.
|
|
159
|
+
The first option is to look at what results are already attested in LADOK.
|
|
160
|
+
Unfortunately, this requires the round code---which we don't have access
|
|
161
|
+
to\footnote{%
|
|
162
|
+
For a brief period, IT included the round code in the course title in Canvas
|
|
163
|
+
at KTH.
|
|
164
|
+
That was beneficial in many ways, but unfortunately it faced a backlash from
|
|
165
|
+
teachers and it was undone.
|
|
166
|
+
}.
|
|
167
|
+
However, we can get this data from the [[canvaslms results]] line in
|
|
168
|
+
[[report_components]].
|
|
169
|
+
That's why we want to [[tee]] that data out of that pipeline.
|
|
170
|
+
|
|
171
|
+
When we [[tee]] the data out, we want to use the [[-a]] option to append if the
|
|
172
|
+
file already exists.
|
|
173
|
+
The reason for this is that we want all results for the course in one file.
|
|
174
|
+
But sometimes we might have to run the script several times---once for each
|
|
175
|
+
component.
|
|
176
|
+
<<[[tee]] the component results to use for course grades>>=
|
|
177
|
+
tee -a "${DATA_DIR}/${course}-results.csv"
|
|
178
|
+
<<constants>>=
|
|
179
|
+
DATA_DIR=`mktemp -d`
|
|
180
|
+
@
|
|
181
|
+
|
|
182
|
+
We'll provide a function that takes a course and a component and returns the
|
|
183
|
+
results for that course and component.
|
|
184
|
+
If no component is given, we use all components.
|
|
185
|
+
If we don't have results from before, we report those components to get the
|
|
186
|
+
data.
|
|
187
|
+
<<helper functions>>=
|
|
188
|
+
component_grades() {
|
|
189
|
+
local course="$1"
|
|
190
|
+
local component="${2:-[A-Z]{3}[0-9]+}"
|
|
191
|
+
local grades="${DATA_DIR}/${course}-results.csv"
|
|
192
|
+
|
|
193
|
+
if [[ ! -f "$grades" ]]; then
|
|
194
|
+
report_components "$course" "$component"
|
|
195
|
+
fi
|
|
196
|
+
cat "$grades" \
|
|
197
|
+
| grep -E "\s${component}\s" \
|
|
198
|
+
| sort -u
|
|
199
|
+
}
|
|
200
|
+
@ The data we get here has the following columns (tab separated):
|
|
201
|
+
\begin{minted}{text}
|
|
202
|
+
course component student grade date graders
|
|
203
|
+
\end{minted}
|
|
204
|
+
|
|
205
|
+
Now we can use this file when reporting the course grades.
|
|
206
|
+
If the file doesn't exist, we simply run [[report_components]].
|
|
207
|
+
If the results are not yet attested (certified), the [[ladok report]] command
|
|
208
|
+
will simply give an error that all components of the course are not yet
|
|
209
|
+
attested.
|
|
210
|
+
|
|
211
|
+
Now it's just to sort out the students and then for each student get the grade
|
|
212
|
+
of the component, get the latest grade date of all components and finally
|
|
213
|
+
report to LADOK.
|
|
214
|
+
<<helper functions>>=
|
|
215
|
+
report_course() {
|
|
216
|
+
local course="$1"
|
|
217
|
+
local component="$2"
|
|
218
|
+
|
|
219
|
+
for student in $(component_grades "$course" \
|
|
220
|
+
| cut -f 3)
|
|
221
|
+
do
|
|
222
|
+
local grade=$(component_grades "$course" "$component" \
|
|
223
|
+
| grep "$student" \
|
|
224
|
+
| cut -f 4)
|
|
225
|
+
local grade_date=$(component_grades "$course" \
|
|
226
|
+
| grep "$student" \
|
|
227
|
+
| cut -f 5 \
|
|
228
|
+
| sort \
|
|
229
|
+
| tail -n 1)
|
|
230
|
+
|
|
231
|
+
if [ "$grade" = "" ]; then
|
|
232
|
+
continue
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
local course_code=$(component_grades "$course" "$component" \
|
|
236
|
+
| grep "$student" \
|
|
237
|
+
| cut -f 1 \
|
|
238
|
+
| sort -u)
|
|
239
|
+
|
|
240
|
+
# `component code = course code` yields final grade on course.
|
|
241
|
+
ladok report -fv "$course_code" "$course_code" \
|
|
242
|
+
"$student" "$grade" "$grade_date"
|
|
243
|
+
done
|
|
244
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
doc/ltxobj/ladok3.pdf,sha256=ffhkazbKXtrPNApa39g96qOlnuwK5QniFQkqAnj4eLc,2308912
|
|
2
|
+
ladok3/.gitignore,sha256=R1rBunOQmdfni2mCHqmtU3vDQggAZdeu5qIdK89jRtE,90
|
|
3
|
+
ladok3/Makefile,sha256=V1eijeRoVPOcl0QCoIVzc2d1p6goJfu3PNynINM-ZnU,715
|
|
4
|
+
ladok3/__init__.py,sha256=2ySW9vSJ0joOV5TH9dtK_CAOdk1PByZ7jYfAwQxfAhc,87708
|
|
5
|
+
ladok3/api.nw,sha256=o7ZWO6eplSG3ReS0Y-rzcrLP2yiFVwvQUtg6dPDXG5E,66716
|
|
6
|
+
ladok3/cli.nw,sha256=sA5kevvAWBkzZHZ3UBkPEHoWvPw5yYtGYiPEnWMmjus,22864
|
|
7
|
+
ladok3/cli.py,sha256=6R7B0XVvYFlHcMbv6E73KOeI3ZGQFeQN2-ojz12H1vU,12555
|
|
8
|
+
ladok3/data.nw,sha256=NddmEnzfgaXZkYyaOLle67MNySy4YKWx4OXMWAOAMkA,12676
|
|
9
|
+
ladok3/data.py,sha256=nLta8ZlSmgRqXZoCbJqtw6KlHH-EyZfImbDcPs74G2U,7324
|
|
10
|
+
ladok3/ladok.bash,sha256=zGfTFdtos2zLjV13pzfK-1uCy2b_lF2qUKMoL2ExW7c,1441
|
|
11
|
+
ladok3/ladok3.nw,sha256=mftysbBD9CLaKD8JgRcSO9KOnjV6koOd2dfmbSSJJi0,55594
|
|
12
|
+
ladok3/report.nw,sha256=nmyP6TnSYBsMIeG2bdiVQ7LqQN_gEqyEfbDrrOR1afc,10068
|
|
13
|
+
ladok3/report.py,sha256=BDFbTsO2mwFQoCysRnak2VYA3p4RYOnxaU6zamhqbAY,6091
|
|
14
|
+
ladok3/scripts.nw,sha256=BgKUOEUc1JeShPvGLV4OJLQwrmCOyxUoe7pAeW00d34,8906
|
|
15
|
+
ladok3/student.nw,sha256=qgJN1IA6oIoI4RsfWsoTaO2yxdtB_y3mBNSmlccoE4Y,4395
|
|
16
|
+
ladok3/student.py,sha256=a3z5dY18UXqxZh4WtEhgsUdHePhRNBiU5GaNNP6LKxI,2178
|
|
17
|
+
ladok3/undoc.nw,sha256=rRAZ8fC44PD2hBDF8TeEg1hyge_-vd-ScRcojRXQycI,19841
|
|
18
|
+
ladok3-5.1.dist-info/LICENSE,sha256=Oe-mWTBQ-MzuA4jGuRDPDxQtMe8SuP_YjSQl7SOtZ7Q,1155
|
|
19
|
+
ladok3-5.1.dist-info/METADATA,sha256=wMSkWRA_WBSYjLyfODj60Q8t-u7u5HDArOtFpkalBWc,9370
|
|
20
|
+
ladok3-5.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
21
|
+
ladok3-5.1.dist-info/entry_points.txt,sha256=pi-KKP5Obo0AyuDjXQUpadS9kIvAY2_5ORhPgEYlJv8,41
|
|
22
|
+
ladok3-5.1.dist-info/RECORD,,
|
ladok3-4.19.dist-info/RECORD
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
doc/ltxobj/ladok3.pdf,sha256=ibiFIK0Vn-_0DokQTYSUmwTAG7deD5Sqb4i-3g3HWRc,2279853
|
|
2
|
-
ladok3/.gitignore,sha256=QOcCshtjIsFasi4vaqFcooBWPJkxVWaoYEWOBBtdY_w,81
|
|
3
|
-
ladok3/Makefile,sha256=Jy6OFjoVLU9YivnVxctxI_zrUOK9ysEOiistJ3ST6Nw,557
|
|
4
|
-
ladok3/__init__.py,sha256=2ySW9vSJ0joOV5TH9dtK_CAOdk1PByZ7jYfAwQxfAhc,87708
|
|
5
|
-
ladok3/api.nw,sha256=o7ZWO6eplSG3ReS0Y-rzcrLP2yiFVwvQUtg6dPDXG5E,66716
|
|
6
|
-
ladok3/cli.nw,sha256=sA5kevvAWBkzZHZ3UBkPEHoWvPw5yYtGYiPEnWMmjus,22864
|
|
7
|
-
ladok3/cli.py,sha256=6R7B0XVvYFlHcMbv6E73KOeI3ZGQFeQN2-ojz12H1vU,12555
|
|
8
|
-
ladok3/data.nw,sha256=3o6-kmeMtCGoSJ5yL8qFCuIINQeym_WtW_2mhItuR-s,11785
|
|
9
|
-
ladok3/data.py,sha256=kPRO9l5DTQb9lGnN2kU-YYPSyg31t0bq5HCw986hbPk,6747
|
|
10
|
-
ladok3/ladok.bash,sha256=zGfTFdtos2zLjV13pzfK-1uCy2b_lF2qUKMoL2ExW7c,1441
|
|
11
|
-
ladok3/ladok3.nw,sha256=mftysbBD9CLaKD8JgRcSO9KOnjV6koOd2dfmbSSJJi0,55594
|
|
12
|
-
ladok3/report.nw,sha256=B_xZ06r-xCJMi9lIo-oTA6eLnN4Jnci1G_2YJM4-2CQ,9406
|
|
13
|
-
ladok3/report.py,sha256=fTrolQMwaSVQ9_lUdUqRQcj4KKHC_m7S3SUP1lTHU0M,5817
|
|
14
|
-
ladok3/student.nw,sha256=qgJN1IA6oIoI4RsfWsoTaO2yxdtB_y3mBNSmlccoE4Y,4395
|
|
15
|
-
ladok3/student.py,sha256=a3z5dY18UXqxZh4WtEhgsUdHePhRNBiU5GaNNP6LKxI,2178
|
|
16
|
-
ladok3/undoc.nw,sha256=rRAZ8fC44PD2hBDF8TeEg1hyge_-vd-ScRcojRXQycI,19841
|
|
17
|
-
ladok3-4.19.dist-info/LICENSE,sha256=Oe-mWTBQ-MzuA4jGuRDPDxQtMe8SuP_YjSQl7SOtZ7Q,1155
|
|
18
|
-
ladok3-4.19.dist-info/METADATA,sha256=9rhTkRUfmdmEm4pZQfNZAiSi2C4bM9TlZ8DiUrRCc5k,9371
|
|
19
|
-
ladok3-4.19.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
20
|
-
ladok3-4.19.dist-info/entry_points.txt,sha256=pi-KKP5Obo0AyuDjXQUpadS9kIvAY2_5ORhPgEYlJv8,41
|
|
21
|
-
ladok3-4.19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|