excel2moodle 0.3.5__py3-none-any.whl → 0.3.7__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.
@@ -0,0 +1,30 @@
1
+ """Numerical question implementation."""
2
+
3
+ import lxml.etree as ET
4
+
5
+ from excel2moodle.core.globals import (
6
+ DFIndex,
7
+ XMLTags,
8
+ )
9
+ from excel2moodle.core.parser import QuestionParser
10
+ from excel2moodle.core.question import Question
11
+
12
+
13
+ class NFQuestion(Question):
14
+ def __init__(self, *args, **kwargs) -> None:
15
+ super().__init__(*args, **kwargs)
16
+
17
+
18
+ class NFQuestionParser(QuestionParser):
19
+ """Subclass for parsing numeric questions."""
20
+
21
+ def __init__(self) -> None:
22
+ super().__init__()
23
+ self.genFeedbacks = [XMLTags.GENFEEDB]
24
+
25
+ def setAnswers(self) -> list[ET.Element]:
26
+ result = self.rawInput[DFIndex.RESULT]
27
+ ansEle: list[ET.Element] = []
28
+ tol = self.rawInput[DFIndex.TOLERANCE]
29
+ ansEle.append(self.getNumericAnsElement(result=result, tolerance=tol))
30
+ return ansEle
@@ -0,0 +1,92 @@
1
+ """Numerical question multi implementation."""
2
+
3
+ import re
4
+ from typing import TYPE_CHECKING
5
+
6
+ import lxml.etree as ET
7
+ from asteval import Interpreter
8
+
9
+ from excel2moodle.core import stringHelpers
10
+ from excel2moodle.core.globals import (
11
+ DFIndex,
12
+ XMLTags,
13
+ )
14
+ from excel2moodle.core.parser import QuestionParser
15
+ from excel2moodle.core.question import Question
16
+
17
+ if TYPE_CHECKING:
18
+ import lxml.etree as ET
19
+
20
+
21
+ class NFMQuestion(Question):
22
+ def __init__(self, *args, **kwargs) -> None:
23
+ super().__init__(*args, **kwargs)
24
+
25
+
26
+ class NFMQuestionParser(QuestionParser):
27
+ def __init__(self) -> None:
28
+ super().__init__()
29
+ self.genFeedbacks = [XMLTags.GENFEEDB]
30
+ self.astEval = Interpreter()
31
+
32
+ def setAnswers(self) -> None:
33
+ equation = self.rawInput[DFIndex.RESULT]
34
+ bps = str(self.rawInput[DFIndex.BPOINTS])
35
+ ansElementsList: list[ET.Element] = []
36
+ varNames: list[str] = self._getVarsList(bps)
37
+ self.question.variables, number = self._getVariablesDict(varNames)
38
+ for n in range(number):
39
+ self._setupAstIntprt(self.question.variables, n)
40
+ result = self.astEval(equation)
41
+ if isinstance(result, float):
42
+ tol = self.rawInput[DFIndex.TOLERANCE]
43
+ ansElementsList.append(
44
+ self.getNumericAnsElement(result=round(result, 3), tolerance=tol),
45
+ )
46
+ self.question.answerVariants = ansElementsList
47
+ self.setVariants(len(ansElementsList))
48
+
49
+ def setVariants(self, number: int) -> None:
50
+ self.question.variants = number
51
+ mvar = self.question.category.maxVariants
52
+ if mvar is None:
53
+ self.question.category.maxVariants = number
54
+ else:
55
+ self.question.category.maxVariants = min(number, mvar)
56
+
57
+ def _setupAstIntprt(self, var: dict[str, list[float | int]], index: int) -> None:
58
+ """Setup the asteval Interpreter with the variables."""
59
+ for name, value in var.items():
60
+ self.astEval.symtable[name] = value[index]
61
+
62
+ def _getVariablesDict(self, keyList: list) -> tuple[dict[str, list[float]], int]:
63
+ """Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]."""
64
+ dic: dict = {}
65
+ num: int = 0
66
+ for k in keyList:
67
+ val = self.rawInput[k]
68
+ if isinstance(val, str):
69
+ li = stringHelpers.getListFromStr(val)
70
+ num = len(li)
71
+ variables: list[float] = [float(i.replace(",", ".")) for i in li]
72
+ dic[str(k)] = variables
73
+ else:
74
+ dic[str(k)] = [str(val)]
75
+ num = 1
76
+ self.logger.debug("The following variables were provided: %s", dic)
77
+ return dic, num
78
+
79
+ @staticmethod
80
+ def _getVarsList(bps: str | list[str]) -> list:
81
+ """Durchsucht den bulletPoints String nach den Variablen ``{var}``.
82
+
83
+ It only finds variables after the ``=`` sign, to not catch LaTex.
84
+ """
85
+ varNames = []
86
+ regexFinder = re.compile(r"=\s*\{(\w+)\}")
87
+ if isinstance(bps, list):
88
+ for _p in bps:
89
+ varNames.extend(regexFinder.findall(str(_p)))
90
+ else:
91
+ varNames = regexFinder.findall(str(bps))
92
+ return varNames
excel2moodle/ui/appUi.py CHANGED
@@ -8,13 +8,13 @@ import logging
8
8
  from pathlib import Path
9
9
 
10
10
  from PySide6 import QtCore, QtWidgets
11
- from PySide6.QtCore import Qt
11
+ from PySide6.QtCore import QRunnable, Qt, QThreadPool
12
12
 
13
- from excel2moodle import qSignalLogger
14
-
15
- # from excel2moodle.logger import LogWindowHandler
13
+ from excel2moodle import mainLogger
14
+ from excel2moodle.core.category import Category
16
15
  from excel2moodle.core.dataStructure import QuestionDB
17
16
  from excel2moodle.extra import equationVerification as eqVerif
17
+ from excel2moodle.logger import LogWindowHandler
18
18
  from excel2moodle.ui import dialogs
19
19
  from excel2moodle.ui.settings import Settings, SettingsKey
20
20
  from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
@@ -24,6 +24,9 @@ from .windowEquationChecker import Ui_EquationChecker
24
24
 
25
25
  logger = logging.getLogger(__name__)
26
26
 
27
+ loggerSignal = LogWindowHandler()
28
+ mainLogger.addHandler(loggerSignal)
29
+
27
30
 
28
31
  class MainWindow(QtWidgets.QMainWindow):
29
32
  def __init__(self, settings: Settings, testDB: QuestionDB) -> None:
@@ -35,7 +38,8 @@ class MainWindow(QtWidgets.QMainWindow):
35
38
  self.testDB = testDB
36
39
  self.ui = Ui_MoodleTestGenerator()
37
40
  self.ui.setupUi(self)
38
-
41
+ self.connectEvents()
42
+ logger.info("Settings are stored under: %s", self.settings.fileName())
39
43
  self.ui.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
40
44
  self.ui.treeWidget.header().setSectionResizeMode(
41
45
  QtWidgets.QHeaderView.ResizeToContents,
@@ -43,33 +47,33 @@ class MainWindow(QtWidgets.QMainWindow):
43
47
  self.ui.checkBoxIncludeCategories.setChecked(
44
48
  self.settings.get(SettingsKey.INCLUDEINCATS),
45
49
  )
46
-
47
- self.ui.retranslateUi(self)
48
- logger.info("Settings are stored under: %s", self.settings.fileName())
50
+ self.ui.spinBoxDefaultQVariant.setValue(
51
+ self.settings.get(SettingsKey.QUESTIONVARIANT)
52
+ )
49
53
  self.ui.pointCounter.setReadOnly(True)
50
54
  self.ui.questionCounter.setReadOnly(True)
51
55
  self.setStatus(
52
- "Wählen Sie bitte eine Excel Tabelle und einen Export Ordner für die Fragen aus",
56
+ "Wählen Sie eine Excel Tabelle mit den Fragen aus",
53
57
  )
54
58
  try:
55
59
  self.resize(self.settings.value("windowSize"))
56
60
  self.move(self.settings.value("windowPosition"))
57
61
  except Exception:
58
62
  pass
59
- self.connectEvents()
63
+ self.threadPool = QThreadPool()
60
64
 
61
65
  def connectEvents(self) -> None:
62
- self.ui.treeWidget.itemClicked.connect(self.onSelectionChanged)
66
+ self.ui.treeWidget.itemSelectionChanged.connect(self.onSelectionChanged)
63
67
  self.ui.checkBoxQuestionListSelectAll.checkStateChanged.connect(
64
68
  self.toggleQuestionSelectionState,
65
69
  )
66
- qSignalLogger.emitter.signal.connect(self.updateLog)
70
+ loggerSignal.emitter.signal.connect(self.updateLog)
67
71
  self.ui.actionEquationChecker.triggered.connect(self.openEqCheckerDlg)
68
72
  self.ui.checkBoxIncludeCategories.checkStateChanged.connect(
69
73
  self.setIncludeCategoriesSetting,
70
74
  )
71
- self.ui.actionParseAll.triggered.connect(self.onParseAll)
72
- self.testDB.dataChanged.signal.connect(self.refreshList)
75
+ self.ui.actionParseAll.triggered.connect(self.parseSpreadsheetAll)
76
+ self.testDB.signals.categoryQuestionsReady.connect(self.treeRefreshCategory)
73
77
  self.ui.buttonSpreadSheet.clicked.connect(self.onButSpreadsheet)
74
78
  self.ui.buttonTestGen.clicked.connect(self.onButGenTest)
75
79
  self.ui.actionPreviewQ.triggered.connect(self.openPreviewQuestionDlg)
@@ -81,6 +85,18 @@ class MainWindow(QtWidgets.QMainWindow):
81
85
  def setQVariantDefault(self, value: int) -> None:
82
86
  self.settings.set(SettingsKey.QUESTIONVARIANT, value)
83
87
 
88
+ @QtCore.Slot()
89
+ def parseSpreadsheetAll(self) -> None:
90
+ """Event triggered by the *Tools/Parse all Questions* Event.
91
+
92
+ It parses all the Questions found in the spreadsheet
93
+ and then refreshes the list of questions.
94
+ If successful it prints out a list of all exported Questions
95
+ """
96
+ self.ui.treeWidget.clear()
97
+ process = ParseAllThread(self.testDB, self)
98
+ self.threadPool.start(process)
99
+
84
100
  @QtCore.Slot(Path)
85
101
  def onSheetPathChanged(self, sheet: Path) -> None:
86
102
  logger.debug("Slot, new Spreadsheet triggered")
@@ -90,9 +106,7 @@ class MainWindow(QtWidgets.QMainWindow):
90
106
  svgFolder.resolve()
91
107
  self.settings.set(SettingsKey.PICTUREFOLDER, svgFolder)
92
108
  self.ui.buttonSpreadSheet.setText(str(sheet.name))
93
- self.testDB.readSpreadsheetData(self.spreadSheetPath)
94
- self.testDB.parseAll()
95
- self.refreshList()
109
+ self.parseSpreadsheetAll()
96
110
 
97
111
  def updateLog(self, log) -> None:
98
112
  self.ui.loggerWindow.append(log)
@@ -116,8 +130,6 @@ class MainWindow(QtWidgets.QMainWindow):
116
130
  for q in selection:
117
131
  questions += 1
118
132
  count += q.getQuestion().points
119
-
120
- logger.info("%s questions are selected with %s points", questions, count)
121
133
  self.ui.pointCounter.setValue(count)
122
134
  self.ui.questionCounter.setValue(questions)
123
135
 
@@ -154,39 +166,22 @@ class MainWindow(QtWidgets.QMainWindow):
154
166
  filter=self.tr("Spreadsheet(*.xlsx *.ods)"),
155
167
  selectedFilter=("*.ods"),
156
168
  )
157
- self.excelPath = Path(file[0]).resolve()
158
- self.settings.setSpreadsheet(self.excelPath)
159
- logger.debug(f"Saved Spreadsheet Path: {self.excelPath}\n")
160
- self.setStatus("[OK] Excel Tabelle wurde eingelesen")
161
-
162
- @QtCore.Slot()
163
- def onParseAll(self) -> None:
164
- """Event triggered by the *Tools/Parse all Questions* Event.
165
-
166
- It parses all the Questions found in the spreadsheet
167
- and then refreshes the list of questions.
168
- If successful it prints out a list of all exported Questions
169
- """
170
- self.testDB.readSpreadsheetData(self.spreadSheetPath)
171
- self.testDB.parseAll()
172
- self.setStatus("[OK] Alle Fragen wurden erfolgreich in XML-Dateien umgewandelt")
173
- self.refreshList()
174
-
175
- def refreshList(self) -> None:
176
- """Refresh the question overview in the main window.
169
+ path = Path(file[0]).resolve(strict=True)
170
+ if path.is_file():
171
+ self.excelPath = path
172
+ self.settings.setSpreadsheet(self.excelPath)
173
+ self.setStatus("[OK] Excel Tabelle wurde eingelesen")
174
+ else:
175
+ self.setStatus("[ERROR] keine Tabelle angegeben")
177
176
 
178
- Enable the export Button afterwards.
179
- """
180
- logger.info("starting List refresh")
181
- cats = self.testDB.categories
182
- self.ui.treeWidget.clear()
183
- for cat in cats.values():
184
- catItem = CategoryItem(self.ui.treeWidget, cat)
185
- catItem.setFlags(catItem.flags() & ~Qt.ItemIsSelectable)
186
- for q in cat.questions.values():
187
- QuestionItem(catItem, q)
188
- self.setStatus("[OK] Fragen Liste wurde aktualisiert")
189
- self.ui.buttonTestGen.setEnabled(True)
177
+ @QtCore.Slot(Category)
178
+ def treeRefreshCategory(self, cat: Category) -> None:
179
+ """Append Category with its Questions to the treewidget."""
180
+ catItem = CategoryItem(self.ui.treeWidget, cat)
181
+ catItem.setFlags(catItem.flags() & ~Qt.ItemIsSelectable)
182
+ for q in cat.questions.values():
183
+ QuestionItem(catItem, q)
184
+ self.ui.treeWidget.sortItems(0, Qt.SortOrder.AscendingOrder)
190
185
 
191
186
  @QtCore.Slot()
192
187
  def openPreviewQuestionDlg(self) -> None:
@@ -214,6 +209,26 @@ class MainWindow(QtWidgets.QMainWindow):
214
209
  about.exec()
215
210
 
216
211
 
212
+ class ParseAllThread(QRunnable):
213
+ """Parse the whole Spreadsheet.
214
+ Start by reading the spreadsheet asynchron.
215
+ When finished parse all Categories subsequently.
216
+ """
217
+
218
+ def __init__(self, questionDB: QuestionDB, mainApp: MainWindow) -> None:
219
+ super().__init__()
220
+ self.testDB = questionDB
221
+ self.mainApp = mainApp
222
+
223
+ @QtCore.Slot()
224
+ def run(self) -> None:
225
+ self.testDB.readCategoriesMetadata(self.mainApp.spreadSheetPath)
226
+ self.testDB.asyncInitAllCategories(self.mainApp.spreadSheetPath)
227
+ self.mainApp.setStatus("[OK] Tabellen wurde eingelesen")
228
+ self.testDB.parseAllQuestions()
229
+ self.mainApp.ui.buttonTestGen.setEnabled(True)
230
+
231
+
217
232
  class EqCheckerWindow(QtWidgets.QWidget):
218
233
  def __init__(self) -> None:
219
234
  super().__init__()
@@ -81,21 +81,22 @@ class QuestinoPreviewDialog(QtWidgets.QDialog):
81
81
  def setAnswers(self) -> None:
82
82
  if self.question.qtype == "NFM":
83
83
  for i, ans in enumerate(self.question.answerVariants):
84
- a = ans.find("text").text
85
- text = QtWidgets.QLineEdit(a, self)
84
+ t = ans.find("text").text
85
+ text = QtWidgets.QLineEdit(t, self)
86
86
  self.ui.answersFormLayout.addRow(f"Answer {i + 1}", text)
87
87
 
88
88
  elif self.question.qtype == "NF":
89
89
  ans = self.question.element.find(XMLTags.ANSWER)
90
- a = ans.find("text").text
91
- text = QtWidgets.QLineEdit(a, self)
90
+ t = ans.find("text").text
91
+ text = QtWidgets.QLineEdit(t, self)
92
92
  self.ui.answersFormLayout.addRow("Result", text)
93
93
 
94
94
  elif self.question.qtype == "MC":
95
95
  for i, ans in enumerate(self.question.element.findall(XMLTags.ANSWER)):
96
- a = ans.find("text").text
96
+ pEle = ans.find("text").text
97
+ t = ET.fromstring(pEle).text
97
98
  frac = ans.get("fraction")
98
- text = QtWidgets.QLineEdit(a, self)
99
+ text = QtWidgets.QLineEdit(t, self)
99
100
  self.ui.answersFormLayout.addRow(f"Fraction: {frac}", text)
100
101
 
101
102
 
@@ -107,7 +108,7 @@ class AboutDialog(QtWidgets.QMessageBox):
107
108
  self.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Close)
108
109
 
109
110
  self.aboutMessage: str = f"""
110
- <h1> About {e2mMetadata["name"]}</h1><br>
111
+ <h1> About {e2mMetadata["name"]} v {e2mMetadata["version"]}</h1><br>
111
112
  <p style="text-align:center">
112
113
 
113
114
  <b><a href="{e2mMetadata["homepage"]}">{e2mMetadata["name"]}</a> - {e2mMetadata["description"]}</b>
@@ -3,7 +3,7 @@
3
3
  import logging
4
4
  from enum import StrEnum
5
5
  from pathlib import Path
6
- from typing import Literal, overload
6
+ from typing import ClassVar, Literal, overload
7
7
 
8
8
  from PySide6.QtCore import QSettings, QTimer, Signal
9
9
 
@@ -11,50 +11,79 @@ logger = logging.getLogger(__name__)
11
11
 
12
12
 
13
13
  class SettingsKey(StrEnum):
14
+ """Settings Keys are needed to always acess the correct Value.
15
+
16
+ As the QSettings settings are accesed via strings, which could easily gotten wrong.
17
+ Further, this Enum defines, which type a setting has to be.
18
+ """
19
+
14
20
  def __new__(
15
21
  cls,
16
22
  key: str,
23
+ place: str,
17
24
  typ: type,
18
25
  default: str | float | Path | bool | None,
19
26
  ):
20
27
  """Define new settings class."""
21
28
  obj = str.__new__(cls, key)
22
29
  obj._value_ = key
30
+ obj._place_ = place
23
31
  obj._default_ = default
24
32
  obj._typ_ = typ
25
33
  return obj
26
34
 
35
+ def __init__(
36
+ self, _, place: str, typ: type, default: str | float | Path | None
37
+ ) -> None:
38
+ self._typ_ = typ
39
+ self._place_ = place
40
+ self._default_ = default
41
+ self._full_ = f"{self._place_}/{self._value_}"
42
+
27
43
  @property
28
44
  def default(self) -> str | int | float | Path | bool | None:
29
45
  """Get default value for the key."""
30
46
  return self._default_
31
47
 
48
+ @property
49
+ def place(self) -> str:
50
+ return self._place_
51
+
52
+ @property
53
+ def full(self) -> str:
54
+ return self._full_
55
+
32
56
  def typ(self) -> type:
33
57
  """Get default value for the key."""
34
58
  return self._typ_
35
59
 
36
- QUESTIONVARIANT = "testgen/defaultQuestionVariant", int, 0
37
- INCLUDEINCATS = "testgen/includeCats", bool, False
38
- PARSERNF_TOLERANCE = "parser/nf/tolerance", int, 1
39
- PICTURESUBFOLDER = "core/pictureSubFolder", str, "Abbildungen"
40
- PICTUREFOLDER = "core/pictureFolder", Path, None
41
- SPREADSHEETFOLDER = "core/spreadsheetFolder", Path, None
42
- LOGLEVEL = "core/loglevel", str, "INFO"
43
- LOGFILE = "core/logfile", str, "excel2moodleLogFile.log"
60
+ QUESTIONVARIANT = "defaultQuestionVariant", "testgen", int, 0
61
+ INCLUDEINCATS = "includeCats", "tesgen", bool, False
62
+ PARSERNF_TOLERANCE = "tolerance", "parser/nf", int, 1
63
+ PICTURESUBFOLDER = "imgSubFolder", "core", str, "Abbildungen"
64
+ PICTUREFOLDER = "pictureFolder", "core", Path, None
65
+ SPREADSHEETFOLDER = "spreadsheetFolder", "core", Path, None
66
+ LOGLEVEL = "loglevel", "core", str, "INFO"
67
+ LOGFILE = "logfile", "core", str, "excel2moodleLogFile.log"
68
+ CATEGORIESSHEET = "categoriesDataSheet", "core", str, "Kategorien"
69
+ VERSION = "version", "project", int, 1
70
+ POINTS = "points", "project", float, 1.0
44
71
 
45
72
 
46
73
  class Settings(QSettings):
47
74
  """Settings for Excel2moodle."""
48
75
 
49
76
  shPathChanged = Signal(Path)
77
+ localSettings: ClassVar[dict[str, str | float | Path]] = {}
50
78
 
51
79
  def __init__(self) -> None:
52
80
  """Instantiate the settings."""
53
81
  super().__init__("jbosse3", "excel2moodle")
54
- if self.contains(SettingsKey.SPREADSHEETFOLDER):
82
+ logger.info("Settings are stored under: %s", self.fileName())
83
+ if self.contains(SettingsKey.SPREADSHEETFOLDER.full):
55
84
  self.sheet = self.get(SettingsKey.SPREADSHEETFOLDER)
56
85
  if self.sheet.is_file():
57
- QTimer.singleShot(0, self._emitSpreadsheetChanged)
86
+ QTimer.singleShot(300, self._emitSpreadsheetChanged)
58
87
 
59
88
  def _emitSpreadsheetChanged(self) -> None:
60
89
  self.shPathChanged.emit(self.sheet)
@@ -62,54 +91,89 @@ class Settings(QSettings):
62
91
  @overload
63
92
  def get(
64
93
  self,
65
- value: Literal[SettingsKey.QUESTIONVARIANT, SettingsKey.PARSERNF_TOLERANCE],
94
+ key: Literal[
95
+ SettingsKey.QUESTIONVARIANT,
96
+ SettingsKey.PARSERNF_TOLERANCE,
97
+ SettingsKey.VERSION,
98
+ SettingsKey.POINTS,
99
+ ],
66
100
  ) -> int: ...
67
101
  @overload
68
- def get(self, value: Literal[SettingsKey.INCLUDEINCATS]) -> bool: ...
102
+ def get(self, key: Literal[SettingsKey.INCLUDEINCATS]) -> bool: ...
69
103
  @overload
70
104
  def get(
71
105
  self,
72
- value: Literal[
73
- SettingsKey.PICTURESUBFOLDER, SettingsKey.LOGLEVEL, SettingsKey.LOGFILE
106
+ key: Literal[
107
+ SettingsKey.PICTURESUBFOLDER,
108
+ SettingsKey.LOGLEVEL,
109
+ SettingsKey.LOGFILE,
110
+ SettingsKey.CATEGORIESSHEET,
74
111
  ],
75
112
  ) -> str: ...
76
113
  @overload
77
114
  def get(
78
115
  self,
79
- value: Literal[SettingsKey.PICTUREFOLDER, SettingsKey.SPREADSHEETFOLDER],
116
+ key: Literal[SettingsKey.PICTUREFOLDER, SettingsKey.SPREADSHEETFOLDER],
80
117
  ) -> Path: ...
81
118
 
82
- def get(self, value: SettingsKey):
119
+ def get(self, key: SettingsKey):
83
120
  """Get the typesafe settings value."""
84
- logger.debug("entering get method. searched typ: %s", value.typ())
85
- if value.typ() is Path:
86
- logger.debug("trying to acess a path object from settings")
87
- path = self.value(value, defaultValue=value.default)
121
+ logger.debug("LocalSettings: %s", self.localSettings)
122
+ if key in self.localSettings:
123
+ val = key.typ()(self.localSettings[key])
124
+ logger.debug("Returning project setting: %s = %s", key, val)
125
+ return val
126
+ if key.typ() is Path:
127
+ path: Path = self.value(key.full, defaultValue=key.default)
88
128
  try:
89
129
  path.resolve(strict=True)
90
130
  except ValueError:
91
131
  logger.warning(
92
- f"The settingsvalue {value} couldn't be fetched with correct typ",
132
+ f"The settingsvalue {key} couldn't be fetched with correct typ",
93
133
  )
94
- return value.default
134
+ return key.default
135
+ logger.debug("Returning path setting: %s = %s", key, path)
95
136
  return path
96
- raw = self.value(value, defaultValue=value.default, type=value.typ())
97
- logger.debug("read a settings Value: %s of type: %s", value, value.typ())
137
+ raw = self.value(key.full, defaultValue=key.default, type=key.typ())
138
+ logger.debug("read a settings Value: %s of type: %s", key, key.typ())
98
139
  try:
99
- return value.typ()(raw)
140
+ logger.debug("Returning global setting: %s = %s", key, raw)
141
+ return key.typ()(raw)
100
142
  except (ValueError, TypeError):
101
143
  logger.warning(
102
- f"The settingsvalue {value} couldn't be fetched with correct typ",
144
+ f"The settingsvalue {key} couldn't be fetched with correct typ",
103
145
  )
104
- return value.default
105
-
106
- def set(self, settingKey: SettingsKey, value: float | bool | Path | str) -> None:
107
- """Set the setting to value."""
108
- # if isinstance(value, SettingsKey.type):
109
- self.setValue(settingKey, value)
110
- logger.info("Saved the Setting %s = %s", settingKey, value)
111
- # else:
112
- # logger.error("trying to save setting with wrong type not possible")
146
+ return key.default
147
+
148
+ def set(
149
+ self,
150
+ key: SettingsKey | str,
151
+ value: float | bool | Path | str,
152
+ local: bool = False,
153
+ ) -> None:
154
+ """Set the setting to value.
155
+
156
+ Parameters
157
+ ----------
158
+ local
159
+ True saves local project specific settings.
160
+ Defaults to False
161
+ The local settings are meant to be set in the first sheet ``settings``
162
+
163
+ """
164
+ if local:
165
+ if key in SettingsKey:
166
+ self.localSettings[key] = value
167
+ logger.info("Saved the project setting %s = %s", key, value)
168
+ else:
169
+ logger.warning("got invalid local Setting %s = %s", key, value)
170
+ return
171
+ if not local and isinstance(key, SettingsKey):
172
+ if not isinstance(value, key.typ()):
173
+ logger.error("trying to save setting with wrong type not possible")
174
+ return
175
+ self.setValue(key.full, value)
176
+ logger.info("Saved the global setting %s = %s", key, value)
113
177
 
114
178
  def setSpreadsheet(self, sheet: Path) -> None:
115
179
  """Save spreadsheet path and emit the changed event."""
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: excel2moodle
3
- Version: 0.3.5
3
+ Version: 0.3.7
4
4
  Summary: A package for converting questions from a spreadsheet, to valid moodle-xml
5
5
  Author: Jakob Bosse
6
6
  License-Expression: GPL-3.0-or-later
7
7
  Project-URL: Repository, https://gitlab.com/jbosse3/excel2moodle.git
8
8
  Project-URL: Documentation, https://jbosse3.gitlab.io/excel2moodle
9
- Keywords: moodle,XML,teaching,question,converter
9
+ Keywords: moodle,XML,teaching,question,converter,open educational Ressource
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.10
@@ -0,0 +1,37 @@
1
+ excel2moodle/__init__.py,sha256=SUSyvbQCcfOtTr00HvHpjziNy-flEdWanVFCM9F9yX0,2223
2
+ excel2moodle/__main__.py,sha256=VGOlPEmC4bhNNIt8uuuG80DZ5unUvPjm1PoVf0yH1FA,756
3
+ excel2moodle/logger.py,sha256=WomQ_EAiqZ2oVHNReG4KdfGRMrJl3jC3LIr8cbvOlbY,3084
4
+ excel2moodle/core/__init__.py,sha256=H4Bt6u076RKb6BH5F58nHLQvYPDUoayosM_Onyr9yT0,398
5
+ excel2moodle/core/category.py,sha256=QIl8nh1ryvlVMtNy8hWRfhZZMgGOhQZ3rwDFqf4woa4,1809
6
+ excel2moodle/core/dataStructure.py,sha256=krsoifn6fUDBcbp8CHRAvL8Zfbm1nAXwjoESK_2Jeh8,13052
7
+ excel2moodle/core/etHelpers.py,sha256=i8DAx7YBxrQqzbXFsU-pIvYMPHSRhYci-JvuzY1MzeI,2299
8
+ excel2moodle/core/exceptions.py,sha256=VgbxrnoR9RRnmDYK2rbB_Bv00r7NLWET6FgddPwo3uw,748
9
+ excel2moodle/core/globals.py,sha256=cB5kstn57UYYiUJgLxNnqAGNAKVTolUjHPTt7XIA4G8,3294
10
+ excel2moodle/core/numericMultiQ.py,sha256=vr-gYogu2sf2a_Bhvhnu1ZSZFZXM32MfhJesjTkoOQM,2618
11
+ excel2moodle/core/parser.py,sha256=eVSHUihgW_Zk-mIffGL1kWJnHeSn9J76MfrgOzg0gNg,8991
12
+ excel2moodle/core/question.py,sha256=gF7dbbNgchoBWSti_kWlAHW17REYc5jpdtTjmNl2iU8,7157
13
+ excel2moodle/core/questionWriter.py,sha256=UiWbrtNSiQiYY_x3sF3nz6Ic0b_lL0uWHyeDNDtFvPM,10985
14
+ excel2moodle/core/stringHelpers.py,sha256=P7xr5LM2r7qfKzEPBVUSMMDsiVB6jpFAUuhOhGkOMQY,2284
15
+ excel2moodle/core/validator.py,sha256=-ogarWJ1tNrWg457SZtfDqwc8ToDAxgyLM6GW4rvI7g,4829
16
+ excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
17
+ excel2moodle/extra/equationVerification.py,sha256=GLJl1r90d8AAiNy0H2hooZrg3D6aEwNfifYKAe3aGxM,3921
18
+ excel2moodle/question_types/__init__.py,sha256=ZYGg_tYop9c5yUfN8St6FddeaDxbyE2hneKKUFVzcGM,955
19
+ excel2moodle/question_types/mc.py,sha256=N0BREdzg6jcVq1CZMg_G8L4yAU1_1QKeDMaMrfmOBCU,3151
20
+ excel2moodle/question_types/nf.py,sha256=r1qSqYl31yUrmvzUxs2Dkkn3pBmFGN1aO7HQYIoOqkA,835
21
+ excel2moodle/question_types/nfm.py,sha256=gv3LvB6M9UI6U2PlpGerGlDVXzz4TZEudc8IsqdIru0,3301
22
+ excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
23
+ excel2moodle/ui/appUi.py,sha256=sZ92bS1DBgoK3Chg1Nu_FQ2ry3Iu_ITKgpwqt4zLVAk,10582
24
+ excel2moodle/ui/dialogs.py,sha256=HkIe3CgSpBb_shbnNEfNJs82OVm1xUA53dsjDE8wwO0,5395
25
+ excel2moodle/ui/questionPreviewDialog.py,sha256=_rJvz1GM90aNnj3P6SugEezK7JW6m74ZALgkChohWLM,4980
26
+ excel2moodle/ui/settings.py,sha256=XRnCUgYrF9cz_bv91Zj965SJw3vJ0QlS0dgzzGeee-Y,6331
27
+ excel2moodle/ui/treewidget.py,sha256=mTRqmZHMZT3QTTvt5Gw9L-yl4KdhhulkF7O1KBwW-_E,2449
28
+ excel2moodle/ui/variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
29
+ excel2moodle/ui/windowDoc.py,sha256=IciZpwrLnGzIQV1aCdKQBg6km3oufHGs8havTFzNJyU,1055
30
+ excel2moodle/ui/windowEquationChecker.py,sha256=fLyal3sbJwpthWCAxLB5vbSFOX23JoivoYksNp3mZVY,7925
31
+ excel2moodle/ui/windowMain.py,sha256=FsaApCyWgngnWPb46R8_01z8q4XeWxRFVqewSIt742c,19027
32
+ excel2moodle-0.3.7.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
33
+ excel2moodle-0.3.7.dist-info/METADATA,sha256=RmY9IFvitRlUaI6EugujfhXdZe8IFAsUdpJBmp_Oq-o,2972
34
+ excel2moodle-0.3.7.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
35
+ excel2moodle-0.3.7.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
36
+ excel2moodle-0.3.7.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
37
+ excel2moodle-0.3.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5