excel2moodle 0.4.1__py3-none-any.whl → 0.4.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.
Files changed (34) hide show
  1. excel2moodle/__init__.py +0 -7
  2. excel2moodle/__main__.py +2 -2
  3. excel2moodle/core/__init__.py +0 -10
  4. excel2moodle/core/category.py +4 -3
  5. excel2moodle/core/dataStructure.py +116 -61
  6. excel2moodle/core/etHelpers.py +2 -2
  7. excel2moodle/core/exceptions.py +2 -2
  8. excel2moodle/core/globals.py +10 -27
  9. excel2moodle/core/parser.py +24 -30
  10. excel2moodle/core/question.py +147 -63
  11. excel2moodle/core/settings.py +107 -111
  12. excel2moodle/core/validator.py +36 -55
  13. excel2moodle/logger.py +7 -4
  14. excel2moodle/question_types/__init__.py +2 -0
  15. excel2moodle/question_types/cloze.py +207 -0
  16. excel2moodle/question_types/mc.py +26 -16
  17. excel2moodle/question_types/nf.py +17 -3
  18. excel2moodle/question_types/nfm.py +60 -17
  19. excel2moodle/ui/{windowEquationChecker.py → UI_equationChecker.py} +98 -78
  20. excel2moodle/ui/{exportSettingsDialog.py → UI_exportSettingsDialog.py} +55 -4
  21. excel2moodle/ui/{windowMain.py → UI_mainWindow.py} +32 -39
  22. excel2moodle/ui/appUi.py +66 -86
  23. excel2moodle/ui/dialogs.py +40 -2
  24. excel2moodle/ui/equationChecker.py +70 -0
  25. excel2moodle/ui/treewidget.py +4 -4
  26. {excel2moodle-0.4.1.dist-info → excel2moodle-0.4.3.dist-info}/METADATA +2 -3
  27. excel2moodle-0.4.3.dist-info/RECORD +38 -0
  28. {excel2moodle-0.4.1.dist-info → excel2moodle-0.4.3.dist-info}/entry_points.txt +0 -3
  29. excel2moodle/ui/questionPreviewDialog.py +0 -115
  30. excel2moodle-0.4.1.dist-info/RECORD +0 -37
  31. /excel2moodle/ui/{variantDialog.py → UI_variantDialog.py} +0 -0
  32. {excel2moodle-0.4.1.dist-info → excel2moodle-0.4.3.dist-info}/WHEEL +0 -0
  33. {excel2moodle-0.4.1.dist-info → excel2moodle-0.4.3.dist-info}/licenses/LICENSE +0 -0
  34. {excel2moodle-0.4.1.dist-info → excel2moodle-0.4.3.dist-info}/top_level.txt +0 -0
excel2moodle/__init__.py CHANGED
@@ -48,11 +48,4 @@ if __package__ is not None:
48
48
  }
49
49
 
50
50
 
51
- isMain: bool = False
52
-
53
51
  mainLogger = logging.getLogger(__name__)
54
-
55
-
56
- def isMainState() -> bool:
57
- mainLogger.debug("Returning mainState %s", isMain)
58
- return isMain
excel2moodle/__main__.py CHANGED
@@ -9,7 +9,7 @@ from PySide6 import QtWidgets, sys
9
9
  import excel2moodle
10
10
  from excel2moodle import e2mMetadata, mainLogger
11
11
  from excel2moodle.core import dataStructure
12
- from excel2moodle.core.settings import Settings, SettingsKey
12
+ from excel2moodle.core.settings import Settings, Tags
13
13
  from excel2moodle.logger import loggerConfig
14
14
  from excel2moodle.ui import appUi as ui
15
15
 
@@ -17,7 +17,7 @@ from excel2moodle.ui import appUi as ui
17
17
  def main() -> None:
18
18
  excel2moodle.isMain = True
19
19
  settings = Settings()
20
- logfile = Path(settings.get(SettingsKey.LOGFILE)).resolve()
20
+ logfile = Path(settings.get(Tags.LOGFILE)).resolve()
21
21
  e2mMetadata["logfile"] = logfile
