excel2moodle 0.3.4__py3-none-any.whl → 0.3.6__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.
- excel2moodle/__init__.py +16 -88
- excel2moodle/__main__.py +9 -1
- excel2moodle/core/__init__.py +1 -3
- excel2moodle/core/category.py +32 -32
- excel2moodle/core/dataStructure.py +35 -51
- excel2moodle/core/etHelpers.py +12 -17
- excel2moodle/core/exceptions.py +1 -1
- excel2moodle/core/globals.py +2 -1
- excel2moodle/core/numericMultiQ.py +11 -12
- excel2moodle/core/parser.py +121 -102
- excel2moodle/core/question.py +32 -30
- excel2moodle/core/questionValidator.py +28 -19
- excel2moodle/core/questionWriter.py +232 -139
- excel2moodle/core/stringHelpers.py +16 -23
- excel2moodle/extra/equationVerification.py +13 -27
- excel2moodle/logger.py +101 -0
- excel2moodle/ui/appUi.py +97 -102
- excel2moodle/ui/dialogs.py +40 -4
- excel2moodle/ui/settings.py +105 -54
- excel2moodle/ui/treewidget.py +13 -10
- excel2moodle/ui/windowMain.py +18 -57
- {excel2moodle-0.3.4.dist-info → excel2moodle-0.3.6.dist-info}/METADATA +1 -1
- excel2moodle-0.3.6.dist-info/RECORD +33 -0
- {excel2moodle-0.3.4.dist-info → excel2moodle-0.3.6.dist-info}/WHEEL +1 -1
- excel2moodle-0.3.4.dist-info/RECORD +0 -32
- {excel2moodle-0.3.4.dist-info → excel2moodle-0.3.6.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.3.4.dist-info → excel2moodle-0.3.6.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.4.dist-info → excel2moodle-0.3.6.dist-info}/top_level.txt +0 -0
@@ -1,15 +1,16 @@
|
|
1
|
-
"""This Module checks if the data inside the Spreadsheet is valid
|
1
|
+
"""This Module checks if the data inside the Spreadsheet is valid.
|
2
2
|
|
3
3
|
Those things are considered:
|
4
4
|
|
5
5
|
#. The mandatory entries must not be ``Nan``
|
6
6
|
#. All fields must have the right data-type
|
7
7
|
|
8
|
-
If Those checks pass, a question is created,
|
8
|
+
If Those checks pass, a question is created,
|
9
|
+
which can be accessed via ``Validator.question``
|
9
10
|
"""
|
10
11
|
|
11
12
|
import logging
|
12
|
-
from
|
13
|
+
from typing import TYPE_CHECKING
|
13
14
|
|
14
15
|
import pandas as pd
|
15
16
|
|
@@ -17,10 +18,18 @@ from excel2moodle.core.exceptions import InvalidFieldException
|
|
17
18
|
from excel2moodle.core.globals import DFIndex
|
18
19
|
from excel2moodle.core.question import Question
|
19
20
|
|
21
|
+
if TYPE_CHECKING:
|
22
|
+
from types import UnionType
|
23
|
+
|
20
24
|
logger = logging.getLogger(__name__)
|
21
25
|
|
22
26
|
|
23
27
|
class Validator:
|
28
|
+
"""Validate the question data from the spreadsheet.
|
29
|
+
|
30
|
+
Creates a dictionary with the data, for easier access later.
|
31
|
+
"""
|
32
|
+
|
24
33
|
def __init__(self, category) -> None:
|
25
34
|
self.question: Question
|
26
35
|
self.category = category
|
@@ -31,15 +40,16 @@ class Validator:
|
|
31
40
|
}
|
32
41
|
self.optional: dict[DFIndex, type | UnionType] = {
|
33
42
|
DFIndex.BPOINTS: str,
|
34
|
-
DFIndex.NAME: str,
|
35
43
|
DFIndex.PICTURE: int | str,
|
36
44
|
}
|
37
|
-
self.nfOpt: dict[DFIndex, type | UnionType] = {
|
45
|
+
self.nfOpt: dict[DFIndex, type | UnionType] = {}
|
46
|
+
self.nfMand: dict[DFIndex, type | UnionType] = {
|
38
47
|
DFIndex.RESULT: float | int,
|
39
48
|
}
|
40
|
-
self.nfMand: dict[DFIndex, type | UnionType] = {}
|
41
49
|
self.nfmOpt: dict[DFIndex, type | UnionType] = {}
|
42
|
-
self.nfmMand: dict[DFIndex, type | UnionType] = {
|
50
|
+
self.nfmMand: dict[DFIndex, type | UnionType] = {
|
51
|
+
DFIndex.RESULT: str,
|
52
|
+
}
|
43
53
|
self.mcOpt: dict[DFIndex, type | UnionType] = {}
|
44
54
|
self.mcMand: dict[DFIndex, type | UnionType] = {
|
45
55
|
DFIndex.TRUE: str,
|
@@ -70,7 +80,7 @@ class Validator:
|
|
70
80
|
raise InvalidFieldException(msg, id, missing)
|
71
81
|
checker, missing = self._typeCheck()
|
72
82
|
if not checker:
|
73
|
-
msg = f"Question {id}
|
83
|
+
msg = f"Question {id} has wrong typed data {missing}"
|
74
84
|
if missing is not None:
|
75
85
|
raise InvalidFieldException(msg, id, missing)
|
76
86
|
self._getQuestion()
|
@@ -81,7 +91,6 @@ class Validator:
|
|
81
91
|
self.qdata: dict[str, str | float | int | list] = {}
|
82
92
|
for idx, val in self.df.items():
|
83
93
|
if not isinstance(idx, str):
|
84
|
-
logger.debug(f"Got a non String key in the spreadsheet, skipping it")
|
85
94
|
continue
|
86
95
|
if idx in self.qdata:
|
87
96
|
if isinstance(self.qdata[idx], list):
|
@@ -93,9 +102,9 @@ class Validator:
|
|
93
102
|
self.qdata[idx] = val
|
94
103
|
|
95
104
|
def _mandatory(self) -> tuple[bool, DFIndex | None]:
|
96
|
-
"""
|
105
|
+
"""Detects if all keys of mandatory are filled with values."""
|
97
106
|
checker = pd.Series.notna(self.df)
|
98
|
-
for k in self.mandatory
|
107
|
+
for k in self.mandatory:
|
99
108
|
try:
|
100
109
|
c = checker[k]
|
101
110
|
except KeyError:
|
@@ -112,24 +121,24 @@ class Validator:
|
|
112
121
|
for field, typ in self.mandatory.items():
|
113
122
|
if isinstance(self.df[field], pd.Series):
|
114
123
|
for f in self.df[field]:
|
115
|
-
if pd.notna(f):
|
116
|
-
|
117
|
-
invalid.append(field)
|
124
|
+
if pd.notna(f) and not isinstance(f, typ):
|
125
|
+
invalid.append(field)
|
118
126
|
elif not isinstance(self.df[field], typ):
|
119
127
|
invalid.append(field)
|
120
128
|
for field, typ in self.optional.items():
|
121
129
|
if field in self.df:
|
122
|
-
if not isinstance(self.df[field], typ):
|
130
|
+
if not isinstance(self.df[field], typ) and pd.notna(self.df[field]):
|
123
131
|
invalid.append(field)
|
124
132
|
if len(invalid) == 0:
|
125
133
|
return True, None
|
126
|
-
|
127
|
-
return False, invalid
|
134
|
+
return False, invalid
|
128
135
|
|
129
136
|
def _getQuestion(self) -> None:
|
130
137
|
name = self.df[DFIndex.NAME]
|
131
138
|
qtype = self.df[DFIndex.TYPE]
|
132
139
|
self.question = Question(
|
133
|
-
self.category,
|
140
|
+
self.category,
|
141
|
+
name=str(name),
|
142
|
+
number=self.index,
|
143
|
+
qtype=str(qtype),
|
134
144
|
)
|
135
|
-
return None
|
@@ -1,174 +1,267 @@
|
|
1
|
-
"""This Module holds the related Functions for writing the Questions to an xml-File
|
1
|
+
"""This Module holds the related Functions for writing the Questions to an xml-File.
|
2
2
|
|
3
3
|
It is planned to rework those Functions, because they're not quite elegant.
|
4
4
|
"""
|
5
5
|
|
6
|
-
from .stringHelpers import get_bullet_string
|
7
6
|
|
8
|
-
def write_question_MC(
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
def write_question_MC(
|
8
|
+
save_dir,
|
9
|
+
ID,
|
10
|
+
name,
|
11
|
+
s_1,
|
12
|
+
s_2,
|
13
|
+
s_3,
|
14
|
+
points_avail,
|
15
|
+
ans_type,
|
16
|
+
true_ans,
|
17
|
+
false_ans,
|
18
|
+
pic,
|
19
|
+
) -> None:
|
20
|
+
"""Funktion schreibt MC-Frage auf Grundlage der übergebenen strings nach Pfad f_path."""
|
21
|
+
perc = [
|
22
|
+
"100",
|
23
|
+
"50",
|
24
|
+
"33.33333",
|
25
|
+
"25",
|
26
|
+
"20",
|
27
|
+
"16.66667",
|
28
|
+
"14.28571",
|
29
|
+
"12.5",
|
30
|
+
"11.11111",
|
31
|
+
"10",
|
32
|
+
]
|
33
|
+
num_true = len(true_ans)
|
34
|
+
perc_true = perc[num_true - 1]
|
35
|
+
num_false = len(false_ans)
|
36
|
+
perc_false = "-" + perc_true
|
37
|
+
q_name = ID + "_" + name
|
38
|
+
f_path = (save_dir / q_name).with_suffix(".xml")
|
17
39
|
|
18
|
-
with open(f_path,
|
19
|
-
|
40
|
+
with open(f_path, "w", encoding="utf-8") as f:
|
41
|
+
# Text schreiben
|
20
42
|
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
21
|
-
f.write(
|
43
|
+
f.write("<quiz>\n")
|
22
44
|
f.write('<question type="multichoice">\n')
|
23
|
-
f.write(
|
24
|
-
f.write(
|
25
|
-
f.write(
|
45
|
+
f.write("<name>\n")
|
46
|
+
f.write("<text>" + q_name + "</text>\n")
|
47
|
+
f.write("</name>\n")
|
26
48
|
f.write('<questiontext format="html">\n')
|
27
49
|
if pic != 0:
|
28
|
-
f.write(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
50
|
+
f.write(
|
51
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"> <b>ID '
|
52
|
+
+ str(ID)
|
53
|
+
+ "</b> <br></p>"
|
54
|
+
'<p dir="ltr" style="text-align: left;">' + s_1 + "<br></p>"
|
55
|
+
'<p dir="ltr" style="text-align: left;">' + s_2 + "<br></p>"
|
56
|
+
'<p dir="ltr" style="text-align: left;">' + s_3 + "<br><br></p>"
|
57
|
+
'<br><img src="@@PLUGINFILE@@/'
|
58
|
+
+ str(ID)
|
59
|
+
+ '.svg" alt="Bild" width="500"><br>'
|
60
|
+
"<br></p>]]></text>\n",
|
61
|
+
)
|
34
62
|
f.write(str(pic))
|
35
63
|
else:
|
36
|
-
f.write(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
64
|
+
f.write(
|
65
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"> <b>ID '
|
66
|
+
+ q_name
|
67
|
+
+ " </b> <br></p>"
|
68
|
+
'<p dir="ltr" style="text-align: left;">' + s_1 + "<br></p>"
|
69
|
+
'<p dir="ltr" style="text-align: left;">' + s_2 + "<br></p>"
|
70
|
+
'<p dir="ltr" style="text-align: left;">' + s_3 + "<br></p>"
|
71
|
+
"<br></p>]]></text>\n",
|
72
|
+
)
|
73
|
+
|
74
|
+
f.write("</questiontext>\n")
|
43
75
|
f.write('<generalfeedback format="html">\n')
|
44
|
-
f.write(
|
45
|
-
f.write(
|
46
|
-
f.write(
|
47
|
-
f.write(
|
48
|
-
f.write(
|
49
|
-
f.write(
|
50
|
-
f.write(
|
51
|
-
f.write(
|
52
|
-
f.write(
|
53
|
-
f.write(
|
76
|
+
f.write("<text></text>\n")
|
77
|
+
f.write("</generalfeedback>\n")
|
78
|
+
f.write("<defaultgrade>" + str(float(points_avail)) + "</defaultgrade>\n")
|
79
|
+
f.write("<penalty>0.3333333</penalty>\n")
|
80
|
+
f.write("<hidden>0</hidden>\n")
|
81
|
+
f.write("<idnumber>" + ID + "</idnumber>\n")
|
82
|
+
f.write("<single>false</single>\n")
|
83
|
+
f.write("<shuffleanswers>true</shuffleanswers>\n")
|
84
|
+
f.write("<answernumbering>abc</answernumbering>\n")
|
85
|
+
f.write("<showstandardinstruction>0</showstandardinstruction>\n")
|
54
86
|
f.write('<correctfeedback format="html">\n')
|
55
|
-
f.write(
|
56
|
-
f.write(
|
87
|
+
f.write("<text>Die Frage wurde richtig beantwortet.</text>\n")
|
88
|
+
f.write("</correctfeedback>\n")
|
57
89
|
f.write('<partiallycorrectfeedback format="html">\n')
|
58
|
-
f.write(
|
59
|
-
f.write(
|
90
|
+
f.write("<text>Die Frage wurde teilweise richtig beantwortet.</text>\n")
|
91
|
+
f.write("</partiallycorrectfeedback>\n")
|
60
92
|
f.write('<incorrectfeedback format="html">\n')
|
61
|
-
f.write(
|
62
|
-
f.write(
|
63
|
-
f.write(
|
64
|
-
|
93
|
+
f.write("<text>Die Frage wurde falsch beantwortet.</text>\n")
|
94
|
+
f.write("</incorrectfeedback>\n")
|
95
|
+
f.write("<shownumcorrect/>\n")
|
96
|
+
|
65
97
|
# Alle richtigen Antworten
|
66
|
-
for i in range
|
67
|
-
|
68
|
-
|
69
|
-
f.write(
|
70
|
-
|
98
|
+
for i in range(num_true):
|
99
|
+
if ans_type == "unit":
|
100
|
+
f.write('<answer fraction="' + perc_true + '" format="html">\n')
|
101
|
+
f.write(
|
102
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">\\(\\mathrm{'
|
103
|
+
+ true_ans[i]
|
104
|
+
+ "}\\)<br></p>]]></text>\n",
|
105
|
+
)
|
71
106
|
f.write('<feedback format="html">\n')
|
72
|
-
f.write(
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
107
|
+
f.write(
|
108
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(152, 202, 62);">richtig</span><br></p>]]></text>\n',
|
109
|
+
)
|
110
|
+
f.write("</feedback>\n")
|
111
|
+
f.write("</answer>\n")
|
112
|
+
|
113
|
+
elif ans_type == "math":
|
114
|
+
f.write('<answer fraction="' + perc_true + '" format="html">\n')
|
115
|
+
f.write(
|
116
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">\\('
|
117
|
+
+ true_ans[i]
|
118
|
+
+ "\\)<br></p>]]></text>\n",
|
119
|
+
)
|
79
120
|
f.write('<feedback format="html">\n')
|
80
|
-
f.write(
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
f.write('<
|
121
|
+
f.write(
|
122
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(152, 202, 62);">richtig</span><br></p>]]></text>\n',
|
123
|
+
)
|
124
|
+
f.write("</feedback>\n")
|
125
|
+
f.write("</answer>\n")
|
126
|
+
|
127
|
+
elif ans_type == "text":
|
128
|
+
f.write('<answer fraction="' + perc_true + '" format="html">\n')
|
129
|
+
f.write(
|
130
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">'
|
131
|
+
+ true_ans[i]
|
132
|
+
+ "<br></p>]]></text>\n",
|
133
|
+
)
|
88
134
|
f.write('<feedback format="html">\n')
|
89
|
-
f.write(
|
90
|
-
|
91
|
-
|
92
|
-
|
135
|
+
f.write(
|
136
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(152, 202, 62);">richtig</span><br></p>]]></text>\n',
|
137
|
+
)
|
138
|
+
f.write("</feedback>\n")
|
139
|
+
f.write("</answer>\n")
|
140
|
+
|
93
141
|
# Alle falschen Antworten
|
94
|
-
for i in range
|
95
|
-
if ans_type==
|
96
|
-
f.write('<answer fraction="' + perc_false +'" format="html">\n')
|
97
|
-
f.write(
|
142
|
+
for i in range(num_false):
|
143
|
+
if ans_type == "unit":
|
144
|
+
f.write('<answer fraction="' + perc_false + '" format="html">\n')
|
145
|
+
f.write(
|
146
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">\\(\\mathrm{'
|
147
|
+
+ false_ans[i]
|
148
|
+
+ "}\\)<br></p>]]></text>\n",
|
149
|
+
)
|
98
150
|
f.write('<feedback format="html">\n')
|
99
|
-
f.write(
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
151
|
+
f.write(
|
152
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(239, 69, 64);">falsch</span><br></p>]]></text>\n',
|
153
|
+
)
|
154
|
+
f.write("</feedback>\n")
|
155
|
+
f.write("</answer>\n")
|
156
|
+
|
157
|
+
elif ans_type == "math":
|
158
|
+
f.write('<answer fraction="' + perc_false + '" format="html">\n')
|
159
|
+
f.write(
|
160
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">\\('
|
161
|
+
+ false_ans[i]
|
162
|
+
+ "\\)<br></p>]]></text>\n",
|
163
|
+
)
|
106
164
|
f.write('<feedback format="html">\n')
|
107
|
-
f.write(
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
165
|
+
f.write(
|
166
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(239, 69, 64);">falsch</span><br></p>]]></text>\n',
|
167
|
+
)
|
168
|
+
f.write("</feedback>\n")
|
169
|
+
f.write("</answer>\n")
|
170
|
+
|
171
|
+
elif ans_type == "text":
|
172
|
+
f.write('<answer fraction="' + perc_false + '" format="html">\n')
|
173
|
+
f.write(
|
174
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;">'
|
175
|
+
+ false_ans[i]
|
176
|
+
+ "<br></p>]]></text>\n",
|
177
|
+
)
|
114
178
|
f.write('<feedback format="html">\n')
|
115
|
-
f.write(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
179
|
+
f.write(
|
180
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(239, 69, 64);">falsch</span><br></p>]]></text>\n',
|
181
|
+
)
|
182
|
+
f.write("</feedback>\n")
|
183
|
+
f.write("</answer>\n")
|
184
|
+
|
185
|
+
f.write("</question>\n")
|
186
|
+
f.write("</quiz>\n")
|
187
|
+
|
188
|
+
|
189
|
+
def write_question_NF(
|
190
|
+
save_dir,
|
191
|
+
ID,
|
192
|
+
name,
|
193
|
+
s_1,
|
194
|
+
s_2,
|
195
|
+
s_3,
|
196
|
+
b_str,
|
197
|
+
points_avail,
|
198
|
+
result,
|
199
|
+
pic,
|
200
|
+
tol_abs,
|
201
|
+
picID=None,
|
202
|
+
) -> None:
|
203
|
+
"""Funktion schreibt NF-Frage auf Grundlage der übergebenen strings nach Pfad f_path."""
|
204
|
+
if picID is None:
|
126
205
|
picID = ID
|
127
|
-
q_name = ID +
|
128
|
-
f_path = (save_dir / q_name).with_suffix(
|
129
|
-
|
130
|
-
with open(f_path,
|
206
|
+
q_name = ID + "_" + name
|
207
|
+
f_path = (save_dir / q_name).with_suffix(".xml")
|
208
|
+
|
209
|
+
with open(f_path, "w", encoding="utf-8") as f:
|
131
210
|
# Text schreiben
|
132
211
|
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
|
133
|
-
f.write(
|
212
|
+
f.write("<quiz>\n")
|
134
213
|
f.write('<question type="numerical">\n')
|
135
|
-
f.write(
|
136
|
-
f.write(
|
137
|
-
f.write(
|
214
|
+
f.write("<name>\n")
|
215
|
+
f.write("<text>" + q_name + "</text>\n")
|
216
|
+
f.write("</name>\n")
|
138
217
|
f.write('<questiontext format="html">\n')
|
139
218
|
if pic != 0:
|
140
|
-
f.write(
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
219
|
+
f.write(
|
220
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"> <b>ID '
|
221
|
+
+ str(ID)
|
222
|
+
+ " </b> <br></p>"
|
223
|
+
'<p dir="ltr" style="text-align: left;">' + s_1 + "<br></p>"
|
224
|
+
'<p dir="ltr" style="text-align: left;">' + s_2 + "<br></p>"
|
225
|
+
'<p dir="ltr" style="text-align: left;">' + s_3 + b_str + "<br><br></p>"
|
226
|
+
'<br><img src="@@PLUGINFILE@@/'
|
227
|
+
+ str(picID)
|
228
|
+
+ '.svg" alt="Bild" width="500"><br>'
|
229
|
+
"<br></p>]]></text>\n",
|
230
|
+
)
|
146
231
|
f.write(pic)
|
147
232
|
else:
|
148
|
-
f.write(
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
233
|
+
f.write(
|
234
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"> <b>ID '
|
235
|
+
+ ID
|
236
|
+
+ " </b> <br></p>"
|
237
|
+
'<p dir="ltr" style="text-align: left;">' + s_1 + "<br></p>"
|
238
|
+
'<p dir="ltr" style="text-align: left;">' + s_2 + "<br></p>"
|
239
|
+
'<p dir="ltr" style="text-align: left;">'
|
240
|
+
+ s_3
|
241
|
+
+ b_str
|
242
|
+
+ "]]></text>\n",
|
243
|
+
)
|
244
|
+
|
245
|
+
f.write("</questiontext>\n")
|
154
246
|
f.write('<generalfeedback format="html">\n')
|
155
|
-
f.write(
|
156
|
-
f.write(
|
157
|
-
f.write(
|
158
|
-
f.write(
|
159
|
-
f.write(
|
160
|
-
f.write(
|
247
|
+
f.write("<text></text>\n")
|
248
|
+
f.write("</generalfeedback>\n")
|
249
|
+
f.write("<defaultgrade>" + str(float(points_avail)) + "</defaultgrade>\n")
|
250
|
+
f.write("<penalty>0.3333333</penalty>\n")
|
251
|
+
f.write("<hidden>0</hidden>\n")
|
252
|
+
f.write("<idnumber>" + ID + "</idnumber>\n")
|
161
253
|
f.write('<answer fraction="100" format="moodle_auto_format">\n')
|
162
|
-
f.write(
|
254
|
+
f.write("<text>" + str(result) + "</text>\n")
|
163
255
|
f.write('<feedback format="html">\n')
|
164
|
-
f.write(
|
165
|
-
|
166
|
-
|
167
|
-
f.write(
|
168
|
-
f.write(
|
169
|
-
f.write(
|
170
|
-
f.write(
|
171
|
-
f.write(
|
172
|
-
f.write(
|
173
|
-
f.write(
|
174
|
-
|
256
|
+
f.write(
|
257
|
+
'<text><![CDATA[<p dir="ltr" style="text-align: left;"><span class="" style="color: rgb(152, 202, 62);">Das Ergebnis ist im Rahmen der 1%-Toleranz korrekt.</span><br></p>]]></text>\n',
|
258
|
+
)
|
259
|
+
f.write("</feedback>\n")
|
260
|
+
f.write("<tolerance>" + str(tol_abs) + "</tolerance>\n")
|
261
|
+
f.write("</answer>\n")
|
262
|
+
f.write("<unitgradingtype>0</unitgradingtype>\n")
|
263
|
+
f.write("<unitpenalty>0.1000000</unitpenalty>\n")
|
264
|
+
f.write("<showunits>3</showunits>\n")
|
265
|
+
f.write("<unitsleft>0</unitsleft>\n")
|
266
|
+
f.write("</question>\n")
|
267
|
+
f.write("</quiz>\n")
|
@@ -1,6 +1,6 @@
|
|
1
|
-
"""This Module holds small Helperfunctions related to string manipulation"""
|
1
|
+
"""This Module holds small Helperfunctions related to string manipulation."""
|
2
2
|
|
3
|
-
import base64
|
3
|
+
import base64
|
4
4
|
from pathlib import Path
|
5
5
|
|
6
6
|
import lxml.etree as ET
|
@@ -21,7 +21,7 @@ def stringToFloat(string: str) -> float:
|
|
21
21
|
|
22
22
|
|
23
23
|
def get_bullet_string(s):
|
24
|
-
"""Formatiert die Angaben zum Statischen System hübsch"""
|
24
|
+
"""Formatiert die Angaben zum Statischen System hübsch."""
|
25
25
|
split = s.split(";")
|
26
26
|
s_spl = stripWhitespace(split)
|
27
27
|
name = []
|
@@ -35,16 +35,12 @@ def get_bullet_string(s):
|
|
35
35
|
quant.append(sc_split[3])
|
36
36
|
unit.append(sc_split[4])
|
37
37
|
bulletString = ['</p><ul dir="ltr">']
|
38
|
-
for i in range(
|
38
|
+
for i in range(len(s_spl)):
|
39
39
|
num = quant[i].split(",")
|
40
|
-
if len(num) == 2
|
41
|
-
num_s = f"{str(num[0])},\\!{str(num[1])}~"
|
42
|
-
else:
|
43
|
-
num_s = f"{str(num[0])},\\!0~"
|
40
|
+
num_s = f"{num[0]!s},\\!{num[1]!s}~" if len(num) == 2 else f"{num[0]!s},\\!0~"
|
44
41
|
bulletString.append('<li style="text-align: left;">')
|
45
42
|
bulletString.append(
|
46
|
-
f"{name[i]}: \\( {var[i]} = {
|
47
|
-
num_s} \\mathrm{{ {unit[i]} }}\\) </li>\n"
|
43
|
+
f"{name[i]}: \\( {var[i]} = {num_s} \\mathrm{{ {unit[i]} }}\\) </li>\n",
|
48
44
|
)
|
49
45
|
bulletString.append("<br></ul>")
|
50
46
|
return "\n".join(bulletString)
|
@@ -52,43 +48,40 @@ def get_bullet_string(s):
|
|
52
48
|
|
53
49
|
def getBase64Img(imgPath):
|
54
50
|
with open(imgPath, "rb") as img:
|
55
|
-
|
56
|
-
return img64
|
51
|
+
return base64.b64encode(img.read()).decode("utf-8")
|
57
52
|
|
58
53
|
|
59
|
-
def getUnitsElementAsString(unit):
|
60
|
-
|
54
|
+
def getUnitsElementAsString(unit) -> None:
|
61
55
|
def __getUnitEle__(name, multipl):
|
62
56
|
unit = ET.Element("unit")
|
63
57
|
ET.SubElement(unit, "multiplier").text = multipl
|
64
58
|
ET.SubElement(unit, "unit_name").text = name
|
65
59
|
return unit
|
66
60
|
|
67
|
-
|
61
|
+
ET.Element("units")
|
68
62
|
|
69
63
|
|
70
64
|
def printDom(xmlElement: ET.Element, file: Path | None = None) -> None:
|
71
|
-
"""Prints the document tree of ``xmlTree`` to the ``file``, if specified, else dumps to stdout"""
|
65
|
+
"""Prints the document tree of ``xmlTree`` to the ``file``, if specified, else dumps to stdout."""
|
72
66
|
documentTree = ET.ElementTree(xmlElement)
|
73
67
|
if file is not None:
|
74
68
|
if file.parent.exists():
|
75
69
|
documentTree.write(
|
76
|
-
file,
|
70
|
+
file,
|
71
|
+
xml_declaration=True,
|
72
|
+
encoding="utf-8",
|
73
|
+
pretty_print=True,
|
77
74
|
)
|
78
75
|
else:
|
79
|
-
|
80
|
-
print(f"\n{msg:=^80}")
|
81
|
-
print(ET.tostring(xmlElement, encoding="utf-8", pretty_print=True))
|
82
|
-
print(f'{" End of Element ":=^80}')
|
76
|
+
pass
|
83
77
|
|
84
78
|
|
85
79
|
def texWrapper(text: str | list[str], style: str) -> list[str]:
|
86
|
-
"""Puts the strings inside ``text`` into a LaTex environment
|
80
|
+
r"""Puts the strings inside ``text`` into a LaTex environment.
|
87
81
|
|
88
82
|
if ``style == unit``: inside ``\\mathrm{}``
|
89
83
|
if ``style == math``: inside ``\\( \\)``
|
90
84
|
"""
|
91
|
-
|
92
85
|
answers: list[str] = []
|
93
86
|
begin = ""
|
94
87
|
end = ""
|