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 CHANGED
@@ -24,18 +24,24 @@ Functionality
24
24
  * create single XML File from a selection of questions
25
25
  """
26
26
 
27
+ import logging
28
+ import logging.config as logConfig
27
29
  from importlib import metadata
28
30
  from importlib.metadata import version
31
+ from pathlib import Path
32
+
33
+ from excel2moodle.logger import LogWindowHandler, loggerConfig
34
+ from excel2moodle.ui.settings import Settings, SettingsKey
29
35
 
30
36
  try:
31
37
  __version__ = version("excel2moodle")
32
38
  except Exception:
33
39
  __version__ = "unknown"
34
40
 
35
-
41
+ e2mMetadata: dict = {}
36
42
  if __package__ is not None:
37
43
  meta = metadata.metadata(__package__)
38
- e2mMetadata: dict = {
44
+ e2mMetadata = {
39
45
  "version": __version__,
40
46
  "name": meta["name"],
41
47
  "description": meta["summary"],
@@ -46,91 +52,13 @@ if __package__ is not None:
46
52
  "issues": "https://gitlab.com/jbosse3/excel2moodle/issues",
47
53
  }
48
54
 
49
- import logging as logging
50
- from logging import config as logConfig
51
-
52
- from PySide6.QtCore import QObject, Signal
53
-
54
- # from excel2moodle.core import klausurGenerator
55
- # from excel2moodle.core import numericMultiQ
56
- # from excel2moodle.core import questionWriter
57
- # from excel2moodle.core import questionParser
58
- # from excel2moodle.core import stringHelpers
59
- # from excel2moodle.core import globals
60
- #
61
- # from excel2moodle.ui import kGeneratorQt
62
- from excel2moodle.ui import settings
63
-
64
- loggerConfig = {
65
- "version": 1,
66
- "disable_existing_loggers": False,
67
- "formatters": {
68
- "standard": {"format": "%(asctime)s [%(levelname)s] %(name)s: %(message)s"},
69
- },
70
- "handlers": {
71
- "default": {
72
- "level": "DEBUG",
73
- "formatter": "standard",
74
- "class": "logging.StreamHandler",
75
- "stream": "ext://sys.stdout", # Default is stderr
76
- },
77
- },
78
- "loggers": {
79
- "": { # root logger
80
- "handlers": ["default"],
81
- "level": "DEBUG",
82
- "propagate": True,
83
- },
84
- "excel2moodle.questionParser": {
85
- "handlers": ["default"],
86
- "level": "DEBUG",
87
- "propagate": True,
88
- },
89
- "__main__": { # if __name__ == '__main__'
90
- "handlers": ["default"],
91
- "level": "DEBUG",
92
- "propagate": True,
93
- },
94
- },
95
- }
96
-
97
-
98
- class QSignaler(QObject):
99
- signal = Signal(str)
100
-
101
-
102
- class LogHandler(logging.Handler):
103
- def __init__(self, *args, **kwargs) -> None:
104
- super().__init__(*args, **kwargs)
105
- self.emitter = QSignaler()
106
- # Define a formatter with log level and module
107
- log_format = "[%(levelname)s] %(module)s: %(message)s"
108
- self.formatter = logging.Formatter(log_format)
109
- self.setFormatter(self.formatter)
110
- self.logLevelColors = {
111
- "DEBUG": "gray",
112
- "INFO": "green",
113
- "WARNING": "orange",
114
- "ERROR": "red",
115
- "CRITICAL": "pink",
116
- }
117
-
118
- def emit(self, record) -> None:
119
- log_message = self.format(record)
120
- color = self.logLevelColors.get(record.levelname, "black")
121
- prettyMessage = f'<span style="color:{color};">{log_message}</span>'
122
- self.emitter.signal.emit(prettyMessage)
123
- return None
124
-
125
-
126
- settings = settings.Settings()
127
-
128
- logger = logging.getLogger(__name__)
129
- logging.config.dictConfig(config=loggerConfig)
130
-
131
- qSignalLogger = LogHandler()
132
- logger.addHandler(qSignalLogger)
133
55
 
56
+ settings = Settings()
57
+ logfile = Path(settings.get(SettingsKey.LOGFILE)).resolve()
58
+ e2mMetadata["logfile"] = logfile
59
+ if logfile.exists():
60
+ logfile.replace(f"{logfile}.old")
134
61
 
135
- for k, v in e2mMetadata.items():
136
- print(f"{k}: \t {v}\n")
62
+ mainLogger = logging.getLogger(__name__)
63
+ logConfig.dictConfig(config=loggerConfig)
64
+ qSignalLogger = LogWindowHandler()
excel2moodle/__main__.py CHANGED
@@ -1,19 +1,27 @@
1
- """Main Function to make the Package executable"""
1
+ """Main Function to make the Package executable."""
2
+
3
+ import signal
2
4
 
3
5
  from PySide6 import QtWidgets, sys
4
6
 
7
+ from excel2moodle import e2mMetadata, mainLogger, qSignalLogger
5
8
  from excel2moodle.core import dataStructure
6
9
  from excel2moodle.ui import appUi as ui
7
10
  from excel2moodle.ui.settings import Settings
8
11
 
9
12
 
10
13
  def main() -> None:
14
+ mainLogger.addHandler(qSignalLogger)
15
+ signal.signal(signal.SIGINT, signal.SIG_DFL)
11
16
  app = QtWidgets.QApplication(sys.argv)
12
17
  settings = Settings()
13
18
  database: dataStructure.QuestionDB = dataStructure.QuestionDB(settings)
14
19
  window = ui.MainWindow(settings, database)
15
20
  database.window = window
16
21
  window.show()
22
+ for k, v in e2mMetadata.items():
23
+ msg = f"{k:^14s}: {v}"
24
+ mainLogger.info(msg)
17
25
  sys.exit(app.exec())
18
26
 
19
27
 
@@ -1,6 +1,4 @@
1
- """
2
- These Modules are the heart of the excel2moodle Package
3
- """
1
+ """These Modules are the heart of the excel2moodle Package."""
4
2
 
5
3
  # from pandas._config import config
6
4
  # from excel2moodle.core import questionWriter
@@ -10,8 +10,9 @@ from excel2moodle.core.parser import (
10
10
  QNotParsedException,
11
11
  )
12
12
  from excel2moodle.core.question import Question
13
+ from excel2moodle.logger import LogAdapterQuestionID
13
14
 
14
- logger = logging.getLogger(__name__)
15
+ loggerObj = logging.getLogger(__name__)
15
16
 
16
17
 
17
18
  class Category:
@@ -32,14 +33,14 @@ class Category:
32
33
  self.version = int(version)
33
34
  self.questions: dict[int, Question] = {}
34
35
  self.maxVariants: int | None = None
35
- logger.info(f"initializing {self.NAME=}")
36
+ loggerObj.info("initializing Category %s", self.NAME)
36
37
 
37
38
  @property
38
- def name(self):
39
+ def name(self) -> str:
39
40
  return self.NAME
40
41
 
41
42
  @property
42
- def id(self):
43
+ def id(self) -> str:
43
44
  return f"{self.version}{self.n:02d}"
44
45
 
45
46
  def __hash__(self) -> int:
@@ -56,39 +57,38 @@ class Category:
56
57
  questionData: dict | None = None,
57
58
  xmlTree: ET._Element | None = None,
58
59
  ) -> bool:
60
+ """Parse the given question."""
61
+ logger = LogAdapterQuestionID(loggerObj, {"qID": q.id})
59
62
  if q.element is not None:
60
- logger.info(f"Question {q.id} is already parsed")
63
+ logger.info("Question already parsed")
61
64
  return True
65
+ if q.qtype == "NF":
66
+ parser = NFQuestionParser(q, questionData)
67
+ logger.debug("setup a new NF parser ")
68
+ elif q.qtype == "MC":
69
+ parser = MCQuestionParser(q, questionData)
70
+ logger.debug("setup a new MC parser ")
71
+ elif q.qtype == "NFM":
72
+ parser = NFMQuestionParser(q, questionData)
73
+ logger.debug("setup a new NFM parser ")
62
74
  else:
63
- if q.qtype == "NF":
64
- parser = NFQuestionParser(q, questionData)
65
- logger.debug("setup a new NF parser ")
66
- elif q.qtype == "MC":
67
- parser = MCQuestionParser(q, questionData)
68
- logger.debug("setup a new MC parser ")
69
- elif q.qtype == "NFM":
70
- parser = NFMQuestionParser(q, questionData)
71
- logger.debug("setup a new NFM parser ")
72
- else:
73
- logger.error("ERROR, couldn't setup Parser")
74
- return False
75
- try:
76
- parser.parse(xmlTree=xmlTree)
77
- return True
78
- except QNotParsedException as e:
79
- logger.critical(
80
- f"The Question {q.id} couldn't be parsed",
81
- exc_info=e,
82
- stack_info=True,
83
- )
84
- return False
85
- finally:
86
- del parser
75
+ logger.error("couldn't setup Parser")
76
+ return False
77
+ try:
78
+ parser.parse(xmlTree=xmlTree)
79
+ return True
80
+ except QNotParsedException as e:
81
+ logger.critical(
82
+ "Question couldn't be parsed",
83
+ exc_info=e,
84
+ stack_info=True,
85
+ )
86
+ return False
87
+ finally:
88
+ del parser
87
89
 
88
90
  def getCategoryHeader(self) -> ET.Element:
89
- """vor den Fragen einer Kategorie wird ein
90
- <question type='category'> eingefügt mit Name und Beschreibung
91
- """
91
+ """Insert an <question type='category'> before all Questions of this Category."""
92
92
  header = ET.Element("question", type="category")
93
93
  cat = ET.SubElement(header, "category")
94
94
  info = ET.SubElement(header, "info", format="html")
@@ -1,68 +1,53 @@
1
- """Main Module which does the heavy lifting
1
+ """Main Module which does the heavy lifting.
2
2
 
3
3
  At the heart is the class ``xmlTest``
4
4
  """
