ladok3 4.19__tar.gz → 5.1__tar.gz

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.

Files changed (35) hide show
  1. {ladok3-4.19 → ladok3-5.1}/PKG-INFO +1 -1
  2. {ladok3-4.19 → ladok3-5.1}/doc/Makefile +1 -0
  3. ladok3-5.1/doc/abstract.tex +8 -0
  4. {ladok3-4.19 → ladok3-5.1}/doc/ladok3.tex +62 -25
  5. {ladok3-4.19 → ladok3-5.1}/pyproject.toml +1 -1
  6. {ladok3-4.19 → ladok3-5.1}/src/ladok3/.gitignore +1 -0
  7. {ladok3-4.19 → ladok3-5.1}/src/ladok3/Makefile +6 -0
  8. {ladok3-4.19 → ladok3-5.1}/src/ladok3/data.nw +26 -8
  9. {ladok3-4.19 → ladok3-5.1}/src/ladok3/report.nw +17 -2
  10. ladok3-5.1/src/ladok3/scripts.nw +244 -0
  11. ladok3-4.19/doc/abstract.tex +0 -3
  12. {ladok3-4.19 → ladok3-5.1}/LICENSE +0 -0
  13. {ladok3-4.19 → ladok3-5.1}/README.md +0 -0
  14. {ladok3-4.19 → ladok3-5.1}/doc/preamble.tex +0 -0
  15. {ladok3-4.19 → ladok3-5.1}/makefiles/doc.mk +0 -0
  16. {ladok3-4.19 → ladok3-5.1}/makefiles/exam.mk +0 -0
  17. {ladok3-4.19 → ladok3-5.1}/makefiles/haskell.mk +0 -0
  18. {ladok3-4.19 → ladok3-5.1}/makefiles/miun.course.mk +0 -0
  19. {ladok3-4.19 → ladok3-5.1}/makefiles/miun.depend.mk +0 -0
  20. {ladok3-4.19 → ladok3-5.1}/makefiles/miun.docs.mk +0 -0
  21. {ladok3-4.19 → ladok3-5.1}/makefiles/miun.port.mk +0 -0
  22. {ladok3-4.19 → ladok3-5.1}/makefiles/miun.pub.mk +0 -0
  23. {ladok3-4.19 → ladok3-5.1}/makefiles/noweb.mk +0 -0
  24. {ladok3-4.19 → ladok3-5.1}/makefiles/pkg.mk +0 -0
  25. {ladok3-4.19 → ladok3-5.1}/makefiles/portability.mk +0 -0
  26. {ladok3-4.19 → ladok3-5.1}/makefiles/pub.mk +0 -0
  27. {ladok3-4.19 → ladok3-5.1}/makefiles/results.mk +0 -0
  28. {ladok3-4.19 → ladok3-5.1}/makefiles/subdir.mk +0 -0
  29. {ladok3-4.19 → ladok3-5.1}/makefiles/tex.mk +0 -0
  30. {ladok3-4.19 → ladok3-5.1}/makefiles/transform.mk +0 -0
  31. {ladok3-4.19 → ladok3-5.1}/src/ladok3/api.nw +0 -0
  32. {ladok3-4.19 → ladok3-5.1}/src/ladok3/cli.nw +0 -0
  33. {ladok3-4.19 → ladok3-5.1}/src/ladok3/ladok3.nw +0 -0
  34. {ladok3-4.19 → ladok3-5.1}/src/ladok3/student.nw +0 -0
  35. {ladok3-4.19 → ladok3-5.1}/src/ladok3/undoc.nw +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ladok3
3
- Version: 4.19
3
+ Version: 5.1
4
4
  Summary: Python wrapper and CLI for the LADOK3 REST API.
5
5
  Home-page: https://github.com/dbosk/ladok3
6
6
  License: MIT
@@ -24,6 +24,7 @@ ladok3.pdf: ../examples/example_LadokSession.py
24
24
  ladok3.pdf: ../examples/example_Student.py
25
25
  ladok3.pdf: ../examples/example_Course.py