22
22
  if logfile.exists() and logfile.is_file():
23
23
  logfile.replace(f"{logfile}.old")
@@ -1,11 +1 @@
1
1
  """These Modules are the heart of the excel2moodle Package."""
2
-
3
- # from pandas._config import config
4
- # from excel2moodle.core import questionWriter
5
- # from excel2moodle.core import stringHelpers
6
- # from excel2moodle.core import numericMultiQ
7
- # from excel2moodle.core import globals
8
- # from PySide6.QtCore import QObject, Signal
9
- # import logging as logging
10
- # from logging import config as logConfig
11
- #
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import re
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  import lxml.etree as ET
@@ -15,7 +16,6 @@ class Category:
15
16
 
16
17
  def __init__(
17
18
  self,
18
- n: int,
19
19
  name: str,
20
20
  description: str,
21
21
  dataframe: pd.DataFrame,
@@ -23,8 +23,9 @@ class Category:
23
23
  version: int = 0,
24
24
  ) -> None:
25
25
  """Instantiate a new Category object."""
26
- self.n = n
27
26
  self.NAME = name
27
+ match = re.search(r"\d+$", self.NAME)
28
+ self.n: int = int(match.group(0)) if match else 99
28
29
  self.desc = str(description)
29
30
  self.dataframe: pd.DataFrame = dataframe
30
31
  self.points = points
@@ -56,6 +57,6 @@ class Category:
56
57
  info = ET.SubElement(header, "info", format="html")
57
58
  ET.SubElement(cat, "text").text = f"$module$/top/{self.NAME}"
58
59
  ET.SubElement(info, "text").text = str(self.desc)
59
- ET.SubElement(header, "idnumber").text = str(self.n)
60
+ ET.SubElement(header, "idnumber").text = self.id
60
61
  ET.indent(header)
61
62
  return header
@@ -4,6 +4,7 @@ At the heart is the class ``xmlTest``
4
4
  """
5
5
 
6
6
  import logging
7
+ import sys
7
8
  from concurrent.futures import ProcessPoolExecutor, as_completed
8
9
  from pathlib import Path
9
10
  from typing import TYPE_CHECKING
@@ -16,12 +17,13 @@ from PySide6.QtCore import QObject, Signal
16
17
  from excel2moodle.core import stringHelpers
17
18
  from excel2moodle.core.category import Category
18
19
  from excel2moodle.core.exceptions import InvalidFieldException, QNotParsedException
19
- from excel2moodle.core.globals import DFIndex
20
+ from excel2moodle.core.globals import Tags
20
21
  from excel2moodle.core.question import Question
21
- from excel2moodle.core.settings import Settings, SettingsKey
22
+ from excel2moodle.core.settings import Settings
22
23
  from excel2moodle.core.validator import Validator
23
24
  from excel2moodle.logger import LogAdapterQuestionID
24
25
  from excel2moodle.question_types import QuestionTypeMapping
26
+ from excel2moodle.question_types.cloze import ClozeQuestion, ClozeQuestionParser
25
27
  from excel2moodle.question_types.mc import MCQuestion, MCQuestionParser
26
28
  from excel2moodle.question_types.nf import NFQuestion, NFQuestionParser
27
29
  from excel2moodle.question_types.nfm import NFMQuestion, NFMQuestionParser
@@ -49,6 +51,7 @@ def processSheet(sheetPath: str, categoryName: str) -> pd.DataFrame:
49
51
  sheet_name=str(categoryName),
50
52
  index_col=0,
51
53
  header=None,
54
+ engine="calamine",
52
55
  )
53
56
 
54
57
 
@@ -64,6 +67,7 @@ class QuestionDB:
64
67
  nfParser: NFQuestionParser = NFQuestionParser()
65
68
  nfmParser: NFMQuestionParser = NFMQuestionParser()
66
69
  mcParser: MCQuestionParser = MCQuestionParser()
70
+ clozeParser: ClozeQuestionParser = ClozeQuestionParser()
67
71
 
68
72
  def __init__(self, settings: Settings) -> None:
69
73
  self.settings = settings
@@ -72,7 +76,18 @@ class QuestionDB:
72
76
  self.categoriesMetaData: pd.DataFrame
73
77
  self.categories: dict[str, Category]
74
78
 
75
- def readCategoriesMetadata(self, sheetPath: Path) -> None:
79
+ @property
80
+ def spreadsheet(self) -> Path:
81
+ return self._spreadsheet
82
+
83
+ @spreadsheet.setter
84
+ def spreadsheet(self, sheet) -> None:
85
+ self.settings.clear()
86
+ self._spreadsheet = sheet
87
+ self.settings.set(Tags.SPREADSHEETPATH, sheet)
88
+ logger.info("saved new spreadsheet %s", sheet)
89
+
90
+ def readCategoriesMetadata(self, sheetPath: Path | None = None) -> pd.DataFrame:
76
91
  """Read the metadata and questions from the spreadsheet.