5
5
 
6
6
  import logging
7
7
  from pathlib import Path
8
+ from typing import TYPE_CHECKING
8
9
 
9
- import lxml.etree as ET
10
+ import lxml.etree as ET # noqa: N812
10
11
  import pandas as pd
11
- from PySide6 import QtCore, QtWidgets
12
- from PySide6.QtCore import Signal
13
- from PySide6.QtWidgets import QMainWindow, QMessageBox, QTreeWidget
12
+ from PySide6 import QtWidgets
14
13
 
15
- from excel2moodle import QSignaler
16
14
  from excel2moodle.core import stringHelpers
17
15
  from excel2moodle.core.category import Category
18
16
  from excel2moodle.core.exceptions import InvalidFieldException, QNotParsedException
19
17
  from excel2moodle.core.question import Question
20
18
  from excel2moodle.core.questionValidator import Validator
19
+ from excel2moodle.logger import QSignaler
21
20
  from excel2moodle.ui.dialogs import QuestionVariantDialog
22
- from excel2moodle.ui.settings import Settings
23
- from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
21
+ from excel2moodle.ui.settings import Settings, SettingsKey
22
+ from excel2moodle.ui.treewidget import QuestionItem
23
+
24
+ if TYPE_CHECKING:
25
+ from PySide6.QtWidgets import QMainWindow
24
26
 
