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.
excel2moodle/__init__.py CHANGED
@@ -1,14 +1,20 @@
1
1
  """This Python program helps to create Moodle questions in less time.
2
2
 
3
- The aim is to put alle the information for the questions into a spreadsheet file, and then parse it, to generate Moodle compliant XML-Files.
4
- Furthermore this program lets you create a single ``.xml``-File with a selection of questions, that then can be imported to a Moodle-Test.
3
+ The aim is to put alle the information for the questions into a spreadsheet
4
+ file, and then parse it, to generate Moodle compliant XML-Files.
5
+ Furthermore this program lets you create a single ``.xml``-File with a selection
6
+ of questions, that then can be imported to a Moodle-Test.
5
7
 
6
8
  Concept
7
9
  =========
8
- The concept is, to store the different questions into categories of similar types and difficulties of questions, for each of which, a separated sheet in the Spreadsheet document should be created.
9
-
10
- There Should be a sheet called *"Kategorien"*, where an overview over the different categories is stored.
11
- This sheet stores The names and descriptions, for all categories. The name have to be the same as the actual sheet names with the questions.
10
+ The concept is, to store the different questions into categories of similar
11
+ types and difficulties of questions, for each of which, a separated sheet in
12
+ the Spreadsheet document should be created.
13
+
14
+ There Should be a sheet called *"Kategorien"*, where an overview over the
15
+ different categories is stored.
16
+ This sheet stores The names and descriptions, for all categories.
17
+ The name have to be the same as the actual sheet names with the questions.
12
18
  Furthermore the points used for grading, are set in the "Kategorien" sheet
13
19
 
14
20
  Functionality
@@ -17,111 +23,47 @@ Functionality
17
23
  * Parse Numeric Questions, each into one XML file
18
24
  * create single XML File from a selection of questions
19
25
  """
26
+
27
+ import logging
28
+ import logging.config as logConfig
20
29
  from importlib import metadata
21
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
35
+
22
36
  try:
23
37
  __version__ = version("excel2moodle")
24
38
  except Exception:
25
39
  __version__ = "unknown"
26
40
 
27
-
28
- if not __package__ == None:
41
+ e2mMetadata: dict = {}
42
+ if __package__ is not None:
29
43
  meta = metadata.metadata(__package__)
30
- e2mMetadata:dict = {
44
+ e2mMetadata = {
31
45
  "version": __version__,
32
- "name": meta['name'],
33
- "description": meta['summary'],
34
- "author": meta['author'],
35
- "license": meta['license-expression'],
46
+ "name": meta["name"],
47
+ "description": meta["summary"],
48
+ "author": meta["author"],
49
+ "license": meta["license-expression"],
36
50
  "documentation": "https://jbosse3.gitlab.io/excel2moodle",
37
- "homepage": meta['project-url'].split()[1],
51
+ "homepage": meta["project-url"].split()[1],
38
52
  "issues": "https://gitlab.com/jbosse3/excel2moodle/issues",
39
- }
40
-
41
- # from excel2moodle.core import klausurGenerator
42
- # from excel2moodle.core import numericMultiQ
43
- # from excel2moodle.core import questionWriter
44
- # from excel2moodle.core import questionParser
45
- # from excel2moodle.core import stringHelpers
46
- # from excel2moodle.core import globals
47
- #
48
- # from excel2moodle.ui import kGeneratorQt
49
- from excel2moodle.ui import settings
53
+ }
50
54
 
51
- from PySide6.QtCore import QObject, Signal
52
- import logging as logging
53
- from logging import config as logConfig
54
55
 