77
92
 
78
93
  Get the category data from the spreadsheet and stores it in the
@@ -80,31 +95,56 @@ class QuestionDB:
80
95
  Setup the categories and store them in ``self.categories = {}``
81
96
  Pass the question data to the categories.
82
97
  """
98
+ sheetPath = sheetPath if sheetPath else self.spreadsheet
83
99
  logger.info("Start Parsing the Excel Metadata Sheet\n")
84
100
  with Path(sheetPath).open("rb") as f:
85
101
  settingDf = pd.read_excel(
86
102
  f,
87
103
  sheet_name="settings",
88
104
  index_col=0,
105
+ engine="calamine",
89
106
  )
90
107
  logger.debug("Found the settings: \n\t%s", settingDf)
91
- self._setProjectSettings(settingDf)
108
+ self._setProjectSettings(settingDf, mainPath=sheetPath.parent)
92
109
  with Path(sheetPath).open("rb") as f:
93
110
  self.categoriesMetaData = pd.read_excel(
94
111
  f,
95
- sheet_name=self.settings.get(SettingsKey.CATEGORIESSHEET),
112
+ sheet_name=self.settings.get(Tags.CATEGORIESSHEET),
96
113
  index_col=0,
114
+ engine="calamine",
97
115
  )
98
116
  logger.info(
99
117
  "Sucessfully read categoriesMetaData \n %s", self.categoriesMetaData
100
118
  )
119
+ return self.categoriesMetaData
101
120
 
102
- def _setProjectSettings(self, settings: pd.DataFrame) -> None:
121
+ def _setProjectSettings(
122
+ self, settings: pd.DataFrame, mainPath: Path | None = None
123
+ ) -> None:
124
+ settings = self.harmonizeDFIndex(settings)
103
125
  for tag, value in settings.iterrows():
104
- self.settings.set(tag, value.iloc[0], local=True)
126
+ value = value.iloc[0]
127
+ if tag == Tags.TOLERANCE:
128
+ tol = value if value <= 1 else value / 100
129
+ self.settings.set(Tags.TOLERANCE, tol)
130
+ else:
131
+ self.settings.set(tag, value)
132
+ if Tags.IMPORTMODULE in settings.index:
133
+ logger.warning(
134
+ "Appending: %s to sys.path. All names defined by it will be usable",
135
+ mainPath,
136
+ )
137
+ sys.path.append(str(mainPath))
138
+ imgFolder = self.settings.get(Tags.SPREADSHEETPATH).parent / self.settings.get(
139
+ Tags.PICTURESUBFOLDER
140
+ )
141
+ if Tags.PICTURESUBFOLDER not in settings.index:
142
+ logger.warning("You didn't specify an image Folder. This may cause errors.")
143
+ self.settings.set(Tags.PICTUREFOLDER, imgFolder.resolve())
105
144
 
106
- def initAllCategories(self, sheetPath: Path) -> None:
145
+ def initAllCategories(self, sheetPath: Path | None = None) -> None:
107
146
  """Read all category sheets and initialize all Categories."""
147
+ sheetPath = sheetPath if sheetPath else self.spreadsheet
108
148
  if not hasattr(self, "categoriesMetaData"):
109
149
  logger.error("Can't process the Categories without Metadata")
110
150
  return
@@ -112,19 +152,19 @@ class QuestionDB:
112
152
  self.categories.clear()
113
153
  else:
114
154
  self.categories: dict[str, Category] = {}
115
- with Path(sheetPath).open("rb") as f:
116
- excelFile = pd.ExcelFile(f)
155
+ with pd.ExcelFile(sheetPath, engine="calamine") as excelFile:
117
156
  for categoryName in excelFile.sheet_names:
118
157
  logger.debug("Starting to read category %s", categoryName)
119
- if categoryName.startswith("KAT"):
120
- self.initCategory(sheetPath, categoryName)
158
+ if categoryName in self.categoriesMetaData.index:
159
+ self.initCategory(categoryName, sheetPath=sheetPath)
121
160
 
122
- def asyncInitAllCategories(self, sheetPath: Path) -> None:
161
+ def asyncInitAllCategories(self, sheetPath: Path | None = None) -> None:
123
162
  """Read all category sheets asynchron and initialize all Categories.
