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