excel2moodle 0.3.1__py3-none-any.whl → 0.3.3__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 CHANGED
@@ -17,12 +17,27 @@ Functionality
17
17
  * Parse Numeric Questions, each into one XML file
18
18
  * create single XML File from a selection of questions
19
19
  """
20
+ from importlib import metadata
20
21
  from importlib.metadata import version
21
22
  try:
22
23
  __version__ = version("excel2moodle")
23
24
  except Exception:
24
25
  __version__ = "unknown"
25
26
 
27
+
28
+ if not __package__ == None:
29
+ meta = metadata.metadata(__package__)
30
+ e2mMetadata:dict = {
31
+ "version": __version__,
32
+ "name": meta['name'],
33
+ "description": meta['summary'],
34
+ "author": meta['author'],
35
+ "license": meta['license-expression'],
36
+ "documentation": "https://jbosse3.gitlab.io/excel2moodle",
37
+ "homepage": meta['project-url'].split()[1],
38
+ "issues": "https://gitlab.com/jbosse3/excel2moodle/issues",
39
+ }
40
+
26
41
  # from excel2moodle.core import klausurGenerator
27
42
  # from excel2moodle.core import numericMultiQ
28
43
  # from excel2moodle.core import questionWriter
@@ -36,7 +51,6 @@ from excel2moodle.ui import settings
36
51
  from PySide6.QtCore import QObject, Signal
37
52
  import logging as logging
38
53
  from logging import config as logConfig
39
- from pathlib import Path
40
54
 
41
55
  loggerConfig = {
42
56
  'version': 1,
@@ -108,6 +122,6 @@ logging.config.dictConfig(config=loggerConfig)
108
122
  qSignalLogger = LogHandler()
109
123
  logger.addHandler(qSignalLogger)
110
124
 
111
- p = Path(__file__).parent
112
- dirProjectRoot = p.parent.resolve()
113
- dirDocumentation = (dirProjectRoot / "docs/_build/html")
125
+
126
+ for k,v in e2mMetadata.items():
127
+ print(f"{k}: \t {v}\n")
excel2moodle/__main__.py CHANGED
@@ -1,21 +1,11 @@
1
1
  """Main Function to make the Package executable"""
2
2
 
3
- from pathlib import Path
4
-
5
3
  from PySide6 import QtWidgets, sys
6
- import xml.etree.ElementTree as xmlET
7
4
 
8
5
  from excel2moodle.core import dataStructure
9
6
  from excel2moodle.ui import appUi as ui
10
7
  from excel2moodle.ui.settings import Settings
11
8
 
12
- import logging
13
-
14
- katOutPath = None
15
- excelFile = None
16
-
17
- logger = logging.getLogger(__name__)
18
-
19
9
 
20
10
  def main()->None:
21
11
  app = QtWidgets.QApplication(sys.argv)
@@ -39,48 +39,19 @@ class Category():
39
39
  return self.NAME == other.NAME
40
40
  return False
41
41
 
42
- def _getQuestions(self)->None:
43
- self.questions:dict[int,Question] = {}
44
- validator = Validator(self)
45
- for q in self.dataframe.columns:
46
- logger.debug(f"Starting to check Validity of {q}")
47
- qdat = self.dataframe[q]
48
- if isinstance(qdat, pd.Series):
49
- validator.setup(qdat, q)
50
- check = False
51
- try:
52
- check = validator.validate()
53
- except InvalidFieldException as e:
54
- logger.error(f"Frage {self.id}{q:02d} ist invalid.", exc_info=e)
55
- if check:
56
- self.questions[q]=validator.question
57
- try:
58
- self.parseQ(self.questions[q])
59
- except QNotParsedException as e:
60
- logger.error(f"Frage {self.questions[q].id} konnte nicht erstellt werden", exc_info=e)
61
- return None
62
-
63
- def parsAll(self, tree: ET.Element = None)->None:
64
- if tree is None:
65
- tree = ET.Element("quiz")
66
- tree.append(self.getCategoryHeader())
67
- for q in self.questions.values():
68
- self.parseQ(q, tree)
69
-
70
- def parseQ(self, q:Question, xmlTree:ET._Element|None=None)->bool:
42
+ def parseQ(self, q:Question, questionData:dict|None=None, xmlTree:ET._Element|None=None)->bool:
71
43
  if q.element is not None:
72
44
  logger.info(f"Question {q.id} is already parsed")
73
45
  return True
74
46
  else:
75
- series = self.dataframe[q.number]
76
47
  if q.qtype == "NF":
77
- parser = NFQuestionParser( q, series)
48
+ parser = NFQuestionParser( q, questionData)
78
49
  logger.debug(f"setup a new NF parser ")
79
50
  elif q.qtype == "MC":
80
- parser = MCQuestionParser( q, series)
51
+ parser = MCQuestionParser( q, questionData)
81
52
  logger.debug(f"setup a new MC parser ")
82
53
  elif q.qtype == "NFM":
83
- parser = NFMQuestionParser( q, series)
54
+ parser = NFMQuestionParser( q, questionData)
84
55
  logger.debug(f"setup a new NFM parser ")
85
56
  else:
86
57
  logger.error(f"ERROR, couldn't setup Parser")
@@ -46,6 +46,7 @@ class QuestionDB():
46
46
  self.spreadSheetPath = sheet
47
47
  self.svgFolder = (self.spreadSheetPath.parent / 'Abbildungen_SVG')
48
48
  self.retrieveCategoriesData()
49
+ self.parseAll()
49
50
 
50
51
  def retrieveCategoriesData(self)->None:
51
52
  """Scans through the sheet with the metadata for all the question categories