124
163
 
125
164
  It does the same as `initAllCategories` but the parsing of the excelfile
126
165
  is done asynchron via `concurrent.futures.ProcessPoolExecutor`
127
166
  """
167
+ sheetPath = sheetPath if sheetPath else self.spreadsheet
128
168
  if not hasattr(self, "categoriesMetaData"):
129
169
  logger.error("Can't process the Categories without Metadata")
130
170
  return
@@ -132,71 +172,86 @@ class QuestionDB:
132
172
  self.categories.clear()
133
173
  else:
134
174
  self.categories: dict[str, Category] = {}
135
- sheet_names = []
136
- with Path(sheetPath).open("rb") as f:
137
- excel_file = pd.ExcelFile(f)
138
- sheet_names = [
139
- name for name in excel_file.sheet_names if name.startswith("KAT_")
175
+ sheetNames = []
176
+ with pd.ExcelFile(sheetPath, engine="calamine") as excelFile:
177
+ sheetNames = [
178
+ name
179
+ for name in excelFile.sheet_names
180
+ if name in self.categoriesMetaData.index
140
181
  ]
141
- logger.debug("found those caetegory sheets: \n %s ", sheet_names)
182
+ logger.debug("found those category sheets: \n %s ", sheetNames)
142
183
  with ProcessPoolExecutor() as executor:
143
184
  futures = {
144
185
  executor.submit(processSheet, str(sheetPath), sheet): sheet
145
- for sheet in sheet_names
186
+ for sheet in sheetNames
146
187
  }
147
188
  for future in as_completed(futures):
148
189
  categoryName = futures[future]
149
190
  try:
150
191
  categoryDataF = future.result()
151
- categoryNumber = int(categoryName[4:])
152
- self._setupCategory(categoryDataF, categoryName, categoryNumber)
192
+ self._setupCategory(categoryDataF, categoryName)
153
193
  logger.debug("Finished processing %s", categoryName)
154
194
  except Exception as e:
155
195
  logger.exception("Error processing sheet %s: %s", categoryName, e)
156
196
  logger.debug("Future exception: %s", future.exception())
157
197
 
158
- def initCategory(self, sheetPath: Path, categoryName: str) -> None:
159
- """Read `categoryName` from the file ``sheetPath`` and initialize the category."""
160
- categoryNumber = int(categoryName[4:])
198
+ def initCategory(
199
+ self, categoryName: str, sheetPath: Path | None = None
200
+ ) -> bool | Category:
201
+ """Read `categoryName` from the ``sheetPath`` and initialize the category.
202
+ Returns the Category and appends it to `self.categories`.
203
+ """
204
+ sheetPath = sheetPath if sheetPath else self.spreadsheet
161
205
  katDf = pd.read_excel(
162
206
  sheetPath,
163
207
  sheet_name=str(categoryName),
164
208
  index_col=0,
165
209
  header=None,
210
+ engine="calamine",
166
211
  )
167
212
  if not katDf.empty:
168
213
  logger.debug("Sucessfully read the Dataframe for cat %s", categoryName)
169
- self._setupCategory(katDf, categoryName, categoryNumber)
170
-
171
- def _setupCategory(
172
- self, categoryDf: pd.DataFrame, categoryName: str, categoryNumber: int
173
- ) -> None:
214
+ return self._setupCategory(katDf, categoryName)
215
+ return False
216
+
217
+ @staticmethod
218
+ def harmonizeDFIndex(dataframe: pd.DataFrame) -> pd.DataFrame:
219
+ """Convert the index strings to lowercase without whitespace."""
220
+ index = dataframe.index.str.lower()
221
+ harmonizedIdx = ["".join(i.split()) if pd.notna(i) else i for i in index]
222
+ dataframe.index = pd.Index(harmonizedIdx)
223
+ return dataframe
224
+
225
+ def _setupCategory(self, categoryDf: pd.DataFrame, categoryName: str) -> Category:
174
226
  """Setup the category from the ``dataframe``.
