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