25
27
  logger = logging.getLogger(__name__)
26
28
 
27
29
 
28
30
  class QuestionDB:
29
- """oberste Klasse für den Test"""
31
+ """oberste Klasse für den Test."""
30
32
 
31
33
  dataChanged = QSignaler()
32
34
 
33
- def __init__(self, settings: Settings):
35
+ def __init__(self, settings: Settings) -> None:
34
36
  self.settings = settings
35
- self.spreadSheetPath = Path()
36
- self.mainPath = Path()
37
37
  self.window: QMainWindow | None = None
38
38
  self.version = None
39
39
  self.categoriesMetaData = pd.DataFrame()
40
40
  self.categories: dict[str, Category] = {}
41
- self.settings.shPathChanged.connect(self.onSheetPathChanged)
42
-
43
- @QtCore.Slot(Path)
44
- def onSheetPathChanged(self, sheet: Path) -> None:
45
- logger.debug("Slot, new Spreadsheet triggered")
46
- self.spreadSheetPath = sheet
47
- svgFolder = self.spreadSheetPath.parent / str(
48
- self.settings.get("core/pictureSubFolder", default="Abbildungen_SVG")
49
- )
50
- svgFolder.resolve()
51
- self.settings.set("core/pictureFolder", svgFolder)
52
- self.retrieveCategoriesData()
53
- self.parseAll()
54
41
 
55
- def retrieveCategoriesData(self) -> None:
56
- """Scans through the sheet with the metadata for all categories
42
+ def readSpreadsheetData(self, sheet: Path) -> None:
43
+ """Read the metadata and questions from the spreadsheet.
57
44
 
58
- The information that will be shown in the UI like description
59
- and points is retrieved from one spreadsheet sheet.
60
45
  This method gathers this information and stores it in the
61
46
  ``categoriesMetaData`` dataframe
47
+ It also reads the question data and stores it in ``self.categories = {}``
62
48
  """
63
-
64
49
  logger.info("Start Parsing the Excel Metadata Sheet\n")
65
- with open(self.spreadSheetPath, "rb") as f:
50
+ with Path(sheet).open("rb") as f:
66
51
  excelFile = pd.ExcelFile(f)
67
52
  self.categoriesMetaData = pd.read_excel(
68
53
  f,
@@ -71,13 +56,15 @@ class QuestionDB:
71
56
  index_col=0,
72
57
  )
73
58
  logger.info("Sucessfully read categoriesMetaData")
74
- print(self.categoriesMetaData)
75
59
  self.categories = {}
76
60
  for sh in excelFile.sheet_names:
77
61
  if sh.startswith("KAT"):
78
62
  n = int(sh[4:])
79
63
  katDf = pd.read_excel(
80
- f, sheet_name=str(sh), index_col=0, header=None
64
+ f,
65
+ sheet_name=str(sh),
66
+ index_col=0,
67
+ header=None,
81
68
  )
82
69
  if not katDf.empty:
83
70
  p = self.categoriesMetaData["Punkte"].iloc[n - 1]
@@ -92,10 +79,9 @@ class QuestionDB:
92
79
  points=points,
93
80
  version=version,
94
81
  )
95
- self.dataChanged.signal.emit("whoo")
96
- return None
82
+ # self.dataChanged.signal.emit("whoo")
97
83
 
98
- def parseAll(self):
84
+ def parseAll(self) -> None:
99
85
  self.mainTree = ET.Element("quiz")
100
86
  for c in self.categories.values():
101
87
  validator = Validator(c)
@@ -108,9 +94,8 @@ class QuestionDB:
108
94
  try:
109
95
  check = validator.validate()
110
96
  except InvalidFieldException as e:
111
- logger.error(
112
- f"Question {c.id}{
113
- q:02d} is invalid.",
97
+ logger.exception(
98
+ f"Question {c.id}{q:02d} is invalid.",
114
99
  exc_info=e,
115
100
  )
116
101
  if check:
@@ -118,13 +103,17 @@ class QuestionDB:
118
103
  try:
119
104
  c.parseQ(c.questions[q], validator.qdata)
120
105
  except QNotParsedException as e:
121
- logger.error(
106
+ logger.exception(
122
107
  f"Frage {
123
- c.questions[q].id} konnte nicht erstellt werden",
108
+ c.questions[q].id
109
+ } konnte nicht erstellt werden",
124
110
  exc_info=e,
125
111
  )
126
112
 
127
- def appendQuestions(self, questions: list[QuestionItem], file: Path | None = None):
113
+ def appendQuestions(
114
+ self, questions: list[QuestionItem], file: Path | None = None
115
+ ) -> None:
116
+ """Append selected question Elements to the tree."""
128
117
  tree = ET.Element("quiz")
129
118
  catdict: dict[Category, list[Question]] = {}
130
119
  for q in questions:
@@ -132,15 +121,13 @@ class QuestionDB:
132
121
  cat = q.parent().getCategory()
133
122
  if cat not in catdict:
134
123
  catdict[cat] = []
135
- print(f"Category is parent of Q {cat=}")
136
124
  catdict[cat].append(q.getQuestion())
137
125
  for cat, qlist in catdict.items():
138
- print(f"{cat=}, mit fragen {qlist=}")
139
126
  self.appendQElements(
140
127
  cat,
141
128
  qlist,
142
129
  tree=tree,
143
- includeHeader=self.settings.value("testGen/includeCats"),
130
+ includeHeader=self.settings.get(SettingsKey.INCLUDEINCATS),
144
131
  )
145
132
  stringHelpers.printDom(tree, file=file)
146
133
 
@@ -154,23 +141,20 @@ class QuestionDB:
154
141
  if includeHeader:
155
142
  tree.append(cat.getCategoryHeader())
156
143
  logger.debug(f"Appended a new category item {cat=}")
157
- sameVariant = False
158
- variant = 1
144
+ variant: int = self.settings.get(SettingsKey.QUESTIONVARIANT)
159
145
  for q in qList:
160
146
  if cat.parseQ(q):
161
147
  if q.variants is not None:
162
- if sameVariant is False:
148
+ if variant == 0 or variant > q.variants:
163
149
  dialog = QuestionVariantDialog(self.window, q)
164
150
  if dialog.exec() == QtWidgets.QDialog.Accepted:
165
151
  variant = dialog.variant
166
- sameVariant = dialog.categoryWide
167
152
  logger.debug(f"Die Fragen-Variante {variant} wurde gewählt")
168
153
  q.assemble(variant)
169
154
  else:
170
- print("skipping this question")
155
+ pass
171
156
  else:
172
157
  q.assemble()
173
158
  tree.append(q.element)
174
159
  else:
175
160
  logger.warning(f"Frage {q} wurde nicht erstellt")
176
- return None
@@ -1,4 +1,4 @@
1
- """Helper Module which aids in creating XML-Elements for the Questions
1
+ """Helper Module which aids in creating XML-Elements for the Questions.
2
2
 
3
3
  This module host different functions. All of them will return an ``lxml.etree.Element``
4
4
  """
@@ -12,7 +12,7 @@ from .globals import DFIndex, XMLTags
12
12
 
13
13
 
14
14
  def getElement(eleName: str, text: str, **attribs) -> ET.Element:
15
- """Creates an XML-Element with text
15
+ """Creates an XML-Element with text.
16
16
 
17
17
  If ``type(text)``is a ``QuestionFields``, the specific field is directly read.
18
18
  Otherwise it will include whatever is ``text`` as a string
@@ -20,7 +20,6 @@ def getElement(eleName: str, text: str, **attribs) -> ET.Element:
20
20
  raises:
21
21
  NanException if the spreadsheet cell of text:QuestionFields is ``nan``
22
22
  """
23
-
24
23
  toEle = ET.Element(eleName)
25
24
  toEle.text = str(text)
26
25
  for k, v in attribs.items():
@@ -29,8 +28,7 @@ def getElement(eleName: str, text: str, **attribs) -> ET.Element:
29
28
 
30
29
 
31
30
  def getTextElement(eleName: str, text: str | DFIndex, **attribs) -> ET.Element:
32
- """Creates two nested elements: ``eleName`` with child ``text`` which holds the text"""
33
-
31
+ """Creates two nested elements: ``eleName`` with child ``text`` which holds the text."""
34
32
  toEle = ET.Element(eleName, **attribs)
35
33
  child = getElement("text", text)
36
34
  toEle.append(child)
@@ -38,8 +36,7 @@ def getTextElement(eleName: str, text: str | DFIndex, **attribs) -> ET.Element:
38
36
 
39
37
 
40
38
  def getCdatTxtElement(subEle: ET._Element | list[ET._Element]) -> ET.Element:
41
- """Puts all ``subEle`` as ``str`` into a ``<text><![CDATA[...subEle...]]</text>`` element"""
42
-
39
+ """Puts all ``subEle`` as ``str`` into a ``<text><![CDATA[...subEle...]]</text>`` element."""
43
40
  textEle = ET.Element(XMLTags.TEXT)
44
41
  if isinstance(subEle, list):
45
42
  elementString: list = []
@@ -47,21 +44,19 @@ def getCdatTxtElement(subEle: ET._Element | list[ET._Element]) -> ET.Element:
47
44
  elementString.append(ET.tostring(i, encoding="unicode", pretty_print=True))
48
45
  textEle.text = ET.CDATA("".join(elementString))
49
46
  return textEle
50
- else:
51
- textEle.text = ET.CDATA(
52
- ET.tostring(subEle, encoding="unicode", pretty_print=True)
53
- )
54
- return textEle
47
+ textEle.text = ET.CDATA(
48
+ ET.tostring(subEle, encoding="unicode", pretty_print=True),
49
+ )
50
+ return textEle
55
51
 
56
52
 
57
53
  def getFeedBEle(
58
- feedback: XMLTags, text: str | None = None, style: TextElements | None = None
54
+ feedback: XMLTags,
55
+ text: str | None = None,
56
+ style: TextElements | None = None,
59
57
  ) -> ET.Element:
60
58
  """Gets ET Elements with the feedback for the question."""
61
- if style is None:
62
- span = feedBElements[feedback]
63
- else:
64
- span = style.create()
59
+ span = feedBElements[feedback] if style is None else style.create()
65
60
  if text is None:
66
61
  text = feedbackStr[feedback]
67
62
  ele = ET.Element(feedback, format="html")
@@ -8,7 +8,7 @@ class QNotParsedException(Exception):
8
8
 
9
9
 
10
10
  class NanException(QNotParsedException):
11
- def __init__(self, message, qID, field, *args, **kwargs):
11
+ def __init__(self, message, qID, field, *args, **kwargs) -> None:
12
12
  super().__init__(message, qID, *args, **kwargs)
13
13
  self.field = field
14
14
 
@@ -10,7 +10,7 @@ questionTypes = {
10
10
 
11
11
 
12
12
  class DFIndex(StrEnum):
13
- """This Enum holds the identifier string for for the spreadsheet and the string for the xml-tag
13
+ """The identifier string for for the spreadsheet and the string for the xml-tag.
14
14
 
15
15
  Each enum corresponds to a list of two values.
16
16
  The first Value is the index in the spreadsheet, the second is the name of the xml-tag
@@ -26,6 +26,7 @@ class DFIndex(StrEnum):
26
26
  PICTURE = "picture"
27
27
  NUMBER = "number"
28
28
  ANSTYPE = "answerType"
29
+ TOLERANCE = "tolerance"
29
30
 
30
31
 
31
32
  class TextElements(Enum):
@@ -1,11 +1,11 @@
1
- """Numeric Multi Questions Module to calculate results from a formula
1
+ """Numeric Multi Questions Module to calculate results from a formula.
2
2
 
3
3
  This module calculates a series of results from al matrix of variables.
4
4
  For each column in the matrix there will be one result.
5
5
  As well it returns a bullet points string that shows the numerical values corresponding to the set of variables
6
6
  """