26
26
  ladok3.pdf: ../examples/canvas2ladok.tex
27
+ ladok3.pdf: ../src/ladok3/scripts.tex
27
28
 
28
29
  ladok3.pdf: didactic.sty
29
30
 
@@ -0,0 +1,8 @@
1
+ We provide a Python wrapper for the LADOK3 REST API\@.
2
+ This wrapper provide a useful object-oriented framework for working with LADOK
3
+ and direct API calls that return the unprocessed JSON data directly from
4
+ LADOK\@.
5
+
6
+ We also provide a command-line interface (CLI) for the library.
7
+ We then construct some useful (shell) scripts using this CLI\@.
8
+ These scripts automates the reporting of results from Canvas to LADOK\@.
@@ -46,10 +46,10 @@ higher education in Sweden.
46
46
  This is the documented source code of \texttt{ladok3}, a LADOK3 API wrapper for
47
47
  Python.
48
48
 
49
- The \texttt{ladok3} library provides a non-GUI application that, similar to
50
- access via a web browser, only provides the user with access to the LADOK data
51
- and functionality that they would actually have based on their specific user
52
- permissions in LADOK.
49
+ The \texttt{ladok3} library provides a non-GUI application (a command-line
50
+ interface, CLI) that, similar to access via a web browser, only provides the
51
+ user with access to the LADOK data and functionality that they would actually
52
+ have based on their specific user permissions in LADOK.
53
53
  It can be seem as a very streamlined web browser just for LADOK's web
54
54
  interface.
55
55
  While the library exploits caching to reduce the load on the LADOK server, this
@@ -58,15 +58,27 @@ LADOK's web GUI export functions.
58
58
 
59
59
  You can install the \texttt{ladok3} package by running
60
60
  \begin{minted}{bash}
61
- pip3 install ladok3
61
+ python3 -m pip install ladok3 # to use it in Python, or
62
+ pipx install ladok3 # to just use the CLI
62
63
  \end{minted}
63
64
  in the terminal.
64
- You can find a quick reference by running
65
+
66
+ Then you can use the CLI by running \mintinline{bash}{ladok} in the terminal.
67
+ The command has built-in help, simply run \mintinline{bash}{ladok -h} to see
68
+ the available commands.
69
+ However, the first thing you want to run after installing the package is
65
70
  \begin{minted}[firstnumber=last]{bash}
66
- pydoc ladok3
71
+ ladok login
67
72
  \end{minted}
73
+ This will set up your credentials for the CLI.
74
+ However, if you want to use the library in a script, you can read
75
+ \begin{minted}[firstnumber=last]{bash}
76
+ ladok login -h
77
+ \end{minted}
78
+ for alternative ways of providing your credentials.
68
79
 
69
- We provide the main class \texttt{LadokSession} (\cref{LadokSession}).
80
+ For use in Python scripts,
81
+ we provide the main class \texttt{LadokSession} (\cref{LadokSession}).
70
82
  The \texttt{LadokSession} class acts like \enquote{factories} and will return
71
83
  objects representing various LADOK data.
72
84
  These data objects' classes inherit the \texttt{LadokData} (\cref{LadokData})
@@ -83,27 +95,55 @@ We do this by caching all factory methods of any \texttt{LadokSession}.
83
95
  The \texttt{LadokSession} itself is also designed to be cacheable: if the session to
84
96
  LADOK expires, it will automatically reauthenticate to establish a new session.
85
97
 
98
+ You can find a quick reference by running
99
+ \begin{minted}[firstnumber=last]{bash}
100
+ pydoc ladok3 # doesn't work if installed with pipx
101
+ \end{minted}
86
102
 
87
103
 
88
- \part{The library}
89
104
 
90
- \input{../src/ladok3/ladok3.tex}
105
+ \part{Example applications}
91
106
 
107
+ \chapter{Transfer results from Canvas to LADOK}\label{SomeScripts}
92
108
 
93
- \part{API calls}
109
+ \input{../src/ladok3/scripts.tex}
94
110
 