@@ -62,6 +63,7 @@ class QuestionDB():
62
63
  index_col=0)
63
64
  logger.info("Sucessfully read categoriesMetaData")
64
65
  print(self.categoriesMetaData)
66
+ self.categories = {}
65
67
  for sh in excelFile.sheet_names:
66
68
  if sh.startswith("KAT"):
67
69
  n = int(sh[4:])
@@ -97,7 +99,7 @@ class QuestionDB():
97
99
  if check:
98
100
  c.questions[q]=validator.question
99
101
  try:
100
- c.parseQ(c.questions[q])
102
+ c.parseQ(c.questions[q], validator.qdata)
101
103
  except QNotParsedException as e:
102
104
  logger.error(f"Frage {c.questions[q].id} konnte nicht erstellt werden", exc_info=e)
103
105
 
@@ -119,6 +121,7 @@ class QuestionDB():
119
121
  def appendQElements(self,cat:Category, qList:list[Question], tree:ET.Element, includeHeader:bool=True)->None:
120
122
  if includeHeader:
121
123
  tree.append( cat.getCategoryHeader())
124
+ logger.debug(f"Appended a new category item {cat=}")
122
125
  sameVariant = False
123
126
  variant = 1
124
127
  for q in qList:
@@ -132,7 +135,8 @@ class QuestionDB():
132
135
  logger.debug(f"Die Fragen-Variante {variant} wurde gewählt")
133
136
  q.assemble(variant)
134
137
  else: print("skipping this question")
135
- q.assemble
138
+ else:
139
+ q.assemble()
136
140
  tree.append(q.element)
137
141
  else: logger.warning(f"Frage {q} wurde nicht erstellt")
138
142
  return None
@@ -1,12 +1,6 @@
1
-
2
-
3
- from unicodedata import category
4
1
  from asteval import Interpreter
5
2
  import lxml.etree as ET
6
- # import xml.etree.ElementTree as ET
7
- from typing import Union
8
3
  from pathlib import Path
9
- from numpy import isin
10
4
  import pandas as pd
11
5
  import base64 as base64
12
6
  import logging as logging
@@ -20,21 +14,27 @@ from excel2moodle.core import stringHelpers
20
14
  from excel2moodle.core.question import Picture, Question
21
15
  import re as re
22
16
 
17
+ from excel2moodle import settings
18
+
23
19
 
24
20
  logger = logging.getLogger(__name__)
25
- svgFolder = Path("../Fragensammlung/Abbildungen_SVG/")
21
+ f = Path("../Fragensammlung/Abbildungen_SVG/").resolve()
22
+ settings.set("core/pictureFolder", f)
23
+ svgFolder = settings.get("core/pictureFolder", default=f)
24
+ print(svgFolder)
26
25
 
27
26
  class QuestionParser():
28
- def __init__(self, question:Question, dataframe:pd.Series):
27
+ def __init__(self, question:Question, data:dict):
29
28
  self.question:Question = question
30
- self.df = dataframe
29
+ self.rawInput = data
30
+ logger.debug(f"The following Data was provided for the question {self.question.id}:\n {self.rawInput =}")
31
31
  self.genFeedbacks:list[XMLTags] = []
32
32
 
33
33
  def hasPicture(self)->bool:
34
34
  """Creates a ``Picture`` object inside ``question``, if the question needs a pic"""
35
35
 
36
- picKey = self.df.get(DFIndex.PICTURE)
37
- if picKey != 0 and not pd.isna(picKey):
36
+ picKey = self.rawInput[DFIndex.PICTURE]
37
+ if picKey != 0 and picKey != 'nan':
38
38
  if not hasattr(self.question, 'picture'):
39
39
  self.question.picture = Picture(picKey, svgFolder, self.question)
40
40
  if self.question.picture.ready:
@@ -44,31 +44,32 @@ class QuestionParser():
44
44
  def setMainText(self)->None:
45
45
  paragraphs:list[ET._Element]=[TextElements.PLEFT.create()]
46
46
  ET.SubElement(paragraphs[0],"b").text = f"ID {self.question.id}"
47
- text = self.df.get(DFIndex.TEXT)
47
+ text = self.rawInput[DFIndex.TEXT]
48
48
  pcount = 0
49
49
  for t in text:
50
50
  if not pd.isna(t):
51
51
  pcount +=1
52
52
  paragraphs.append(TextElements.PLEFT.create())
53
53
  paragraphs[-1].text = t
54
- self.question.qtextElements = paragraphs
54
+ self.question.qtextParagraphs = paragraphs
55
55
  logger.debug(f"Created main Text {self.question.id} with:{pcount} paragraphs")
56
56
  return None
57
-
57
+
58
58
  def setBPoints(self)->None:
59
59
  """If there bulletPoints are set in the Spreadsheet it creates an unordered List-Element in ``Question.bulletList``"""
60
- if DFIndex.BPOINTS in self.df.index:
61
- bps = self.df.get(DFIndex.BPOINTS)
60
+ if DFIndex.BPOINTS in self.rawInput:
61
+ bps:str = self.rawInput[DFIndex.BPOINTS]
62
62
  try:
63
63
  bulletList = self.formatBulletList(bps)
64
64
  except IndexError as e:
65
65
  raise QNotParsedException(f"konnt Bullet Liste {self.question.id} nicht generieren", self.question.id, exc_info=e)
66
- self.question.bulletList.append(bulletList)
67
- logger.debug(f"appendet Bullet List {bulletList = }")
66
+ logger.debug(f"Generated BPoint List: \n {ET.tostring(bulletList, encoding='unicode')}")
67
+ self.question.bulletList = bulletList
68
68
  return None
69
69
 
70
70
  def formatBulletList(self,bps:str)->ET.Element:
71
- li:list[str] =stringHelpers.stripWhitespace( bps.split(';'))
71
+ logger.debug(f"Formatting the bulletpoint list")
72
+ li:list[str] = stringHelpers.stripWhitespace( bps.split(';'))
72
73
  name = []
73
74
  var = []
74
75
  quant = []
@@ -81,17 +82,23 @@ class QuestionParser():
81
82
  quant.append(sc_split[3])
82
83
  unit.append(sc_split[4])
83
84
  for i in range(0, len(name)):