55
- loggerConfig = {
56
- 'version': 1,
57
- 'disable_existing_loggers': False,
58
- 'formatters': {
59
- 'standard': {
60
- 'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
61
- },
62
- },
63
- 'handlers': {
64
- 'default': {
65
- 'level': 'DEBUG',
66
- 'formatter': 'standard',
67
- 'class': 'logging.StreamHandler',
68
- 'stream': 'ext://sys.stdout', # Default is stderr
69
- },
70
- },
71
- 'loggers': {
72
- '': { # root logger
73
- 'handlers': ['default'],
74
- 'level': 'DEBUG',
75
- 'propagate': True
76
- },
77
- 'excel2moodle.questionParser': {
78
- 'handlers': ['default'],
79
- 'level': 'DEBUG',
80
- 'propagate': True
81
- },
82
- '__main__': { # if __name__ == '__main__'
83
- 'handlers': ['default'],
84
- 'level': 'DEBUG',
85
- 'propagate': True
86
- },
87
- }
88
- }
89
-
90
- class QSignaler(QObject):
91
- signal = Signal(str)
92
-
93
- class LogHandler(logging.Handler):
94
- def __init__(self, *args, **kwargs)->None:
95
- super().__init__(*args,**kwargs)
96
- self.emitter = QSignaler()
97
- # Define a formatter with log level and module
98
- log_format = "[%(levelname)s] %(module)s: %(message)s"
99
- self.formatter = logging.Formatter(log_format)
100
- self.setFormatter(self.formatter)
101
- self.logLevelColors = {
102
- 'DEBUG': 'gray',
103
- 'INFO': 'green',
104
- 'WARNING': 'orange',
105
- 'ERROR': 'red',
106
- 'CRITICAL': 'pink'
107
- }
108
-
109
- def emit(self, record)->None:
110
- log_message = self.format(record)
111
- color = self.logLevelColors.get(record.levelname, 'black')
112
- prettyMessage = f'<span style="color:{color};">{log_message}</span>'
113
- self.emitter.signal.emit(prettyMessage)
114
- return None
115
-
116
-
117
- settings = settings.Settings()
56
+ settings = Settings()
57
+ logfile = Path(settings.get(SettingsKey.LOGFILE)).resolve()
58
+ e2mMetadata["logfile"] = logfile
59
+ logfile.replace(f"{logfile}.old")
118
60
 
119
61
  logger = logging.getLogger(__name__)
120
- logging.config.dictConfig(config=loggerConfig)
121
-
122
- qSignalLogger = LogHandler()
62
+ logConfig.dictConfig(config=loggerConfig)
63
+ qSignalLogger = LogWindowHandler()
123
64
  logger.addHandler(qSignalLogger)
124
65
 
125
66
 
126
- for k,v in e2mMetadata.items():
127
- print(f"{k}: \t {v}\n")
67
+ for k, v in e2mMetadata.items():
68
+ msg = f"{k:^14s}: {v}"
69
+ logger.info(msg)
excel2moodle/__main__.py CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  from PySide6 import QtWidgets, sys
4
4
 
5
- from excel2moodle.core import dataStructure
5
+ from excel2moodle.core import dataStructure
6
6
  from excel2moodle.ui import appUi as ui
7
7
  from excel2moodle.ui.settings import Settings
8
8
 
9
9
 
10
- def main()->None:
10
+ def main() -> None:
11
11
  app = QtWidgets.QApplication(sys.argv)
12
12
  settings = Settings()
13
- database:dataStructure.QuestionDB = dataStructure.QuestionDB(settings)
13
+ database: dataStructure.QuestionDB = dataStructure.QuestionDB(settings)
14
14
  window = ui.MainWindow(settings, database)
15
15
  database.window = window
16
16
  window.show()
17
17
  sys.exit(app.exec())
18
18
 
19
- if __name__ =="__main__":
20
- main()
21
19
 
20
+ if __name__ == "__main__":
21
+ main()
@@ -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
@@ -1,34 +1,46 @@
1
-
2
1
  import logging
3
- import pandas as pd
2
+
4
3
  import lxml.etree as ET
4
+ import pandas as pd
5
5
 
6
- from excel2moodle.core.exceptions import InvalidFieldException
7
- from excel2moodle.core.questionValidator import Validator
8
- logger = logging.getLogger(__name__)
6
+ from excel2moodle.core.parser import (
7
+ MCQuestionParser,
8
+ NFMQuestionParser,
9
+ NFQuestionParser,
10
+ QNotParsedException,
11
+ )
9
12
  from excel2moodle.core.question import Question
13
+ from excel2moodle.logger import LogAdapterQuestionID
10
14
 
11
- from excel2moodle.core.globals import XMLTags, TextElements, DFIndex, questionTypes, parserSettings
12
- from excel2moodle.core.parser import NFMQuestionParser, NFQuestionParser, MCQuestionParser, QNotParsedException
15
+ loggerObj = logging.getLogger(__name__)
13
16
 
14
- class Category():
15
- def __init__(self, n:int, name:str, description:str, dataframe: pd.DataFrame, points:float=0, version:int=0)->None:
17
+
18
+ class Category:
19
+ def __init__(
20
+ self,
21
+ n: int,
22
+ name: str,
23
+ description: str,
24
+ dataframe: pd.DataFrame,
25
+ points: float = 0,
26
+ version: int = 0,
27
+ ) -> None:
16
28
  self.n = n
17
29
  self.NAME = name
18
30
  self.desc = str(description)
19
- self.dataframe:pd.DataFrame = dataframe
31
+ self.dataframe: pd.DataFrame = dataframe
20
32
  self.points = points
21
33
  self.version = int(version)
22
- self.questions:dict[int,Question] = {}
23
- self.maxVariants:int|None = None
24
- logger.info(f"initializing {self.NAME =}")
34
+ self.questions: dict[int, Question] = {}
35
+ self.maxVariants: int | None = None
36
+ loggerObj.info("initializing Category %s", self.NAME)
25
37
 
26
38
  @property
27
- def name(self):
39
+ def name(self) -> str:
28
40
  return self.NAME
29
41
 
30
42
  @property
31
- def id(self):
43
+ def id(self) -> str:
32
44
  return f"{self.version}{self.n:02d}"
33
45
 
34
46
  def __hash__(self) -> int:
@@ -39,41 +51,49 @@ class Category():
39
51
  return self.NAME == other.NAME
40
52
  return False
41
53
 
42
- def parseQ(self, q:Question, questionData:dict|None=None, xmlTree:ET._Element|None=None)->bool:
54
+ def parseQ(
55
+ self,
56
+ q: Question,
57
+ questionData: dict | None = None,
58
+ xmlTree: ET._Element | None = None,
59
+ ) -> bool:
60
+ """Parse the given question."""
61
+ logger = LogAdapterQuestionID(loggerObj, {"qID": q.id})
43
62
  if q.element is not None:
44
- logger.info(f"Question {q.id} is already parsed")
63
+ logger.info("Question already parsed")
45
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 ")
46
74
  else:
47
- if q.qtype == "NF":
48
- parser = NFQuestionParser( q, questionData)
49
- logger.debug(f"setup a new NF parser ")
50
- elif q.qtype == "MC":
51
- parser = MCQuestionParser( q, questionData)
52
- logger.debug(f"setup a new MC parser ")
53
- elif q.qtype == "NFM":
54
- parser = NFMQuestionParser( q, questionData)
55
- logger.debug(f"setup a new NFM parser ")
56
- else:
57
- logger.error(f"ERROR, couldn't setup Parser")
58
- return False
59
- try:
60
- parser.parse(xmlTree=xmlTree)
61
- return True
62
- except QNotParsedException as e:
63
- logger.critical(f"The Question {q.id} couldn't be parsed", exc_info=e, stack_info=True)
64
- return False
65
- finally:
66
- 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
67
89
 
68
- def getCategoryHeader(self)->ET.Element:
69
- """vor den Fragen einer Kategorie wird ein <question type='category'> eingefügt mit Name und Beschreibung"""
70
- header = ET.Element('question', type='category')
71
- cat = ET.SubElement(header,"category")
72
- info = ET.SubElement(header,"info", format='html')
73
- ET.SubElement(cat,"text").text = f"$module$/top/{self.NAME}"
74
- ET.SubElement(info,"text").text = str(self.desc)
75
- ET.SubElement(header,"idnumber").text = str(self.n)
90
+ def getCategoryHeader(self) -> ET.Element:
91
+ """Insert an <question type='category'> before all Questions of this Category."""
92
+ header = ET.Element("question", type="category")
93
+ cat = ET.SubElement(header, "category")
94
+ info = ET.SubElement(header, "info", format="html")
95
+ ET.SubElement(cat, "text").text = f"$module$/top/{self.NAME}"
96
+ ET.SubElement(info, "text").text = str(self.desc)
97
+ ET.SubElement(header, "idnumber").text = str(self.n)
76
98
  ET.indent(header)
77
99
  return header
78
-
79
-
@@ -1,88 +1,87 @@
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
+ import logging
6
7
  from pathlib import Path
7
- from PySide6 import QtCore
8
- from PySide6 import QtWidgets
9
- from PySide6.QtCore import Signal
10
- from PySide6.QtWidgets import QMainWindow, QMessageBox, QTreeWidget
8
+ from typing import TYPE_CHECKING
9
+
10
+ import lxml.etree as ET # noqa: N812
11
11
  import pandas as pd
12
- import lxml.etree as ET
13
- import logging
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
- from excel2moodle.ui.dialogs import QuestionVariantDialog
19
- from excel2moodle.ui.treewidget import QuestionItem, CategoryItem
20
- from excel2moodle.core.questionValidator import Validator
21
- from excel2moodle.ui.settings import Settings
22
- from excel2moodle.core.question import Question
23
16
  from excel2moodle.core.exceptions import InvalidFieldException, QNotParsedException
17
+ from excel2moodle.core.question import Question
18
+ from excel2moodle.core.questionValidator import Validator
19
+ from excel2moodle.logger import QSignaler
20
+ from excel2moodle.ui.dialogs import QuestionVariantDialog
21
+ from excel2moodle.ui.settings import Settings, SettingsKey
22
+ from excel2moodle.ui.treewidget import QuestionItem
24
23
 
24
+ if TYPE_CHECKING:
25
+ from PySide6.QtWidgets import QMainWindow
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
 
28
29
 
29
- class QuestionDB():
30
- """oberste Klasse für den Test"""
30
+ class QuestionDB:
31
+ """oberste Klasse für den Test."""
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
- self.window:QMainWindow|None = None
37
+ self.window: QMainWindow | None = None
38
38
  self.version = None
39
39
  self.categoriesMetaData = pd.DataFrame()
40
- self.categories:dict[str,Category]={}
41
- self.settings.shPathChanged.connect(self.onSheetPathChanged)
40
+ self.categories: dict[str, Category] = {}
42
41
 
43
- @QtCore.Slot(Path)
44
- def onSheetPathChanged(self, sheet:Path)->None:
45
- logger.debug("Slot, new Spreadsheet triggered")
46
- self.spreadSheetPath = sheet
47
- self.svgFolder = (self.spreadSheetPath.parent / 'Abbildungen_SVG')
48
- self.retrieveCategoriesData()
49
- self.parseAll()
42
+ def readSpreadsheetData(self, sheet: Path) -> None:
43
+ """Read the metadata and questions from the spreadsheet.
50
44
 
51
- def retrieveCategoriesData(self)->None:
52
- """Scans through the sheet with the metadata for all the question categories
53
-
54
- The information that will be shown in the UI like description and points is retrieved from one spreadsheet sheet.
55
- This method gathers this information and stores it in the ``categoriesMetaData`` dataframe
45
+ This method gathers this information and stores it in the
46
+ ``categoriesMetaData`` dataframe
47
+ It also reads the question data and stores it in ``self.categories = {}``
56
48
  """
57
-
58
49
  logger.info("Start Parsing the Excel Metadata Sheet\n")
59
- with open(self.spreadSheetPath, 'rb') as f:
50
+ with Path(sheet).open("rb") as f:
60
51
  excelFile = pd.ExcelFile(f)
61
- self.categoriesMetaData = pd.read_excel(f, sheet_name="Kategorien",
62
- usecols=["Kategorie", "Beschreibung", "Punkte", "Version"],
63
- index_col=0)
52
+ self.categoriesMetaData = pd.read_excel(
53
+ f,
54
+ sheet_name="Kategorien",
55
+ usecols=["Kategorie", "Beschreibung", "Punkte", "Version"],
56
+ index_col=0,
57
+ )
64
58
  logger.info("Sucessfully read categoriesMetaData")
65
- print(self.categoriesMetaData)
66
59
  self.categories = {}
67
60
  for sh in excelFile.sheet_names:
68
61
  if sh.startswith("KAT"):
69
62
  n = int(sh[4:])
70
- katDf = pd.read_excel(f, sheet_name=str(sh), index_col=0, header=None)
63
+ katDf = pd.read_excel(
64
+ f,
65
+ sheet_name=str(sh),
66
+ index_col=0,
67
+ header=None,
68
+ )
71
69
  if not katDf.empty:
72
- p = self.categoriesMetaData["Punkte"].iloc[n-1]
73
- points = (p if not pd.isna(p) else 1)
74
- v = self.categoriesMetaData["Version"].iloc[n-1]
75
- version = (v if not pd.isna(v) else 0)
76
- self.categories[sh] = Category(n,sh,
77
- self.categoriesMetaData["Beschreibung"].iloc[n-1],
78
- dataframe=katDf,
79
- points=points,
80
- version=version)
70
+ p = self.categoriesMetaData["Punkte"].iloc[n - 1]
71
+ points = p if not pd.isna(p) else 1
72
+ v = self.categoriesMetaData["Version"].iloc[n - 1]
73
+ version = v if not pd.isna(v) else 0
74
+ self.categories[sh] = Category(
75
+ n,
76
+ sh,
77
+ self.categoriesMetaData["Beschreibung"].iloc[n - 1],
78
+ dataframe=katDf,
79
+ points=points,
80
+ version=version,
81
+ )
81
82
  self.dataChanged.signal.emit("whoo")
82
- return None
83
83
 
84
-
85
- def parseAll(self):
84
+ def parseAll(self) -> None:
86
85
  self.mainTree = ET.Element("quiz")
87
86
  for c in self.categories.values():
88
87
  validator = Validator(c)
@@ -95,50 +94,67 @@ class QuestionDB():
95
94
  try:
96
95
  check = validator.validate()
97
96
  except InvalidFieldException as e:
98
- logger.error(f"Question {c.id}{q:02d} is invalid.", exc_info=e)
99
- if check:
100
- c.questions[q]=validator.question
97
+ logger.exception(
98
+ f"Question {c.id}{q:02d} is invalid.",
99
+ exc_info=e,
100
+ )
101
+ if check:
102
+ c.questions[q] = validator.question
101
103
  try:
102
104
  c.parseQ(c.questions[q], validator.qdata)
103
105
  except QNotParsedException as e:
104
- logger.error(f"Frage {c.questions[q].id} konnte nicht erstellt werden", exc_info=e)
105
-
106
- def appendQuestions(self, questions:list[QuestionItem], file:Path|None=None):
106
+ logger.exception(
107
+ f"Frage {
108
+ c.questions[q].id
109
+ } konnte nicht erstellt werden",
110
+ exc_info=e,
111
+ )
112
+
113
+ def appendQuestions(
114
+ self, questions: list[QuestionItem], file: Path | None = None
115
+ ) -> None:
116
+ """Append selected question Elements to the tree."""
107
117
  tree = ET.Element("quiz")
108
- catdict:dict[Category,list[Question]]={}
118
+ catdict: dict[Category, list[Question]] = {}
109
119
  for q in questions:
110
120
  logger.debug(f"got a question to append {q=}")
111
121
  cat = q.parent().getCategory()
112
122
  if cat not in catdict:
113
123
  catdict[cat] = []
114
- print(f"Category is parent of Q {cat=}")
115
124
  catdict[cat].append(q.getQuestion())
116
125
  for cat, qlist in catdict.items():
117
- print(f"{cat = }, mit fragen {qlist = }")
118
- self.appendQElements(cat, qlist, tree=tree, includeHeader=self.settings.value("testGen/includeCats"))
126
+ self.appendQElements(
127
+ cat,
128
+ qlist,
129
+ tree=tree,
130
+ includeHeader=self.settings.get(SettingsKey.INCLUDEINCATS),
131
+ )
119
132
  stringHelpers.printDom(tree, file=file)
120
133
 
121
- def appendQElements(self,cat:Category, qList:list[Question], tree:ET.Element, includeHeader:bool=True)->None:
134
+ def appendQElements(
135
+ self,
136
+ cat: Category,
137
+ qList: list[Question],
138
+ tree: ET.Element,
139
+ includeHeader: bool = True,
140
+ ) -> None:
122
141
  if includeHeader:
123
- tree.append( cat.getCategoryHeader())
142
+ tree.append(cat.getCategoryHeader())
124
143
  logger.debug(f"Appended a new category item {cat=}")
125
- sameVariant = False
126
- variant = 1
144
+ variant: int = self.settings.get(SettingsKey.QUESTIONVARIANT)
127
145
  for q in qList:
128
146
  if cat.parseQ(q):
129
147
  if q.variants is not None:
130
- if sameVariant is False:
148
+ if variant == 0 or variant > q.variants:
131
149
  dialog = QuestionVariantDialog(self.window, q)
132
150
  if dialog.exec() == QtWidgets.QDialog.Accepted:
133
151
  variant = dialog.variant
134
- sameVariant = dialog.categoryWide
135
152
  logger.debug(f"Die Fragen-Variante {variant} wurde gewählt")
136
153
  q.assemble(variant)
137
- else: print("skipping this question")
154
+ else:
155
+ pass
138
156
  else:
139
157
  q.assemble()
140
158
  tree.append(q.element)
141
- else: logger.warning(f"Frage {q} wurde nicht erstellt")
142
- return None
143
-
144
-
159
+ else:
160
+ logger.warning(f"Frage {q} wurde nicht erstellt")
@@ -1,62 +1,62 @@
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
  """