95
- \input{../src/ladok3/api.tex}
96
- \input{../src/ladok3/undoc.tex}
111
+ \chapter{Transfer results from Canvas to LADOK in Python}
112
+
113
+ Here we provide an example program~\texttt{canvas2ladok.py} which exports
114
+ results from Canvas to LADOK for the introductory programming course~prgi
115
+ (DD1315).
116
+
117
+ However, a better way to do this is by using the CLI
118
+ (see \cref{SomeScripts} for an even better version):
119
+ \begin{minted}{bash}
120
+ #!/bin/bash
97
121
 
122
+ . ${HOME}/.profile
98
123
 
124
+ year=22
125
+ courses="DD13(10HT${year}|17HT${year})"
126
+ components="(LAB[123]|MAT1|KAL1)"
99
127
 
100
- \part{A command-line interface}
128
+ canvaslms results -c "$courses" -A "$components" \
129
+ | sed -E "s/ ?[HV]T[0-9]{2}( \(.*\))?//" \
130
+ | ladok report -fv
131
+ \end{minted}
132
+
133
+ But now we'll have a look at how we can do this (well, a simpler version) in
134
+ Python.
135
+
136
+
137
+ \input{../examples/canvas2ladok.tex}
138
+
139
+
140
+ \part{The command-line interface}
101
141
 
102
142
  \chapter{The base interface}
103
143
 
104
144
  \input{../src/ladok3/cli.tex}
105
145
 
106
- \chapter{The \texttt{data} command}
146
+ \chapter{The \texttt{course} command}
107
147
 
108
148
  \input{../src/ladok3/data.tex}
109
149
 
@@ -117,19 +157,16 @@ LADOK expires, it will automatically reauthenticate to establish a new session.
117
157
 
118
158
 
119
159
 
120
- \part{Other example applications}
160
+ \part{The Python library}
161
+
162
+ \input{../src/ladok3/ladok3.tex}
121
163
 
122
- \chapter{Transfer results from KTH Canvas to LADOK}
123
164
 
124
- Here we provide an example program~\texttt{canvas2ladok.py} which exports
125
- results from KTH Canvas to LADOK for the introductory programming course~prgi
126
- (DD1315).
127
- You can find an up-to-date version of this chapter at
128
- \begin{center}
129
- \url{https://github.com/dbosk/intropy/tree/master/adm/reporting}.
130
- \end{center}
165
+ \part{API calls}
166
+
167
+ \input{../src/ladok3/api.tex}
168
+ \input{../src/ladok3/undoc.tex}
131
169
 
132
- \input{../examples/canvas2ladok.tex}
133
170
 
134
171
 
135
172
  \backmatter
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "ladok3"
3
- version = "4.19"
3
+ version = "5.1"
4
4
  description = "Python wrapper and CLI for the LADOK3 REST API."
5
5
  authors = [
6
6
  "Daniel Bosk <dbosk@kth.se>",
@@ -8,3 +8,4 @@ prgi.py
8
8
  prgi.tex
9
9
  ladok.bash
10
10
  test.bash
11
+ ladok.sh
@@ -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:
@@ -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 data DD1315 > DD1315.csv
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 time.
29
+ \item either absolute date or normalized to the course start and finish.
30
30
  \end{itemize}
31
31
 
32
32
 
33
- \section{The [[data]] subcommand}\label{DataCommand}
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("data",
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
- data_writer.writerow([
83
- "Course", "Round", "Component", "Student", "Grade", "Time"
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, normalized_date
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}
@@ -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
- print(f"{course_code} {component_code}={grade} ({date}) {student_id}: "
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"{args.student_id}: {err}",
273
+ f"{student}: {err}",
259
274
  file=sys.stderr)
260
275
  @
261
276
 
@@ -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
+ }
@@ -1,3 +0,0 @@
1
- We provide a Python wrapper for the LADOK3 REST API\@.
2
- We provide a useful object-oriented framework and direct API calls that return
3
- the unprocessed JSON data from LADOK.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes