excel2moodle 0.3.6__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.
- excel2moodle/__init__.py +1 -2
- excel2moodle/__main__.py +1 -2
- excel2moodle/core/category.py +6 -44
- excel2moodle/core/dataStructure.py +242 -79
- excel2moodle/core/globals.py +0 -20
- excel2moodle/core/parser.py +15 -171
- excel2moodle/core/question.py +30 -13
- excel2moodle/core/stringHelpers.py +8 -32
- excel2moodle/core/{questionValidator.py → validator.py} +26 -31
- excel2moodle/question_types/__init__.py +33 -0
- excel2moodle/question_types/mc.py +93 -0
- excel2moodle/question_types/nf.py +30 -0
- excel2moodle/question_types/nfm.py +92 -0
- excel2moodle/ui/appUi.py +38 -33
- excel2moodle/ui/dialogs.py +8 -7
- excel2moodle/ui/settings.py +98 -35
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.3.7.dist-info}/METADATA +2 -2
- excel2moodle-0.3.7.dist-info/RECORD +37 -0
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.3.7.dist-info}/WHEEL +1 -1
- excel2moodle-0.3.6.dist-info/RECORD +0 -33
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.3.7.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.3.7.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.3.7.dist-info}/top_level.txt +0 -0
@@ -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
@@ -10,9 +10,11 @@ from pathlib import Path
|
|
10
10
|
from PySide6 import QtCore, QtWidgets
|
11
11
|
from PySide6.QtCore import QRunnable, Qt, QThreadPool
|
12
12
|
|
13
|
-
from excel2moodle import
|
13
|
+
from excel2moodle import mainLogger
|
14
|
+
from excel2moodle.core.category import Category
|
14
15
|
from excel2moodle.core.dataStructure import QuestionDB
|
15
16
|
from excel2moodle.extra import equationVerification as eqVerif
|
17
|
+
from excel2moodle.logger import LogWindowHandler
|
16
18
|
from excel2moodle.ui import dialogs
|
17
19
|
from excel2moodle.ui.settings import Settings, SettingsKey
|
18
20
|
from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
|
@@ -22,6 +24,9 @@ from .windowEquationChecker import Ui_EquationChecker
|
|
22
24
|
|
23
25
|
logger = logging.getLogger(__name__)
|
24
26
|
|
27
|
+
loggerSignal = LogWindowHandler()
|
28
|
+
mainLogger.addHandler(loggerSignal)
|
29
|
+
|
25
30
|
|
26
31
|
class MainWindow(QtWidgets.QMainWindow):
|
27
32
|
def __init__(self, settings: Settings, testDB: QuestionDB) -> None:
|
@@ -58,17 +63,17 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
58
63
|
self.threadPool = QThreadPool()
|
59
64
|
|
60
65
|
def connectEvents(self) -> None:
|
61
|
-
self.ui.treeWidget.
|
66
|
+
self.ui.treeWidget.itemSelectionChanged.connect(self.onSelectionChanged)
|
62
67
|
self.ui.checkBoxQuestionListSelectAll.checkStateChanged.connect(
|
63
68
|
self.toggleQuestionSelectionState,
|
64
69
|
)
|
65
|
-
|
70
|
+
loggerSignal.emitter.signal.connect(self.updateLog)
|
66
71
|
self.ui.actionEquationChecker.triggered.connect(self.openEqCheckerDlg)
|
67
72
|
self.ui.checkBoxIncludeCategories.checkStateChanged.connect(
|
68
73
|
self.setIncludeCategoriesSetting,
|
69
74
|
)
|
70
|
-
self.ui.actionParseAll.triggered.connect(self.
|
71
|
-
|
75
|
+
self.ui.actionParseAll.triggered.connect(self.parseSpreadsheetAll)
|
76
|
+
self.testDB.signals.categoryQuestionsReady.connect(self.treeRefreshCategory)
|
72
77
|
self.ui.buttonSpreadSheet.clicked.connect(self.onButSpreadsheet)
|
73
78
|
self.ui.buttonTestGen.clicked.connect(self.onButGenTest)
|
74
79
|
self.ui.actionPreviewQ.triggered.connect(self.openPreviewQuestionDlg)
|
@@ -81,7 +86,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
81
86
|
self.settings.set(SettingsKey.QUESTIONVARIANT, value)
|
82
87
|
|
83
88
|
@QtCore.Slot()
|
84
|
-
def
|
89
|
+
def parseSpreadsheetAll(self) -> None:
|
85
90
|
"""Event triggered by the *Tools/Parse all Questions* Event.
|
86
91
|
|
87
92
|
It parses all the Questions found in the spreadsheet
|
@@ -89,7 +94,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
89
94
|
If successful it prints out a list of all exported Questions
|
90
95
|
"""
|
91
96
|
self.ui.treeWidget.clear()
|
92
|
-
process =
|
97
|
+
process = ParseAllThread(self.testDB, self)
|
93
98
|
self.threadPool.start(process)
|
94
99
|
|
95
100
|
@QtCore.Slot(Path)
|
@@ -101,7 +106,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
101
106
|
svgFolder.resolve()
|
102
107
|
self.settings.set(SettingsKey.PICTUREFOLDER, svgFolder)
|
103
108
|
self.ui.buttonSpreadSheet.setText(str(sheet.name))
|
104
|
-
self.
|
109
|
+
self.parseSpreadsheetAll()
|
105
110
|
|
106
111
|
def updateLog(self, log) -> None:
|
107
112
|
self.ui.loggerWindow.append(log)
|
@@ -125,8 +130,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
125
130
|
for q in selection:
|
126
131
|
questions += 1
|
127
132
|
count += q.getQuestion().points
|
128
|
-
|
129
|
-
logger.info("%s questions are selected with %s points", questions, count)
|
130
133
|
self.ui.pointCounter.setValue(count)
|
131
134
|
self.ui.questionCounter.setValue(questions)
|
132
135
|
|
@@ -163,25 +166,22 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
163
166
|
filter=self.tr("Spreadsheet(*.xlsx *.ods)"),
|
164
167
|
selectedFilter=("*.ods"),
|
165
168
|
)
|
166
|
-
|
167
|
-
|
168
|
-
|
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")
|
169
176
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
self.ui.treeWidget.
|
178
|
-
for cat in cats.values():
|
179
|
-
catItem = CategoryItem(self.ui.treeWidget, cat)
|
180
|
-
catItem.setFlags(catItem.flags() & ~Qt.ItemIsSelectable)
|
181
|
-
for q in cat.questions.values():
|
182
|
-
QuestionItem(catItem, q)
|
183
|
-
self.setStatus("[OK] Fragen Liste wurde aktualisiert")
|
184
|
-
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)
|
185
185
|
|
186
186
|
@QtCore.Slot()
|
187
187
|
def openPreviewQuestionDlg(self) -> None:
|
@@ -209,7 +209,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
209
209
|
about.exec()
|
210
210
|
|
211
211
|
|
212
|
-
class
|
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
|
+
|
213
218
|
def __init__(self, questionDB: QuestionDB, mainApp: MainWindow) -> None:
|
214
219
|
super().__init__()
|
215
220
|
self.testDB = questionDB
|
@@ -217,11 +222,11 @@ class ParseSpreadsheetThread(QRunnable):
|
|
217
222
|
|
218
223
|
@QtCore.Slot()
|
219
224
|
def run(self) -> None:
|
220
|
-
self.testDB.
|
225
|
+
self.testDB.readCategoriesMetadata(self.mainApp.spreadSheetPath)
|
226
|
+
self.testDB.asyncInitAllCategories(self.mainApp.spreadSheetPath)
|
221
227
|
self.mainApp.setStatus("[OK] Tabellen wurde eingelesen")
|
222
|
-
self.testDB.
|
223
|
-
self.mainApp.
|
224
|
-
self.mainApp.refreshList()
|
228
|
+
self.testDB.parseAllQuestions()
|
229
|
+
self.mainApp.ui.buttonTestGen.setEnabled(True)
|
225
230
|
|
226
231
|
|
227
232
|
class EqCheckerWindow(QtWidgets.QWidget):
|
excel2moodle/ui/dialogs.py
CHANGED
@@ -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
|
-
|
85
|
-
text = QtWidgets.QLineEdit(
|
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
|
-
|
91
|
-
text = QtWidgets.QLineEdit(
|
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
|
-
|
96
|
+
pEle = ans.find("text").text
|
97
|
+
t = ET.fromstring(pEle).text
|
97
98
|
frac = ans.get("fraction")
|
98
|
-
text = QtWidgets.QLineEdit(
|
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>
|
excel2moodle/ui/settings.py
CHANGED
@@ -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,48 +11,76 @@ 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 = "
|
37
|
-
INCLUDEINCATS = "
|
38
|
-
PARSERNF_TOLERANCE = "parser/nf
|
39
|
-
PICTURESUBFOLDER = "core
|
40
|
-
PICTUREFOLDER = "
|
41
|
-
SPREADSHEETFOLDER = "
|
42
|
-
LOGLEVEL = "
|
43
|
-
LOGFILE = "
|
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
82
|
logger.info("Settings are stored under: %s", self.fileName())
|
55
|
-
if self.contains(SettingsKey.SPREADSHEETFOLDER):
|
83
|
+
if self.contains(SettingsKey.SPREADSHEETFOLDER.full):
|
56
84
|
self.sheet = self.get(SettingsKey.SPREADSHEETFOLDER)
|
57
85
|
if self.sheet.is_file():
|
58
86
|
QTimer.singleShot(300, self._emitSpreadsheetChanged)
|
@@ -63,54 +91,89 @@ class Settings(QSettings):
|
|
63
91
|
@overload
|
64
92
|
def get(
|
65
93
|
self,
|
66
|
-
|
94
|
+
key: Literal[
|
95
|
+
SettingsKey.QUESTIONVARIANT,
|
96
|
+
SettingsKey.PARSERNF_TOLERANCE,
|
97
|
+
SettingsKey.VERSION,
|
98
|
+
SettingsKey.POINTS,
|
99
|
+
],
|
67
100
|
) -> int: ...
|
68
101
|
@overload
|
69
|
-
def get(self,
|
102
|
+
def get(self, key: Literal[SettingsKey.INCLUDEINCATS]) -> bool: ...
|
70
103
|
@overload
|
71
104
|
def get(
|
72
105
|
self,
|
73
|
-
|
74
|
-
SettingsKey.PICTURESUBFOLDER,
|
106
|
+
key: Literal[
|
107
|
+
SettingsKey.PICTURESUBFOLDER,
|
108
|
+
SettingsKey.LOGLEVEL,
|
109
|
+
SettingsKey.LOGFILE,
|
110
|
+
SettingsKey.CATEGORIESSHEET,
|
75
111
|
],
|
76
112
|
) -> str: ...
|
77
113
|
@overload
|
78
114
|
def get(
|
79
115
|
self,
|
80
|
-
|
116
|
+
key: Literal[SettingsKey.PICTUREFOLDER, SettingsKey.SPREADSHEETFOLDER],
|
81
117
|
) -> Path: ...
|
82
118
|
|
83
|
-
def get(self,
|
119
|
+
def get(self, key: SettingsKey):
|
84
120
|
"""Get the typesafe settings value."""
|
85
|
-
logger.debug("
|
86
|
-
if
|
87
|
-
|
88
|
-
|
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)
|
89
128
|
try:
|
90
129
|
path.resolve(strict=True)
|
91
130
|
except ValueError:
|
92
131
|
logger.warning(
|
93
|
-
f"The settingsvalue {
|
132
|
+
f"The settingsvalue {key} couldn't be fetched with correct typ",
|
94
133
|
)
|
95
|
-
return
|
134
|
+
return key.default
|
135
|
+
logger.debug("Returning path setting: %s = %s", key, path)
|
96
136
|
return path
|
97
|
-
raw = self.value(
|
98
|
-
logger.debug("read a settings Value: %s of type: %s",
|
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())
|
99
139
|
try:
|
100
|
-
|
140
|
+
logger.debug("Returning global setting: %s = %s", key, raw)
|
141
|
+
return key.typ()(raw)
|
101
142
|
except (ValueError, TypeError):
|
102
143
|
logger.warning(
|
103
|
-
f"The settingsvalue {
|
144
|
+
f"The settingsvalue {key} couldn't be fetched with correct typ",
|
104
145
|
)
|
105
|
-
return
|
106
|
-
|
107
|
-
def set(
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
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)
|
114
177
|
|
115
178
|
def setSpreadsheet(self, sheet: Path) -> None:
|
116
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.
|
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,33 +0,0 @@
|
|
1
|
-
excel2moodle/__init__.py,sha256=tJopLP1Oks1wtiFPiW25qKwgJWa-X_C0UNQijM-pRnw,2236
|
2
|
-
excel2moodle/__main__.py,sha256=gPhAh5GmRw-atxu-3JCdElFBKFw5ZB-G_G3xoPPJQVM,812
|
3
|
-
excel2moodle/logger.py,sha256=WomQ_EAiqZ2oVHNReG4KdfGRMrJl3jC3LIr8cbvOlbY,3084
|
4
|
-
excel2moodle/core/__init__.py,sha256=H4Bt6u076RKb6BH5F58nHLQvYPDUoayosM_Onyr9yT0,398
|
5
|
-
excel2moodle/core/category.py,sha256=VunNsfRUv7O8L3XMe1BB2EFxtfCAJFaRafRYRKF15kE,3006
|
6
|
-
excel2moodle/core/dataStructure.py,sha256=G-XpkcmP4ZpySvpi9ykvETrP-U69BNtjTR762ZekuBk,6217
|
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=OGBxkgD19etaMP6sTtsTZ_u8NXbnHYdCQYAu9E3pcgE,3692
|
10
|
-
excel2moodle/core/numericMultiQ.py,sha256=vr-gYogu2sf2a_Bhvhnu1ZSZFZXM32MfhJesjTkoOQM,2618
|
11
|
-
excel2moodle/core/parser.py,sha256=jsBMjluc5OICCkrtT4WNVwO26-MaBOTuiOUbQhN2d6g,15232
|
12
|
-
excel2moodle/core/question.py,sha256=ZubBhS3n7wRFgtcKZ4AoUBSsxLyIM1wsmzsBqbSab-I,6489
|
13
|
-
excel2moodle/core/questionValidator.py,sha256=a12bPd6hdE5vuj8aoXA0qXVZ0pI98X-uUBzF0oIbzYk,4810
|
14
|
-
excel2moodle/core/questionWriter.py,sha256=UiWbrtNSiQiYY_x3sF3nz6Ic0b_lL0uWHyeDNDtFvPM,10985
|
15
|
-
excel2moodle/core/stringHelpers.py,sha256=XZAyXKZcQT_bAQSb9tBQs5tMC5soJf_ZhYFHrDX9ck4,2994
|
16
|
-
excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
|
17
|
-
excel2moodle/extra/equationVerification.py,sha256=GLJl1r90d8AAiNy0H2hooZrg3D6aEwNfifYKAe3aGxM,3921
|
18
|
-
excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
|
19
|
-
excel2moodle/ui/appUi.py,sha256=wbvAPrPFAZaXP7sPQmGt9P4KoKgzhAAwnxdh6gN1Gak,10355
|
20
|
-
excel2moodle/ui/dialogs.py,sha256=EgAKcydF4clyEweocjmtyCdsQJdQmvY-LVzOlu_cl_8,5320
|
21
|
-
excel2moodle/ui/questionPreviewDialog.py,sha256=_rJvz1GM90aNnj3P6SugEezK7JW6m74ZALgkChohWLM,4980
|
22
|
-
excel2moodle/ui/settings.py,sha256=eN5h1YeSYJ84-hDe13ief31Am5y46CxahoRx5Cn8usQ,4360
|
23
|
-
excel2moodle/ui/treewidget.py,sha256=mTRqmZHMZT3QTTvt5Gw9L-yl4KdhhulkF7O1KBwW-_E,2449
|
24
|
-
excel2moodle/ui/variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
|
25
|
-
excel2moodle/ui/windowDoc.py,sha256=IciZpwrLnGzIQV1aCdKQBg6km3oufHGs8havTFzNJyU,1055
|
26
|
-
excel2moodle/ui/windowEquationChecker.py,sha256=fLyal3sbJwpthWCAxLB5vbSFOX23JoivoYksNp3mZVY,7925
|
27
|
-
excel2moodle/ui/windowMain.py,sha256=FsaApCyWgngnWPb46R8_01z8q4XeWxRFVqewSIt742c,19027
|
28
|
-
excel2moodle-0.3.6.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
|
29
|
-
excel2moodle-0.3.6.dist-info/METADATA,sha256=O1NBWfHhc6EMKGMaBJRHW9DmPWotmbJKUzN16gv3wsA,2945
|
30
|
-
excel2moodle-0.3.6.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
|
31
|
-
excel2moodle-0.3.6.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
|
32
|
-
excel2moodle-0.3.6.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
|
33
|
-
excel2moodle-0.3.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|