227
+
175
228
  :emits: categoryReady(self) Signal.
176
- """ # noqa: D401
229
+ """
230
+ categoryDf = self.harmonizeDFIndex(categoryDf)
177
231
  points = (
178
- self.categoriesMetaData["points"].iloc[categoryNumber - 1]
232
+ self.categoriesMetaData["points"].loc[categoryName]
179
233
  if "points" in self.categoriesMetaData
180
- and not pd.isna(self.categoriesMetaData["points"]).iloc[categoryNumber - 1]
181
- else self.settings.get(SettingsKey.POINTS)
234
+ and not pd.isna(self.categoriesMetaData["points"]).loc[categoryName]
235
+ else self.settings.get(Tags.POINTS)
182
236
  )
183
237
  version = (
184
- self.categoriesMetaData["version"].iloc[categoryNumber - 1]
238
+ self.categoriesMetaData["version"].loc[categoryName]
185
239
  if "version" in self.categoriesMetaData
186
- and not pd.isna(self.categoriesMetaData["version"].iloc[categoryNumber - 1])
187
- else self.settings.get(SettingsKey.VERSION)
240
+ and not pd.isna(self.categoriesMetaData["version"].loc[categoryName])
241
+ else self.settings.get(Tags.VERSION)
188
242
  )
189
243
  category = Category(
190
- categoryNumber,
191
244
  categoryName,
192
- self.categoriesMetaData["description"].iloc[categoryNumber - 1],
245
+ self.categoriesMetaData["description"].loc[categoryName],
193
246
  dataframe=categoryDf,
194
247
  points=points,
195
248
  version=version,
196
249
  )
197
- self.categories[categoryName] = category
250
+ if hasattr(self, "categories"):
251
+ self.categories[categoryName] = category
252
+ self.signals.categoryReady.emit(category)
198
253
  logger.debug("Category %s is initialized", categoryName)
199
- self.signals.categoryReady.emit(category)
254
+ return category
200
255
 
201
256
  def parseAllQuestions(self) -> None:
202
257
  """Parse all question from all categories.
@@ -214,13 +269,12 @@ class QuestionDB:
214
269
  for qNum in category.dataframe.columns:
215
270
  try:
216
271
  self.setupAndParseQuestion(category, qNum)
217
- except (InvalidFieldException, QNotParsedException, ValueError) as e:
272
+ except (InvalidFieldException, QNotParsedException):
218
273
  logger.exception(
219
274
  "Question %s%02d couldn't be parsed. The Question Data: \n %s",
220
275
  category.id,
221
276
  qNum,
222
277
  category.dataframe[qNum],
223
- exc_info=e,
224
278
  )
225
279
  self.signals.categoryQuestionsReady.emit(category)
226
280
 
@@ -255,30 +309,33 @@ class QuestionDB:
255
309
  raise QNotParsedException(msg, f"{category.id}{qNumber}")
256
310
  cls.validator.setup(qdat, qNumber)
257
311
  cls.validator.validate()
258
- validData = cls.validator.getQuestionRawData()
259
- qtype: str = str(validData.get(DFIndex.TYPE))
260
- category.questions[qNumber] = QuestionTypeMapping[qtype].create(
261
- category, validData
262
- )
263
- question = category.questions[qNumber]
264
- if question.element is not None:
312
+ validData = cls.validator.getQuestionData()
313
+ qtype: str = validData.get(Tags.TYPE)
314
+ logger.debug("Question type is: %s", qtype)
315
+ question = QuestionTypeMapping[qtype].create(category, validData)
316
+ if question.isParsed:
265
317
  locallogger.info("Question already parsed")
266
318
  return
267
319
  if isinstance(question, NFQuestion):
268
320
  cls.nfParser.setup(question)
269
- cls.nfParser.parse()
270
321
  locallogger.debug("setup a new NF parser ")
322
+ cls.nfParser.parse()
271
323
  elif isinstance(question, MCQuestion):
272
324
  cls.mcParser.setup(question)
273
- cls.mcParser.parse()
274
325
  locallogger.debug("setup a new MC parser ")
326
+ cls.mcParser.parse()
275
327
  elif isinstance(question, NFMQuestion):
276
328
  cls.nfmParser.setup(question)
277
- cls.nfmParser.parse()
278
329
  locallogger.debug("setup a new NFM parser ")
330
+ cls.nfmParser.parse()
331
+ elif isinstance(question, ClozeQuestion):
332
+ cls.clozeParser.setup(question)
333
+ locallogger.debug("setup a new CLOZE parser")
334
+ cls.clozeParser.parse()
279
335
  else:
280
336
  msg = "couldn't setup Parser"
281
337
  raise QNotParsedException(msg, question.id)
338
+ category.questions[qNumber] = question
282
339
 
283
340
  def appendQuestions(
284
341
  self, questions: list[QuestionItem], file: Path | None = None
@@ -293,15 +350,15 @@ class QuestionDB:
293
350
  catdict[cat] = []
294
351
  catdict[cat].append(q.getQuestion())
295
352
  for cat, qlist in catdict.items():
296
- self.appendQElements(
353
+ self._appendQElements(
297
354
  cat,
298
355
  qlist,
299
356
  tree=tree,
300
- includeHeader=self.settings.get(SettingsKey.INCLUDEINCATS),
357
+ includeHeader=self.settings.get(Tags.INCLUDEINCATS),
301
358
  )
302
359
  stringHelpers.printDom(tree, file=file)
303
360
 
304
- def appendQElements(
361
+ def _appendQElements(
305
362
  self,
306
363
  cat: Category,
307
364
  qList: list[Question],
@@ -311,9 +368,9 @@ class QuestionDB:
311
368
  if includeHeader:
312
369
  tree.append(cat.getCategoryHeader())
313
370
  logger.debug(f"Appended a new category item {cat=}")
314
- variant: int = self.settings.get(SettingsKey.QUESTIONVARIANT)
371
+ variant: int = self.settings.get(Tags.QUESTIONVARIANT)
315
372
  for q in qList:
316
- if q.variants is not None:
373
+ if hasattr(q, "variants") and q.variants is not None:
317
374
  if variant == 0 or variant > q.variants:
318
375
  dialog = QuestionVariantDialog(self.window, q)
319
376
  if dialog.exec() == QtWidgets.QDialog.Accepted:
@@ -321,7 +378,5 @@ class QuestionDB:
321
378
  logger.debug("Die Fragen-Variante %s wurde gewählt", variant)
322
379
  else:
323
380
  logger.warning("Keine Fragenvariante wurde gewählt.")
324
- q.assemble(variant)
325
- else:
326
- q.assemble()
381
+ q.assemble(variant=variant)
327
382
  tree.append(q.element)
@@ -8,7 +8,7 @@ import lxml.etree as ET
8
8
  import excel2moodle.core.etHelpers as eth
9
9
  from excel2moodle.core.globals import TextElements, feedbackStr, feedBElements
10
10
 
11
- from .globals import DFIndex, XMLTags
11
+ from .globals import Tags, XMLTags
12
12
 
13
13
 
14
14
  def getElement(eleName: str, text: str, **attribs) -> ET.Element:
@@ -27,7 +27,7 @@ def getElement(eleName: str, text: str, **attribs) -> ET.Element:
27
27
  return toEle
28
28
 
29
29
 
30
- def getTextElement(eleName: str, text: str | DFIndex, **attribs) -> ET.Element:
30
+ def getTextElement(eleName: str, text: str | Tags, **attribs) -> ET.Element:
31
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)
@@ -1,4 +1,4 @@
1
- from excel2moodle.core.globals import DFIndex
1
+ from excel2moodle.core.globals import Tags
2
2
 
3
3
 
4
4
  class QNotParsedException(Exception):
@@ -18,7 +18,7 @@ class InvalidFieldException(Exception):
18
18
  self,
19
19
  message: str,
20
20
  qID: str,
21
- field: DFIndex | list[DFIndex],
21
+ field: Tags | list[Tags],
22
22
  *args: object,
23
23
  **kwargs,
24
24
  ) -> None:
@@ -2,33 +2,16 @@ from enum import Enum, StrEnum
2
2
 
3
3
  import lxml.etree as ET
4
4
 
5
- questionTypes = {
5
+ from excel2moodle.core.settings import Tags
6
+
7
+ QUESTION_TYPES = {
6
8
  "NF": "numerical",
7
9
  "NFM": "numerical",
8
10
  "MC": "multichoice",
11
+ "CLOZE": "cloze",
9
12
  }
10
13
 
11
14
 
12
- class DFIndex(StrEnum):
13
- """The identifier string for for the spreadsheet and the string for the xml-tag.
14
-
15
- Each enum corresponds to a list of two values.
16
- The first Value is the index in the spreadsheet, the second is the name of the xml-tag
17
- """
18
-
19
- TEXT = "text"
20
- BPOINTS = "bulletPoint"
21
- TRUE = "true"
22
- FALSE = "false"
23
- TYPE = "type"
24
- NAME = "name"
25
- RESULT = "result"
26
- PICTURE = "picture"
27
- NUMBER = "number"
28
- ANSTYPE = "answerType"
29
- TOLERANCE = "tolerance"
30
-
31
-
32
15
  class TextElements(Enum):
33
16
  PLEFT = "p", "text-align: left;"
34
17
  SPANRED = "span", "color: rgb(239, 69, 64)"
@@ -58,15 +41,15 @@ class TextElements(Enum):
58
41
 
59
42
 
60
43
  class XMLTags(StrEnum):
61
- def __new__(cls, value: str, dfkey: DFIndex | None = None):
44
+ def __new__(cls, value: str, dfkey: Tags | None = None):
62
45
  obj = str.__new__(cls, value)
63
46
  obj._value_ = value
64
47
  if dfkey is not None:
65
48
  obj._dfkey_ = dfkey
66
49
  return obj
67
50
 
68
- def __init__(self, _: str, dfkey: DFIndex | None = None, getEle=None) -> None:
69
- if isinstance(dfkey, DFIndex):
51
+ def __init__(self, _: str, dfkey: Tags | None = None, getEle=None) -> None:
52
+ if isinstance(dfkey, Tags):
70
53
  self._dfkey_: str = dfkey
71
54
  if getEle:
72
55
  self._getEle_: object = getEle
@@ -85,11 +68,11 @@ class XMLTags(StrEnum):
85
68
  msg.append(f"Df Key {self.dfkey=}")
86
69
  return "\n".join(msg)
87
70
 
88
- NAME = "name", DFIndex.NAME
89
- QTEXT = "questiontext", DFIndex.TEXT
71
+ NAME = "name", Tags.NAME
72
+ QTEXT = "questiontext", Tags.TEXT
90
73
  QUESTION = "question"
91
74
  TEXT = "text"
92
- PICTURE = "file", DFIndex.PICTURE
75
+ PICTURE = "file", Tags.PICTURE
93
76
  GENFEEDB = "generalfeedback"
94
77
  CORFEEDB = "correctfeedback"
95
78
  PCORFEEDB = "partialcorrectfeedback"