84
- num = quant[i].split(',')
85
- if len(num)==2:
86
- num_s = f"{str(num[0])},\\!{str(num[1])}~"
87
- else: num_s = f"{str(num[0])},\\!0~"
85
+ if re.fullmatch(r"{\w+}", quant[i]):
86
+ logger.debug(f"Got an variable bulletItem")
87
+ num_s = quant[i]
88
+ 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~"
88
94
  bullet = TextElements.LISTITEM.create()
89
95
  bullet.text=(f"{ name[i] }: \\( {var[i]} = {num_s} \\mathrm{{ {unit[i]} }}\\)\n")
90
96
  unorderedList.append(bullet)
91
97
  return unorderedList
92
98
 
93
- def appendToQuestion(self, eleName: str, text:str|DFIndex, txtEle=False, **attribs ):
94
- t = (self.df.get(text) if isinstance(text, DFIndex) else text)
99
+ def appendToTmpEle(self, eleName: str, text:str|DFIndex, txtEle=False, **attribs ):
100
+ """Appends the text to the temporary Element"""
101
+ t = self.rawInput[text] if isinstance(text, DFIndex) else text
95
102
  if txtEle is False:
96
103
  self.tmpEle.append(eth.getElement(eleName, t, **attribs))
97
104
  elif txtEle is True:
@@ -107,7 +114,7 @@ class QuestionParser():
107
114
  for p in parser:
108
115
  try:
109
116
  for k, v in parserSettings[p][key].items():
110
- self.appendToQuestion(k, text=v)
117
+ self.appendToTmpEle(k, text=v)
111
118
  except KeyError as e:
112
119
  msg = f"Invalider Input aus den Einstellungen Parser: {type(p) = }"
113
120
  logger.error(msg, exc_info=e)
@@ -120,15 +127,16 @@ class QuestionParser():
120
127
  Generates an new Question Element stored as ``self.tmpEle:ET.Element``
121
128
  if no Exceptions are raised, ``self.tmpEle`` is passed to ``self.question.element``