7
7
 
8
- import re as re
8
+ import re
9
9
 
10
10
  import pandas as pd
11
11
  from asteval import Interpreter
@@ -14,7 +14,7 @@ astEval = Interpreter()
14
14
 
15
15
 
16
16
  def getVariablesDict(df: pd.DataFrame, keyList: list, index: int) -> dict:
17
- """Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]"""
17
+ """Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]."""
18
18
  dic = {}
19
19
  for k in keyList:
20
20
  val = df.loc[str(k)][index]
@@ -23,7 +23,6 @@ def getVariablesDict(df: pd.DataFrame, keyList: list, index: int) -> dict:
23
23
  dic[str(k)] = li
24
24
  else:
25
25
  dic[str(k)] = [str(val)]
26
- print(f"Folgende Variablen wurden gefunden:\n{dic}\n")
27
26
  return dic
28
27
 
29
28
 
@@ -36,12 +35,11 @@ def setParameters(parameters: dict, index: int) -> None:
36
35
  comma = re.compile(r",")
37
36
  value = comma.sub(".", v[index])
38
37
  astEval.symtable[k] = float(value)
39
- return None
40
38
 
41
39
 
42
40
  def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int) -> str:
43
- """
44
- Für jeden Eintrag im varDict, wird im bulletPoints String der Substring "{key}" durch value[index] ersetzt
41
+ """Für jeden Eintrag im varDict, wird im bulletPoints String der
42
+ Substring "{key}" durch value[index] ersetzt.
45
43
  """
46
44
  for k, v in varDict.items():
47
45
  s = r"{" + str(k) + r"}"
@@ -51,9 +49,7 @@ def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int) -> st
51
49
 
52
50
 
53
51
  def getVarsList(bps: str) -> list:
54
- """
55
- Durchsucht den bulletPoints String nach den Variablen, die als "{var}" gekennzeichnet sind
56
- """
52
+ """Durchsucht den bulletPoints String nach den Variablen `{var}`."""
57
53
  vars = re.findall(r"\{\w\}", str(bps))
58
54
  variablen = []
59
55
  for v in vars:
@@ -62,9 +58,12 @@ def getVarsList(bps: str) -> list:
62
58
 
63
59
 
64
60
  def parseNumericMultiQuestion(
65
- datFrame: pd.DataFrame, bulletPoints: str, equation: str, questionIndex: int
61
+ datFrame: pd.DataFrame,
62
+ bulletPoints: str,
63
+ equation: str,
64
+ questionIndex: int,
66
65
  ) -> tuple[list[str], list[float]]:
67
- """Berechnet die Ergebnisse anhand der Variablen in *bulletPoints*
66
+ """Berechnet die Ergebnisse anhand der Variablen in *bulletPoints*.
68
67
 
69
68
  Gibt eine Liste mit allen Ergebnissen zurück
70
69
  und eine Liste mit den bulletPoints-Strings, die die Numerischen Variablen enthalten