excel2moodle 0.3.2__py3-none-any.whl → 0.3.4__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 +72 -49
- excel2moodle/__main__.py +5 -15
- excel2moodle/core/category.py +53 -62
- excel2moodle/core/dataStructure.py +87 -52
- excel2moodle/core/etHelpers.py +21 -16
- excel2moodle/core/exceptions.py +11 -4
- excel2moodle/core/globals.py +41 -28
- excel2moodle/core/numericMultiQ.py +18 -13
- excel2moodle/core/parser.py +202 -149
- excel2moodle/core/question.py +120 -51
- excel2moodle/core/questionValidator.py +48 -37
- excel2moodle/core/stringHelpers.py +41 -23
- excel2moodle/extra/__init__.py +1 -3
- excel2moodle/extra/equationVerification.py +40 -22
- excel2moodle/ui/appUi.py +113 -69
- excel2moodle/ui/dialogs.py +36 -19
- excel2moodle/ui/settings.py +48 -10
- {excel2moodle-0.3.2.dist-info → excel2moodle-0.3.4.dist-info}/METADATA +7 -6
- excel2moodle-0.3.4.dist-info/RECORD +32 -0
- {excel2moodle-0.3.2.dist-info → excel2moodle-0.3.4.dist-info}/WHEEL +1 -1
- excel2moodle-0.3.4.dist-info/entry_points.txt +2 -0
- excel2moodle-0.3.2.dist-info/RECORD +0 -31
- {excel2moodle-0.3.2.dist-info → excel2moodle-0.3.4.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.2.dist-info → excel2moodle-0.3.4.dist-info}/top_level.txt +0 -0
excel2moodle/core/parser.py
CHANGED
@@ -1,74 +1,89 @@
|
|
1
|
+
import base64 as base64
|
2
|
+
import logging as logging
|
3
|
+
import re as re
|
4
|
+
from pathlib import Path
|
1
5
|
|
2
|
-
|
3
|
-
from unicodedata import category
|
4
|
-
from asteval import Interpreter
|
5
6
|
import lxml.etree as ET
|
6
|
-
# import xml.etree.ElementTree as ET
|
7
|
-
from typing import Union
|
8
|
-
from pathlib import Path
|
9
|
-
from numpy import isin
|
10
7
|
import pandas as pd
|
11
|
-
|
12
|
-
import logging as logging
|
8
|
+
from asteval import Interpreter
|
13
9
|
|
14
|
-
from excel2moodle.core import question
|
15
|
-
from excel2moodle.core.exceptions import NanException, QNotParsedException
|
16
10
|
import excel2moodle.core.etHelpers as eth
|
17
|
-
|
18
|
-
from excel2moodle.core.globals import XMLTags, TextElements, DFIndex, questionTypes, parserSettings, feedbackStr, feedBElements
|
11
|
+
from excel2moodle import settings
|
19
12
|
from excel2moodle.core import stringHelpers
|
13
|
+
from excel2moodle.core.exceptions import NanException, QNotParsedException
|
14
|
+
from excel2moodle.core.globals import (DFIndex, TextElements, XMLTags,
|
15
|
+
feedbackStr, feedBElements,
|
16
|
+
parserSettings, questionTypes)
|
20
17
|
from excel2moodle.core.question import Picture, Question
|
21
|
-
import re as re
|
22
|
-
|
23
18
|
|
24
19
|
logger = logging.getLogger(__name__)
|
25
|
-
svgFolder = Path("../Fragensammlung/Abbildungen_SVG/")
|
26
20
|
|
27
|
-
class QuestionParser():
|
28
|
-
def __init__(self, question:Question, dataframe:pd.Series):
|
29
|
-
self.question:Question = question
|
30
|
-
self.df = dataframe
|
31
|
-
self.genFeedbacks:list[XMLTags] = []
|
32
21
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
22
|
+
class QuestionParser:
|
23
|
+
def __init__(self, question: Question, data: dict):
|
24
|
+
self.question: Question = question
|
25
|
+
self.rawInput = data
|
26
|
+
logger.debug(
|
27
|
+
f"The following Data was provided for the question {
|
28
|
+
self.question.id}:\n {self.rawInput=}"
|
29
|
+
)
|
30
|
+
self.genFeedbacks: list[XMLTags] = []
|
31
|
+
|
32
|
+
def hasPicture(self) -> bool:
|
33
|
+
"""Creates a ``Picture`` object inside ``question``,
|
34
|
+
if the question needs a pic"""
|
35
|
+
|
36
|
+
picKey = self.rawInput[DFIndex.PICTURE]
|
37
|
+
svgFolder = settings.get(
|
38
|
+
"core/pictureFolder",
|
39
|
+
default=Path("../Fragensammlung/Abbildungen_SVG").resolve(),
|
40
|
+
)
|
41
|
+
if picKey != 0 and picKey != "nan":
|
42
|
+
if not hasattr(self.question, "picture"):
|
39
43
|
self.question.picture = Picture(picKey, svgFolder, self.question)
|
40
44
|
if self.question.picture.ready:
|
41
45
|
return True
|
42
46
|
return False
|
43
47
|
|
44
|
-
def setMainText(self)->None:
|
45
|
-
paragraphs:list[ET._Element]=[TextElements.PLEFT.create()]
|
46
|
-
ET.SubElement(paragraphs[0],"b").text = f"ID {self.question.id}"
|
47
|
-
text = self.
|
48
|
+
def setMainText(self) -> None:
|
49
|
+
paragraphs: list[ET._Element] = [TextElements.PLEFT.create()]
|
50
|
+
ET.SubElement(paragraphs[0], "b").text = f"ID {self.question.id}"
|
51
|
+
text = self.rawInput[DFIndex.TEXT]
|
48
52
|
pcount = 0
|
49
53
|
for t in text:
|
50
54
|
if not pd.isna(t):
|
51
|
-
pcount +=1
|
55
|
+
pcount += 1
|
52
56
|
paragraphs.append(TextElements.PLEFT.create())
|
53
57
|
paragraphs[-1].text = t
|
54
|
-
self.question.
|
55
|
-
logger.debug(
|
58
|
+
self.question.qtextParagraphs = paragraphs
|
59
|
+
logger.debug(
|
60
|
+
f"Created main Text {
|
61
|
+
self.question.id} with:{pcount} paragraphs"
|
62
|
+
)
|
56
63
|
return None
|
57
|
-
|
58
|
-
def setBPoints(self)->None:
|
64
|
+
|
65
|
+
def setBPoints(self) -> None:
|
59
66
|
"""If there bulletPoints are set in the Spreadsheet it creates an unordered List-Element in ``Question.bulletList``"""
|
60
|
-
if DFIndex.BPOINTS in self.
|
61
|
-
bps = self.
|
67
|
+
if DFIndex.BPOINTS in self.rawInput:
|
68
|
+
bps: str = self.rawInput[DFIndex.BPOINTS]
|
62
69
|
try:
|
63
70
|
bulletList = self.formatBulletList(bps)
|
64
71
|
except IndexError as e:
|
65
|
-
raise QNotParsedException(
|
66
|
-
|
67
|
-
|
72
|
+
raise QNotParsedException(
|
73
|
+
f"konnt Bullet Liste {self.question.id} nicht generieren",
|
74
|
+
self.question.id,
|
75
|
+
exc_info=e,
|
76
|
+
)
|
77
|
+
logger.debug(
|
78
|
+
f"Generated BPoint List: \n {
|
79
|
+
ET.tostring(bulletList, encoding='unicode')}"
|
80
|
+
)
|
81
|
+
self.question.bulletList = bulletList
|
68
82
|
return None
|
69
83
|
|
70
|
-
def formatBulletList(self,bps:str)->ET.Element:
|
71
|
-
|
84
|
+
def formatBulletList(self, bps: str) -> ET.Element:
|
85
|
+
logger.debug("Formatting the bulletpoint list")
|
86
|
+
li: list[str] = stringHelpers.stripWhitespace(bps.split(";"))
|
72
87
|
name = []
|
73
88
|
var = []
|
74
89
|
quant = []
|
@@ -81,54 +96,66 @@ class QuestionParser():
|
|
81
96
|
quant.append(sc_split[3])
|
82
97
|
unit.append(sc_split[4])
|
83
98
|
for i in range(0, len(name)):
|
84
|
-
|
85
|
-
|
86
|
-
num_s =
|
87
|
-
else:
|
99
|
+
if re.fullmatch(r"{\w+}", quant[i]):
|
100
|
+
logger.debug(f"Got an variable bulletItem")
|
101
|
+
num_s = quant[i]
|
102
|
+
else:
|
103
|
+
logger.debug(f"Got a normal bulletItem")
|
104
|
+
num = quant[i].split(",")
|
105
|
+
if len(num) == 2:
|
106
|
+
num_s = f"{str(num[0])},\\!{str(num[1])}~"
|
107
|
+
else:
|
108
|
+
num_s = f"{str(num[0])},\\!0~"
|
88
109
|
bullet = TextElements.LISTITEM.create()
|
89
|
-
bullet.text=
|
110
|
+
bullet.text = f"{name[i]}: \\( {var[i]} = {
|
111
|
+
num_s} \\mathrm{{ {unit[i]} }}\\)\n"
|
90
112
|
unorderedList.append(bullet)
|
91
113
|
return unorderedList
|
92
114
|
|
93
|
-
def
|
94
|
-
|
115
|
+
def appendToTmpEle(
|
116
|
+
self, eleName: str, text: str | DFIndex, txtEle=False, **attribs
|
117
|
+
):
|
118
|
+
"""Appends the text to the temporary Element"""
|
119
|
+
t = self.rawInput[text] if isinstance(text, DFIndex) else text
|
95
120
|
if txtEle is False:
|
96
121
|
self.tmpEle.append(eth.getElement(eleName, t, **attribs))
|
97
122
|
elif txtEle is True:
|
98
123
|
self.tmpEle.append(eth.getTextElement(eleName, t, **attribs))
|
99
124
|
|
100
|
-
def appendFromSettings(self, key="standards")->None:
|
125
|
+
def appendFromSettings(self, key="standards") -> None:
|
101
126
|
"""Appends 1 to 1 mapped Elements defined in the parserSettings to the element"""
|
102
127
|
parser = ["Parser"]
|
103
128
|
if isinstance(self, MCQuestionParser):
|
104
|
-
|
129
|
+
parser.append("MCParser")
|
105
130
|
elif isinstance(self, NFQuestionParser):
|
106
131
|
parser.append("NFParser")
|
107
132
|
for p in parser:
|
108
133
|
try:
|
109
134
|
for k, v in parserSettings[p][key].items():
|
110
|
-
self.
|
135
|
+
self.appendToTmpEle(k, text=v)
|
111
136
|
except KeyError as e:
|
112
|
-
msg = f"Invalider Input aus den Einstellungen Parser: {
|
137
|
+
msg = f"Invalider Input aus den Einstellungen Parser: {
|
138
|
+
type(p) =}"
|
113
139
|
logger.error(msg, exc_info=e)
|
114
140
|
raise QNotParsedException(msg, self.question.id, exc_info=e)
|
115
141
|
return None
|
116
142
|
|
117
|
-
def parse(self, xmlTree: ET._Element|None=None)->None:
|
143
|
+
def parse(self, xmlTree: ET._Element | None = None) -> None:
|
118
144
|
"""Parses the Question
|
119
|
-
|
145
|
+
|
120
146
|
Generates an new Question Element stored as ``self.tmpEle:ET.Element``
|
121
147
|
if no Exceptions are raised, ``self.tmpEle`` is passed to ``self.question.element``
|
122
148
|
"""
|
123
|
-
|
149
|
+
logger.info(f"Starting to parse {self.question.id}")
|
150
|
+
self.tmpEle = ET.Element(XMLTags.QUESTION, type=self.question.moodleType)
|
124
151
|
# self.tmpEle.set(XMLTags.TYPE, self.question.moodleType)
|
125
|
-
self.
|
126
|
-
self.
|
127
|
-
if self.hasPicture()
|
152
|
+
self.appendToTmpEle(XMLTags.NAME, text=DFIndex.NAME, txtEle=True)
|
153
|
+
self.appendToTmpEle(XMLTags.ID, text=self.question.id)
|
154
|
+
if self.hasPicture():
|
128
155
|
self.tmpEle.append(self.question.picture.element)
|
129
|
-
self.tmpEle.append(ET.Element(XMLTags.QTEXT, format
|
130
|
-
self.
|
131
|
-
self.
|
156
|
+
self.tmpEle.append(ET.Element(XMLTags.QTEXT, format="html"))
|
157
|
+
self.appendToTmpEle(XMLTags.POINTS, text=str(self.question.points))
|
158
|
+
self.appendToTmpEle(XMLTags.PENALTY, text="0.3333")
|
132
159
|
self.appendFromSettings()
|
133
160
|
for feedb in self.genFeedbacks:
|
134
161
|
self.tmpEle.append(eth.getFeedBEle(feedb))
|
@@ -144,10 +171,15 @@ class QuestionParser():
|
|
144
171
|
self.question.element = self.tmpEle
|
145
172
|
return None
|
146
173
|
|
147
|
-
def getFeedBEle(
|
174
|
+
def getFeedBEle(
|
175
|
+
self,
|
176
|
+
feedback: XMLTags,
|
177
|
+
text: str | None = None,
|
178
|
+
style: TextElements | None = None,
|
179
|
+
) -> ET.Element:
|
148
180
|
if style is None:
|
149
181
|
span = feedBElements[feedback]
|
150
|
-
else:
|
182
|
+
else:
|
151
183
|
span = style.create()
|
152
184
|
if text is None:
|
153
185
|
text = feedbackStr[feedback]
|
@@ -157,66 +189,86 @@ class QuestionParser():
|
|
157
189
|
par.append(span)
|
158
190
|
ele.append(eth.getCdatTxtElement(par))
|
159
191
|
return ele
|
160
|
-
|
161
|
-
def setAnswers(self)->list[ET.Element]|None:
|
192
|
+
|
193
|
+
def setAnswers(self) -> list[ET.Element] | None:
|
162
194
|
"""Needs to be implemented in the type-specific subclasses"""
|
163
195
|
return None
|
164
196
|
|
165
197
|
@staticmethod
|
166
|
-
def getNumericAnsElement(
|
167
|
-
|
168
|
-
|
169
|
-
|
198
|
+
def getNumericAnsElement(
|
199
|
+
result: int | float,
|
200
|
+
tolerance: int = 0,
|
201
|
+
fraction: int | float = 100,
|
202
|
+
format: str = "moodle_auto_format",
|
203
|
+
) -> ET.Element:
|
170
204
|
"""Returns an ``<answer/>`` Element specific for the numerical Question
|
171
205
|
The element contains those childs:
|
172
206
|
``<text/>`` which holds the value of the answer
|
173
|
-
``<tolerace/>`` with the *relative* tolerance for the result
|
207
|
+
``<tolerace/>`` with the *relative* tolerance for the result in percent
|
174
208
|
``<feedback/>`` with general feedback for a true answer
|
175
209
|
"""
|
176
210
|
|
177
|
-
ansEle:ET.Element = eth.getTextElement(
|
178
|
-
|
179
|
-
|
180
|
-
ansEle.append(
|
211
|
+
ansEle: ET.Element = eth.getTextElement(
|
212
|
+
XMLTags.ANSWER, text=str(result), fraction=str(fraction), format=format
|
213
|
+
)
|
214
|
+
ansEle.append(
|
215
|
+
eth.getFeedBEle(
|
216
|
+
XMLTags.ANSFEEDBACK,
|
217
|
+
feedbackStr["right1Percent"],
|
218
|
+
TextElements.SPANGREEN,
|
219
|
+
)
|
220
|
+
)
|
221
|
+
if tolerance == 0:
|
222
|
+
try:
|
223
|
+
tolerance = int(settings.value(
|
224
|
+
"parser/nf/tolerance"))
|
225
|
+
except ValueError as e:
|
226
|
+
logger.error(
|
227
|
+
f"The tolerance Setting is invalid {e} \n using 1% tolerance",
|
228
|
+
exc_info=e)
|
229
|
+
tolerance = 1
|
230
|
+
logger.debug(f"using tolerance of {tolerance} %")
|
231
|
+
tol = abs(round(result * tolerance, 3))
|
232
|
+
ansEle.append(eth.getElement(XMLTags.TOLERANCE, text=str(tol)))
|
181
233
|
return ansEle
|
182
234
|
|
235
|
+
|
183
236
|
class NFQuestionParser(QuestionParser):
|
184
|
-
def __init__(self, *args)->None:
|
237
|
+
def __init__(self, *args) -> None:
|
185
238
|
super().__init__(*args)
|
186
|
-
self.genFeedbacks=[XMLTags.GENFEEDB]
|
239
|
+
self.genFeedbacks = [XMLTags.GENFEEDB]
|
187
240
|
|
188
|
-
def setAnswers(self)->list[ET.Element]:
|
189
|
-
result = self.
|
190
|
-
ansEle:list[ET.Element]=[]
|
191
|
-
ansEle.append(self.getNumericAnsElement(
|
241
|
+
def setAnswers(self) -> list[ET.Element]:
|
242
|
+
result = self.rawInput[DFIndex.RESULT]
|
243
|
+
ansEle: list[ET.Element] = []
|
244
|
+
ansEle.append(self.getNumericAnsElement(result=result))
|
192
245
|
return ansEle
|
193
246
|
|
247
|
+
|
194
248
|
class NFMQuestionParser(QuestionParser):
|
195
|
-
def __init__(self,
|
196
|
-
super().__init__(
|
197
|
-
self.genFeedbacks=[XMLTags.GENFEEDB]
|
249
|
+
def __init__(self, *args):
|
250
|
+
super().__init__(*args)
|
251
|
+
self.genFeedbacks = [XMLTags.GENFEEDB]
|
198
252
|
self.astEval = Interpreter()
|
199
253
|
|
200
|
-
def setAnswers(self)->None:
|
201
|
-
equation = self.
|
202
|
-
bps = self.
|
203
|
-
ansElementsList:list[ET.Element]=[]
|
204
|
-
varNames:list[str]= self.
|
205
|
-
|
206
|
-
bulletPoints:list[ET.Element] = []
|
254
|
+
def setAnswers(self) -> None:
|
255
|
+
equation = self.rawInput[DFIndex.RESULT]
|
256
|
+
bps = str(self.rawInput[DFIndex.BPOINTS])
|
257
|
+
ansElementsList: list[ET.Element] = []
|
258
|
+
varNames: list[str] = self._getVarsList(bps)
|
259
|
+
self.question.variables, number = self._getVariablesDict(varNames)
|
207
260
|
for n in range(number):
|
208
|
-
self._setupAstIntprt(
|
261
|
+
self._setupAstIntprt(self.question.variables, n)
|
209
262
|
result = self.astEval(equation)
|
210
263
|
if isinstance(result, float):
|
211
|
-
ansElementsList.append(
|
212
|
-
|
213
|
-
|
264
|
+
ansElementsList.append(
|
265
|
+
self.getNumericAnsElement(result=round(result, 3))
|
266
|
+
)
|
214
267
|
self.question.answerVariants = ansElementsList
|
215
|
-
self.
|
216
|
-
self.setVariants(len(bulletPoints))
|
268
|
+
self.setVariants(len(ansElementsList))
|
217
269
|
return None
|
218
270
|
|
219
|
-
def setVariants(self, number:int):
|
271
|
+
def setVariants(self, number: int):
|
220
272
|
self.question.variants = number
|
221
273
|
mvar = self.question.category.maxVariants
|
222
274
|
if mvar is None:
|
@@ -224,48 +276,34 @@ class NFMQuestionParser(QuestionParser):
|
|
224
276
|
else:
|
225
277
|
self.question.category.maxVariants = number if number <= mvar else mvar
|
226
278
|
|
227
|
-
|
228
|
-
|
229
|
-
@staticmethod
|
230
|
-
def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int)-> str:
|
231
|
-
"""
|
232
|
-
Für jeden Eintrag im varDict, wird im bulletPoints String der Substring "{key}" durch value[index] ersetzt
|
233
|
-
"""
|
234
|
-
for k, v in varDict.items():
|
235
|
-
s = r"{" + str(k) + r"}"
|
236
|
-
matcher = re.compile(s)
|
237
|
-
bulletPoints = matcher.sub(str(v[index]), bulletPoints)
|
238
|
-
return bulletPoints
|
239
|
-
|
240
|
-
def _setupAstIntprt(self, var:dict[str, list[str]], index:int)->None:
|
279
|
+
def _setupAstIntprt(self, var: dict[str, list[float | int]], index: int) -> None:
|
241
280
|
"""Ubergibt die Parameter mit entsprechenden Variablen-Namen an den asteval-Interpreter.
|
242
281
|
|
243
282
|
Dann kann dieser die equation lesen.
|
244
283
|
"""
|
245
|
-
for
|
246
|
-
|
247
|
-
value = comma.sub(".",v[index])
|
248
|
-
self.astEval.symtable[k] = float(value)
|
284
|
+
for name, value in var.items():
|
285
|
+
self.astEval.symtable[name] = value[index]
|
249
286
|
return None
|
250
287
|
|
251
|
-
def
|
288
|
+
def _getVariablesDict(self, keyList: list) -> tuple[dict[str, list[float]], int]:
|
252
289
|
"""Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]"""
|
253
|
-
dic:dict = {}
|
254
|
-
num:int = 0
|
290
|
+
dic: dict = {}
|
291
|
+
num: int = 0
|
255
292
|
for k in keyList:
|
256
|
-
val = self.
|
257
|
-
if isinstance(val, str)
|
258
|
-
li = val.split(";")
|
293
|
+
val = self.rawInput[k]
|
294
|
+
if isinstance(val, str):
|
295
|
+
li = stringHelpers.stripWhitespace(val.split(";"))
|
259
296
|
num = len(li)
|
260
|
-
|
261
|
-
|
297
|
+
vars: list[float] = [float(i.replace(",", ".")) for i in li]
|
298
|
+
dic[str(k)] = vars
|
299
|
+
else:
|
262
300
|
dic[str(k)] = [str(val)]
|
263
301
|
num = 1
|
264
302
|
print(f"Folgende Variablen wurden gefunden:\n{dic}\n")
|
265
303
|
return dic, num
|
266
304
|
|
267
305
|
@staticmethod
|
268
|
-
def
|
306
|
+
def _getVarsList(bps: str | list[str]) -> list:
|
269
307
|
"""
|
270
308
|
Durchsucht den bulletPoints String nach den Variablen, die als "{var}" gekennzeichnet sind
|
271
309
|
"""
|
@@ -275,49 +313,64 @@ class NFMQuestionParser(QuestionParser):
|
|
275
313
|
vars.extend(re.findall(r"\{\w\}", str(bps)))
|
276
314
|
else:
|
277
315
|
vars = re.findall(r"\{\w\}", str(bps))
|
278
|
-
variablen=[]
|
316
|
+
variablen = []
|
279
317
|
for v in vars:
|
280
318
|
variablen.append(v.strip("{}"))
|
281
319
|
return variablen
|
282
320
|
|
321
|
+
|
283
322
|
class MCQuestionParser(QuestionParser):
|
284
|
-
def __init__(self, *args)->None:
|
323
|
+
def __init__(self, *args) -> None:
|
285
324
|
super().__init__(*args)
|
286
|
-
self.genFeedbacks=[
|
325
|
+
self.genFeedbacks = [
|
287
326
|
XMLTags.CORFEEDB,
|
288
327
|
XMLTags.PCORFEEDB,
|
289
328
|
XMLTags.INCORFEEDB,
|
290
|
-
|
329
|
+
]
|
291
330
|
|
292
|
-
def getAnsElementsList(
|
331
|
+
def getAnsElementsList(
|
332
|
+
self, answerList: list, fraction: float = 50, format="html"
|
333
|
+
) -> list[ET.Element]:
|
293
334
|
elementList: list[ET.Element] = []
|
294
335
|
for ans in answerList:
|
295
336
|
p = TextElements.PLEFT.create()
|
296
337
|
p.text = str(ans)
|
297
338
|
text = eth.getCdatTxtElement(p)
|
298
|
-
elementList.append(
|
339
|
+
elementList.append(
|
340
|
+
ET.Element(XMLTags.ANSWER, fraction=str(fraction), format=format)
|
341
|
+
)
|
299
342
|
elementList[-1].append(text)
|
300
343
|
if fraction < 0:
|
301
|
-
elementList[-1].append(
|
302
|
-
|
303
|
-
|
344
|
+
elementList[-1].append(
|
345
|
+
eth.getFeedBEle(
|
346
|
+
XMLTags.ANSFEEDBACK,
|
347
|
+
text=feedbackStr["wrong"],
|
348
|
+
style=TextElements.SPANRED,
|
349
|
+
)
|
350
|
+
)
|
304
351
|
elif fraction > 0:
|
305
|
-
elementList[-1].append(
|
306
|
-
|
307
|
-
|
352
|
+
elementList[-1].append(
|
353
|
+
eth.getFeedBEle(
|
354
|
+
XMLTags.ANSFEEDBACK,
|
355
|
+
text=feedbackStr["right"],
|
356
|
+
style=TextElements.SPANGREEN,
|
357
|
+
)
|
358
|
+
)
|
308
359
|
return elementList
|
309
360
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
true = stringHelpers.stripWhitespace(self.df.get(DFIndex.TRUE).split(';'))
|
361
|
+
def setAnswers(self) -> list[ET.Element]:
|
362
|
+
ansStyle = self.rawInput[DFIndex.ANSTYPE]
|
363
|
+
true = stringHelpers.stripWhitespace(self.rawInput[DFIndex.TRUE].split(";"))
|
314
364
|
trueAnsList = stringHelpers.texWrapper(true, style=ansStyle)
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
365
|
+
logger.debug(f"got the following true answers \n {trueAnsList=}")
|
366
|
+
false = stringHelpers.stripWhitespace(self.rawInput[DFIndex.FALSE].split(";"))
|
367
|
+
falseAnsList = stringHelpers.texWrapper(false, style=ansStyle)
|
368
|
+
logger.debug(f"got the following false answers \n {falseAnsList=}")
|
369
|
+
truefrac = 1 / len(trueAnsList) * 100
|
370
|
+
falsefrac = 1 / len(trueAnsList) * (-100)
|
371
|
+
self.tmpEle.find(XMLTags.PENALTY).text = str(round(truefrac / 100, 4))
|
320
372
|
ansList = self.getAnsElementsList(trueAnsList, fraction=round(truefrac, 4))
|
321
|
-
ansList.extend(
|
373
|
+
ansList.extend(
|
374
|
+
self.getAnsElementsList(falseAnsList, fraction=round(falsefrac, 4))
|
375
|
+
)
|
322
376
|
return ansList
|
323
|
-
|