122
129
  """
130
+ logger.info(f"Starting to parse {self.question.id}")
123
131
  self.tmpEle = ET.Element(XMLTags.QUESTION, type = self.question.moodleType)
124
132
  # self.tmpEle.set(XMLTags.TYPE, self.question.moodleType)
125
- self.appendToQuestion(XMLTags.NAME, text=DFIndex.NAME, txtEle=True)
126
- self.appendToQuestion(XMLTags.ID, text=self.question.id)
133
+ self.appendToTmpEle(XMLTags.NAME, text=DFIndex.NAME, txtEle=True)
134
+ self.appendToTmpEle(XMLTags.ID, text=self.question.id)
127
135
  if self.hasPicture() :
128
136
  self.tmpEle.append(self.question.picture.element)
129
137
  self.tmpEle.append(ET.Element(XMLTags.QTEXT, format = "html"))
130
- self.appendToQuestion(XMLTags.POINTS, text=str(self.question.points))
131
- self.appendToQuestion(XMLTags.PENALTY, text="0.3333")
138
+ self.appendToTmpEle(XMLTags.POINTS, text=str(self.question.points))
139
+ self.appendToTmpEle(XMLTags.PENALTY, text="0.3333")
132
140
  self.appendFromSettings()
133
141
  for feedb in self.genFeedbacks:
134
142
  self.tmpEle.append(eth.getFeedBEle(feedb))
@@ -186,34 +194,30 @@ class NFQuestionParser(QuestionParser):
186
194
  self.genFeedbacks=[XMLTags.GENFEEDB]
187
195
 
188
196
  def setAnswers(self)->list[ET.Element]:
189
- result = self.df.get(DFIndex.RESULT)
197
+ result = self.rawInput[DFIndex.RESULT]
190
198
  ansEle:list[ET.Element]=[]
191
199
  ansEle.append(self.getNumericAnsElement( result = result ))
192
200
  return ansEle
193
201
 
194
202
  class NFMQuestionParser(QuestionParser):
195
- def __init__(self, question: Question, dataframe: pd.Series):
196
- super().__init__(question, dataframe)
203
+ def __init__(self, *args):
204
+ super().__init__(*args)
197
205
  self.genFeedbacks=[XMLTags.GENFEEDB]
198
206
  self.astEval = Interpreter()
199
207
 
200
208
  def setAnswers(self)->None:
201
- equation = self.df.get(DFIndex.RESULT)
202
- bps = self.df.get(DFIndex.BPOINTS)
209
+ equation = self.rawInput[DFIndex.RESULT]
210
+ bps = str( self.rawInput[DFIndex.BPOINTS] )
203
211
  ansElementsList:list[ET.Element]=[]
204
- varNames:list[str]= self.getVarsList(bps)
205
- varsDict, number = self.getVariablesDict(varNames)
206
- bulletPoints:list[ET.Element] = []
212
+ varNames:list[str]= self._getVarsList(bps)
213
+ self.question.variables, number = self._getVariablesDict(varNames)
207
214
  for n in range(number):
208
- self._setupAstIntprt(varsDict, n)
215
+ self._setupAstIntprt(self.question.variables, n)
209
216
  result = self.astEval(equation)
210
217
  if isinstance(result, float):
211
218
  ansElementsList.append(self.getNumericAnsElement( result = round(result,3) ))
212
- bpli = self.insertVariablesToBPoints(varsDict, bps, n)
213
- bulletPoints.append(self.formatBulletList(bpli))
214
219
  self.question.answerVariants = ansElementsList
215
- self.question.bulletList = bulletPoints
216
- self.setVariants(len(bulletPoints))
220
+ self.setVariants(len(ansElementsList))
217
221
  return None
218
222
 
219
223
  def setVariants(self, number:int):
@@ -225,38 +229,26 @@ class NFMQuestionParser(QuestionParser):
225
229
  self.question.category.maxVariants = number if number <= mvar else mvar
226
230
 
227
231
 
228
- @staticmethod
229
- def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int)-> str:
230
- """
231
- Für jeden Eintrag im varDict, wird im bulletPoints String der Substring "{key}" durch value[index] ersetzt
232
- """
233
- for k, v in varDict.items():
234
- s = r"{" + str(k) + r"}"
235
- matcher = re.compile(s)
236
- bulletPoints = matcher.sub(str(v[index]), bulletPoints)
237
- return bulletPoints
238
-
239
- def _setupAstIntprt(self, var:dict[str, list[str]], index:int)->None:
232
+ def _setupAstIntprt(self, var:dict[str, list[float|int]], index:int)->None:
240
233
  """Ubergibt die Parameter mit entsprechenden Variablen-Namen an den asteval-Interpreter.
241
234
 
242
235
  Dann kann dieser die equation lesen.
243
236
  """
244
- for k,v in var.items():
245
- comma = re.compile(r",")
246
- value = comma.sub(".",v[index])
247
- self.astEval.symtable[k] = float(value)
237
+ for name,value in var.items():
238
+ self.astEval.symtable[name] = value[index]
248
239
  return None
249
240
 
250
- def getVariablesDict(self, keyList: list)-> tuple[dict[str,list[str]],int]:
241
+ def _getVariablesDict(self, keyList: list)-> tuple[dict[str,list[float]],int]:
251
242
  """Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]"""
252
243
  dic:dict = {}
253
244
  num:int = 0
254
245
  for k in keyList:
255
- val = self.df.get(k)
246
+ val = self.rawInput[k]
256
247
  if isinstance(val, str) :
257
- li = val.split(";")
248
+ li = stringHelpers.stripWhitespace(val.split(";"))
258
249
  num = len(li)
259
- dic[str(k)] = li
250
+ vars:list[float] = [ float(i.replace(",",".")) for i in li ]
251
+ dic[str(k)] = vars
260
252
  else:
261
253
  dic[str(k)] = [str(val)]
262
254
  num = 1
@@ -264,7 +256,7 @@ class NFMQuestionParser(QuestionParser):
264
256
  return dic, num
265
257
 
266
258
  @staticmethod
267
- def getVarsList(bps: str|list[str])->list:
259
+ def _getVarsList(bps: str|list[str])->list:
268
260
  """