5
5
 
6
6
  import lxml.etree as ET
7
- from .globals import DFIndex, XMLTags
8
- import pandas as pd
9
- from excel2moodle.core.globals import feedbackStr, TextElements, feedBElements
7
+
10
8
  import excel2moodle.core.etHelpers as eth
9
+ from excel2moodle.core.globals import TextElements, feedbackStr, feedBElements
10
+
11
+ from .globals import DFIndex, XMLTags
12
+
13
+
14
+ def getElement(eleName: str, text: str, **attribs) -> ET.Element:
15
+ """Creates an XML-Element with text.
11
16
 
12
- def getElement( eleName : str, text: str, **attribs)->ET.Element:
13
- """Creates an XML-Element with text
14
-
15
17
  If ``type(text)``is a ``QuestionFields``, the specific field is directly read.
16
18
  Otherwise it will include whatever is ``text`` as a string
17
19
  :param **kwargs: are treated as attributes for the Element
18
- raises:
20
+ raises:
19
21
  NanException if the spreadsheet cell of text:QuestionFields is ``nan``
20
22
  """
21
-
22
23
  toEle = ET.Element(eleName)
23
24
  toEle.text = str(text)
24
25
  for k, v in attribs.items():
25
- toEle.set(k,v)
26
+ toEle.set(k, v)
26
27
  return toEle
27
28
 
28
29
 
29
- def getTextElement( eleName:str, text:str|DFIndex, **attribs)->ET.Element:
30
- """Creates two nested elements: ``eleName`` with child ``text`` which holds the text"""
31
-
30
+ def getTextElement(eleName: str, text: str | DFIndex, **attribs) -> ET.Element:
31
+ """Creates two nested elements: ``eleName`` with child ``text`` which holds the text."""
32
32
  toEle = ET.Element(eleName, **attribs)
33
33
  child = getElement("text", text)
34
34
  toEle.append(child)
35
35
  return toEle
36
36
 
37
- def getCdatTxtElement(subEle: ET._Element|list[ET._Element])->ET.Element:
38
- """Puts all ``subEle`` as ``str`` into a ``<text><![CDATA[...subEle...]]</text>`` element"""
39
37
 
38
+ def getCdatTxtElement(subEle: ET._Element | list[ET._Element]) -> ET.Element:
39
+ """Puts all ``subEle`` as ``str`` into a ``<text><![CDATA[...subEle...]]</text>`` element."""
40
40
  textEle = ET.Element(XMLTags.TEXT)
41
41
  if isinstance(subEle, list):
42
- elementString : list= []
42
+ elementString: list = []
43
43
  for i in subEle:
44
44
  elementString.append(ET.tostring(i, encoding="unicode", pretty_print=True))
45
45
  textEle.text = ET.CDATA("".join(elementString))
46
46
  return textEle
47
- else:
48
- textEle.text = ET.CDATA(ET.tostring(subEle, encoding="unicode", pretty_print=True))
49
- return textEle
47
+ textEle.text = ET.CDATA(
48
+ ET.tostring(subEle, encoding="unicode", pretty_print=True),
49
+ )
50
+ return textEle
50
51
 
51
52
 
52
- def getFeedBEle(feedback:XMLTags,
53
- text:str|None=None, style: TextElements | None = None
54
- )->ET.Element:
53
+ def getFeedBEle(
54
+ feedback: XMLTags,
55
+ text: str | None = None,
56
+ style: TextElements | None = None,
57
+ ) -> ET.Element:
55
58
  """Gets ET Elements with the feedback for the question."""
56
- if style is None:
57
- span = feedBElements[feedback]
58
- else:
59
- span = style.create()
59
+ span = feedBElements[feedback] if style is None else style.create()
60
60
  if text is None:
61
61
  text = feedbackStr[feedback]
62
62
  ele = ET.Element(feedback, format="html")