269
261
  Durchsucht den bulletPoints String nach den Variablen, die als "{var}" gekennzeichnet sind
270
262
  """
@@ -308,10 +300,10 @@ class MCQuestionParser(QuestionParser):
308
300
 
309
301
 
310
302
  def setAnswers(self)->list[ET.Element]:
311
- ansStyle = self.df.get(DFIndex.ANSTYPE)
312
- true = stringHelpers.stripWhitespace(self.df.get(DFIndex.TRUE).split(';'))
303
+ ansStyle = self.rawInput[DFIndex.ANSTYPE]
304
+ true = stringHelpers.stripWhitespace(self.rawInput[DFIndex.TRUE].split(';'))
313
305
  trueAnsList = stringHelpers.texWrapper(true, style=ansStyle)
314
- false = stringHelpers.stripWhitespace(self.df.get(DFIndex.FALSE).split(';'))
306
+ false = stringHelpers.stripWhitespace(self.rawInput[DFIndex.FALSE].split(';'))
315
307
  falseAnsList= stringHelpers.texWrapper(false, style=ansStyle)
316
308
  truefrac = 1/len(trueAnsList)*100
317
309
  falsefrac = 1/len(trueAnsList)*(-100)
@@ -6,6 +6,8 @@ import base64 as base64
6
6
  from excel2moodle.core import category, etHelpers
7
7
  from excel2moodle.core.globals import XMLTags, TextElements, DFIndex, questionTypes, parserSettings
8
8
  from excel2moodle.core.exceptions import QNotParsedException
9
+ from typing import Match
10
+ import re as re
9
11
 
10
12
 
11
13
  logger = logging.getLogger(__name__)
@@ -19,14 +21,15 @@ class Question():
19
21
  self.parent = parent
20
22
  self.qtype: str = qtype
21
23
  self.moodleType = questionTypes[qtype]
22
- self.points = ( points if points is not 0 else self.category.points)
24
+ self.points = ( points if points != 0 else self.category.points)
23
25
  self.element: ET.Element|None=None
24
26
  self.picture:Picture
25
27
  self.id:str
26
- self.qtextElements: list[ET.Element] = []
27
- self.bulletList: list[ET.Element] = []
28
+ self.qtextParagraphs: list[ET.Element] = []
29
+ self.bulletList: ET.Element|None = None
28
30
  self.answerVariants: list[ET.Element] = []
29
31
  self.variants:int|None = None
32
+ self.variables: dict[str, list[float|int]] = {}
30
33
  self.setID()
31
34
  self.standardTags = {
32
35
  "hidden":"false"
@@ -41,17 +44,32 @@ class Question():
41
44
  return "\n".join(li)
42
45
 
43
46
  def assemble(self, variant:int=1)->None:
47
+ textElements:list[ET.Element] = []
48
+ textElements.extend(self.qtextParagraphs)
49
+ logger.debug(f"Starting assembly of { self.id }")
44
50
  if self.element is not None:
45
51
  mainText = self.element.find(XMLTags.QTEXT)
52
+ logger.debug(f"found existing Text in element {mainText = }")
53
+ txtele = mainText.find("text")
54
+ if txtele is not None:
55
+ mainText.remove(txtele)
56
+ logger.debug(f"removed prevously existing questiontext")
46
57
  else: raise QNotParsedException("Cant assamble, if element is none", self.id)
47
- if len(self.bulletList)>0:
48
- self.qtextElements.append(self.bulletList[variant-1])
58
+ if self.variants is not None:
59
+ textElements.append(self.getBPointVariant(variant-1))
60
+ else:
61
+ textElements.append(self.bulletList)
49
62
  if hasattr(self, "picture") and self.picture.ready:
50
- self.qtextElements.append(self.picture.htmlTag)
63
+ textElements.append(self.picture.htmlTag)
51
64
  mainText.append(self.picture.element)
52
- mainText.append(etHelpers.getCdatTxtElement(self.qtextElements))
53
- self.element.insert(3, mainText)
65
+ mainText.append(etHelpers.getCdatTxtElement(textElements))
66
+ # self.element.insert(3, mainText)
67
+ logger.debug(f"inserted MainText to question element")
54
68
  if len( self.answerVariants ) > 0:
69
+ ans = self.element.find(XMLTags.ANSWER)
70
+ if ans is not None:
71
+ self.element.remove(ans)
72
+ logger.debug("removed previous answer element")
55
73
  self.element.insert(5, self.answerVariants[variant-1])
56
74
  return None
57
75
 
@@ -60,6 +78,28 @@ class Question():
60
78
  self.id: str = f"{self.category.id}{self.number:02d}"
61
79
  else: self.id:str = str(id)
62
80
 
81
+ def getBPointVariant(self, variant:int)->ET.Element:
82
+ if self.bulletList is None:
83
+ return None
84
+ varPlaceholder = re.compile(r"{(\w+)}") # matches {a}, {some_var}, etc.
85
+
86
+ def replaceMatch(match: Match[str])->str|int|float:
87
+ key = match.group(1)
88
+ if key in self.variables:
89
+ value = self.variables[key][variant]
90
+ return f"{value}".replace(".",",\\!")
91
+ return match.group(0) # keep original if no match
92
+
93
+ unorderedList = TextElements.ULIST.create()
94
+ for li in self.bulletList:
95
+ listItemText = li.text or ""
96
+ bullet = TextElements.LISTITEM.create()
97
+ bullet.text = varPlaceholder.sub(replaceMatch, listItemText)
98
+ logger.debug(f"Inserted Variables into List: {bullet}")
99
+ unorderedList.append(bullet)
100
+ return unorderedList
101
+
102
+
63
103
  class Picture():
64
104
  def __init__(self, picKey:str, imgFolder:Path, question:Question):
65
105
  self.pic = picKey
@@ -17,6 +17,8 @@ from excel2moodle.core.exceptions import InvalidFieldException, NanException
17
17
  import pandas as pd
18
18
  import logging
19
19
 
20
+ logger = logging.getLogger(__name__)
21
+
20
22
 
21
23
  class Validator():
22
24
  def __init__(self, category) -> None:
@@ -76,8 +78,24 @@ class Validator():
76
78
  if missing is not None:
77
79
  raise InvalidFieldException(msg, id, missing)
78
80
  self._getQuestion()
81
+ self._getData()
79
82
  return True
80
83
 
84
+ def _getData(self)->None:
85
+ self.qdata:dict[str, str|float|int|list]={}
86
+ for idx, val in self.df.items():
87
+ if not isinstance(idx, str):
88
+ logger.debug(f"Got a non String key in the spreadsheet, skipping it")
89
+ continue
90
+ if idx in self.qdata:
91
+ if isinstance(self.qdata[idx], list):
92
+ self.qdata[idx].append(val)
93
+ else:
94
+ existing = self.qdata[idx]
95
+ self.qdata[idx] = [existing, val]
96
+ else:
97
+ self.qdata[idx]=val
98
+
81
99
  def _mandatory(self)->tuple[bool,DFIndex|None]:
82
100
  """detects if all keys of mandatory are filled with values"""
83
101
  checker = pd.Series.notna(self.df)
@@ -8,9 +8,16 @@ import base64 as base64
8
8
  def stripWhitespace(stringList):
9
9
  stripped = []
10
10
  for i in stringList:
11
- stripped.append(i.strip())
11
+ s = i.strip()
12
+ if s:
13
+ stripped.append(s)
12
14
  return stripped
13
15
 
16
+ def stringToFloat(string:str)->float:
17
+ string.replace(",",".")
18
+ return float(string)
19
+
20
+
14
21
  def get_bullet_string(s):
15
22
  """Formatiert die Angaben zum Statischen System hübsch"""
16
23
  split = s.split(';')