excel2moodle 0.3.6__py3-none-any.whl → 0.4.0__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 +6 -12
- excel2moodle/__main__.py +14 -3
- excel2moodle/core/category.py +6 -44
- excel2moodle/core/dataStructure.py +249 -82
- excel2moodle/core/globals.py +1 -21
- excel2moodle/core/parser.py +34 -204
- excel2moodle/core/question.py +108 -57
- excel2moodle/core/settings.py +201 -0
- excel2moodle/core/stringHelpers.py +10 -33
- excel2moodle/core/{questionValidator.py → validator.py} +32 -34
- excel2moodle/logger.py +1 -1
- excel2moodle/question_types/__init__.py +33 -0
- excel2moodle/question_types/mc.py +127 -0
- excel2moodle/question_types/nf.py +29 -0
- excel2moodle/question_types/nfm.py +91 -0
- excel2moodle/ui/appUi.py +67 -47
- excel2moodle/ui/dialogs.py +43 -34
- excel2moodle/ui/exportSettingsDialog.py +79 -0
- excel2moodle/ui/windowDoc.py +9 -17
- excel2moodle/ui/windowMain.py +220 -225
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.4.0.dist-info}/METADATA +2 -2
- excel2moodle-0.4.0.dist-info/RECORD +37 -0
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.4.0.dist-info}/WHEEL +1 -1
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.4.0.dist-info}/entry_points.txt +3 -0
- excel2moodle/core/questionWriter.py +0 -267
- excel2moodle/ui/settings.py +0 -123
- excel2moodle-0.3.6.dist-info/RECORD +0 -33
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.6.dist-info → excel2moodle-0.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,91 @@
|
|
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
|
+
ansElementsList.append(
|
43
|
+
self.getNumericAnsElement(result=round(result, 3)),
|
44
|
+
)
|
45
|
+
self.question.answerVariants = ansElementsList
|
46
|
+
self.setVariants(len(ansElementsList))
|
47
|
+
|
48
|
+
def setVariants(self, number: int) -> None:
|
49
|
+
self.question.variants = number
|
50
|
+
mvar = self.question.category.maxVariants
|
51
|
+
if mvar is None:
|
52
|
+
self.question.category.maxVariants = number
|
53
|
+
else:
|
54
|
+
self.question.category.maxVariants = min(number, mvar)
|
55
|
+
|
56
|
+
def _setupAstIntprt(self, var: dict[str, list[float | int]], index: int) -> None:
|
57
|
+
"""Setup the asteval Interpreter with the variables."""
|
58
|
+
for name, value in var.items():
|
59
|
+
self.astEval.symtable[name] = value[index]
|
60
|
+
|
61
|
+
def _getVariablesDict(self, keyList: list) -> tuple[dict[str, list[float]], int]:
|
62
|
+
"""Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]."""
|
63
|
+
dic: dict = {}
|
64
|
+
num: int = 0
|
65
|
+
for k in keyList:
|
66
|
+
val = self.rawInput[k]
|
67
|
+
if isinstance(val, str):
|
68
|
+
li = stringHelpers.getListFromStr(val)
|
69
|
+
num = len(li)
|
70
|
+
variables: list[float] = [float(i.replace(",", ".")) for i in li]
|
71
|
+
dic[str(k)] = variables
|
72
|
+
else:
|
73
|
+
dic[str(k)] = [str(val)]
|
74
|
+
num = 1
|
75
|
+
self.logger.debug("The following variables were provided: %s", dic)
|
76
|
+
return dic, num
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def _getVarsList(bps: str | list[str]) -> list:
|
80
|
+
"""Durchsucht den bulletPoints String nach den Variablen ``{var}``.
|
81
|
+
|
82
|
+
It only finds variables after the ``=`` sign, to not catch LaTex.
|
83
|
+
"""
|
84
|
+
varNames = []
|
85
|
+
regexFinder = re.compile(r"=\s*\{(\w+)\}")
|
86
|
+
if isinstance(bps, list):
|
87
|
+
for _p in bps:
|
88
|
+
varNames.extend(regexFinder.findall(str(_p)))
|
89
|
+
else:
|
90
|
+
varNames = regexFinder.findall(str(bps))
|
91
|
+
return varNames
|
excel2moodle/ui/appUi.py
CHANGED
@@ -10,18 +10,24 @@ 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
|
16
|
+
from excel2moodle.core.settings import Settings, SettingsKey
|
15
17
|
from excel2moodle.extra import equationVerification as eqVerif
|
18
|
+
from excel2moodle.logger import LogWindowHandler
|
16
19
|
from excel2moodle.ui import dialogs
|
17
|
-
from excel2moodle.ui.settings import Settings, SettingsKey
|
18
20
|
from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
|
21
|
+
from excel2moodle.ui.windowDoc import DocumentationWindow
|
19
22
|
from excel2moodle.ui.windowMain import Ui_MoodleTestGenerator
|
20
23
|
|
21
24
|
from .windowEquationChecker import Ui_EquationChecker
|
22
25
|
|
23
26
|
logger = logging.getLogger(__name__)
|
24
27
|
|
28
|
+
loggerSignal = LogWindowHandler()
|
29
|
+
mainLogger.addHandler(loggerSignal)
|
30
|
+
|
25
31
|
|
26
32
|
class MainWindow(QtWidgets.QMainWindow):
|
27
33
|
def __init__(self, settings: Settings, testDB: QuestionDB) -> None:
|
@@ -33,16 +39,18 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
33
39
|
self.testDB = testDB
|
34
40
|
self.ui = Ui_MoodleTestGenerator()
|
35
41
|
self.ui.setupUi(self)
|
42
|
+
self.exportDialog = dialogs.ExportDialog(self)
|
43
|
+
self.questionPreview = dialogs.QuestionPreview(self)
|
36
44
|
self.connectEvents()
|
37
45
|
logger.info("Settings are stored under: %s", self.settings.fileName())
|
38
46
|
self.ui.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
|
39
47
|
self.ui.treeWidget.header().setSectionResizeMode(
|
40
48
|
QtWidgets.QHeaderView.ResizeToContents,
|
41
49
|
)
|
42
|
-
self.ui.checkBoxIncludeCategories.setChecked(
|
50
|
+
self.exportDialog.ui.checkBoxIncludeCategories.setChecked(
|
43
51
|
self.settings.get(SettingsKey.INCLUDEINCATS),
|
44
52
|
)
|
45
|
-
self.ui.spinBoxDefaultQVariant.setValue(
|
53
|
+
self.exportDialog.ui.spinBoxDefaultQVariant.setValue(
|
46
54
|
self.settings.get(SettingsKey.QUESTIONVARIANT)
|
47
55
|
)
|
48
56
|
self.ui.pointCounter.setReadOnly(True)
|
@@ -58,30 +66,35 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
58
66
|
self.threadPool = QThreadPool()
|
59
67
|
|
60
68
|
def connectEvents(self) -> None:
|
61
|
-
self.ui.treeWidget.
|
69
|
+
self.ui.treeWidget.itemSelectionChanged.connect(self.onSelectionChanged)
|
62
70
|
self.ui.checkBoxQuestionListSelectAll.checkStateChanged.connect(
|
63
71
|
self.toggleQuestionSelectionState,
|
64
72
|
)
|
65
|
-
|
73
|
+
loggerSignal.emitter.signal.connect(self.updateLog)
|
66
74
|
self.ui.actionEquationChecker.triggered.connect(self.openEqCheckerDlg)
|
67
|
-
self.ui.checkBoxIncludeCategories.checkStateChanged.connect(
|
75
|
+
self.exportDialog.ui.checkBoxIncludeCategories.checkStateChanged.connect(
|
68
76
|
self.setIncludeCategoriesSetting,
|
69
77
|
)
|
70
|
-
self.ui.actionParseAll.triggered.connect(self.
|
71
|
-
|
72
|
-
self.ui.
|
73
|
-
self.ui.
|
74
|
-
self.ui.
|
78
|
+
self.ui.actionParseAll.triggered.connect(self.parseSpreadsheetAll)
|
79
|
+
self.testDB.signals.categoryQuestionsReady.connect(self.treeRefreshCategory)
|
80
|
+
self.ui.actionSpreadsheet.triggered.connect(self.actionSpreadsheet)
|
81
|
+
self.ui.actionExport.triggered.connect(self.onButGenTest)
|
82
|
+
self.ui.buttonSpreadsheet.clicked.connect(self.actionSpreadsheet)
|
83
|
+
self.ui.buttonExport.clicked.connect(self.onButGenTest)
|
84
|
+
self.ui.treeWidget.itemClicked.connect(self.updateQuestionPreview)
|
75
85
|
self.ui.actionAbout.triggered.connect(self.openAboutDlg)
|
86
|
+
self.ui.actionDocumentation.triggered.connect(self.openDocumentation)
|
76
87
|
self.settings.shPathChanged.connect(self.onSheetPathChanged)
|
77
|
-
self.ui.spinBoxDefaultQVariant.valueChanged.connect(
|
88
|
+
self.exportDialog.ui.spinBoxDefaultQVariant.valueChanged.connect(
|
89
|
+
self.setQVariantDefault
|
90
|
+
)
|
78
91
|
|
79
92
|
@QtCore.Slot()
|
80
93
|
def setQVariantDefault(self, value: int) -> None:
|
81
94
|
self.settings.set(SettingsKey.QUESTIONVARIANT, value)
|
82
95
|
|
83
96
|
@QtCore.Slot()
|
84
|
-
def
|
97
|
+
def parseSpreadsheetAll(self) -> None:
|
85
98
|
"""Event triggered by the *Tools/Parse all Questions* Event.
|
86
99
|
|
87
100
|
It parses all the Questions found in the spreadsheet
|
@@ -89,7 +102,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
89
102
|
If successful it prints out a list of all exported Questions
|
90
103
|
"""
|
91
104
|
self.ui.treeWidget.clear()
|
92
|
-
process =
|
105
|
+
process = ParseAllThread(self.testDB, self)
|
93
106
|
self.threadPool.start(process)
|
94
107
|
|
95
108
|
@QtCore.Slot(Path)
|
@@ -100,14 +113,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
100
113
|
svgFolder = self.mainPath / self.settings.get(SettingsKey.PICTURESUBFOLDER)
|
101
114
|
svgFolder.resolve()
|
102
115
|
self.settings.set(SettingsKey.PICTUREFOLDER, svgFolder)
|
103
|
-
self.ui.
|
104
|
-
self.
|
116
|
+
self.ui.buttonSpreadsheet.setText(f"../{sheet.name}")
|
117
|
+
self.parseSpreadsheetAll()
|
105
118
|
|
106
119
|
def updateLog(self, log) -> None:
|
107
120
|
self.ui.loggerWindow.append(log)
|
108
121
|
|
109
122
|
def setIncludeCategoriesSetting(self) -> None:
|
110
|
-
if self.ui.checkBoxIncludeCategories.isChecked():
|
123
|
+
if self.exportDialog.ui.checkBoxIncludeCategories.isChecked():
|
111
124
|
self.settings.set(SettingsKey.INCLUDEINCATS, True)
|
112
125
|
else:
|
113
126
|
self.settings.set(SettingsKey.INCLUDEINCATS, False)
|
@@ -125,8 +138,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
125
138
|
for q in selection:
|
126
139
|
questions += 1
|
127
140
|
count += q.getQuestion().points
|
128
|
-
|
129
|
-
logger.info("%s questions are selected with %s points", questions, count)
|
130
141
|
self.ui.pointCounter.setValue(count)
|
131
142
|
self.ui.questionCounter.setValue(questions)
|
132
143
|
|
@@ -143,6 +154,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
143
154
|
@QtCore.Slot()
|
144
155
|
def onButGenTest(self) -> None:
|
145
156
|
"""Open a file Dialog so the export file may be choosen."""
|
157
|
+
self.exportDialog.exec()
|
146
158
|
path = QtWidgets.QFileDialog.getSaveFileName(
|
147
159
|
self,
|
148
160
|
"Select Output File",
|
@@ -155,7 +167,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
155
167
|
self.testDB.appendQuestions(selection, self.exportFile)
|
156
168
|
|
157
169
|
@QtCore.Slot()
|
158
|
-
def
|
170
|
+
def actionSpreadsheet(self) -> None:
|
159
171
|
file = QtWidgets.QFileDialog.getOpenFileName(
|
160
172
|
self,
|
161
173
|
self.tr("Open Spreadsheet"),
|
@@ -163,32 +175,28 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
163
175
|
filter=self.tr("Spreadsheet(*.xlsx *.ods)"),
|
164
176
|
selectedFilter=("*.ods"),
|
165
177
|
)
|
166
|
-
|
167
|
-
|
168
|
-
|
178
|
+
path = Path(file[0]).resolve(strict=True)
|
179
|
+
if path.is_file():
|
180
|
+
self.excelPath = path
|
181
|
+
self.settings.setSpreadsheet(self.excelPath)
|
182
|
+
self.setStatus("[OK] Excel Tabelle wurde eingelesen")
|
183
|
+
else:
|
184
|
+
self.setStatus("[ERROR] keine Tabelle angegeben")
|
169
185
|
|
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)
|
186
|
+
@QtCore.Slot(Category)
|
187
|
+
def treeRefreshCategory(self, cat: Category) -> None:
|
188
|
+
"""Append Category with its Questions to the treewidget."""
|
189
|
+
catItem = CategoryItem(self.ui.treeWidget, cat)
|
190
|
+
catItem.setFlags(catItem.flags() & ~Qt.ItemIsSelectable)
|
191
|
+
for q in cat.questions.values():
|
192
|
+
QuestionItem(catItem, q)
|
193
|
+
self.ui.treeWidget.sortItems(0, Qt.SortOrder.AscendingOrder)
|
185
194
|
|
186
195
|
@QtCore.Slot()
|
187
|
-
def
|
196
|
+
def updateQuestionPreview(self) -> None:
|
188
197
|
item = self.ui.treeWidget.currentItem()
|
189
198
|
if isinstance(item, QuestionItem):
|
190
|
-
|
191
|
-
dialog.show()
|
199
|
+
self.questionPreview.setupQuestion(item.getQuestion())
|
192
200
|
else:
|
193
201
|
logger.info("current Item is not a Question, can't preview")
|
194
202
|
|
@@ -208,8 +216,21 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
208
216
|
about = dialogs.AboutDialog(self)
|
209
217
|
about.exec()
|
210
218
|
|
219
|
+
@QtCore.Slot()
|
220
|
+
def openDocumentation(self) -> None:
|
221
|
+
if hasattr(self, "documentationWindow"):
|
222
|
+
self.documentationWindow.show()
|
223
|
+
else:
|
224
|
+
self.documentationWindow = DocumentationWindow(self)
|
225
|
+
self.documentationWindow.show()
|
226
|
+
|
227
|
+
|
228
|
+
class ParseAllThread(QRunnable):
|
229
|
+
"""Parse the whole Spreadsheet.
|
230
|
+
Start by reading the spreadsheet asynchron.
|
231
|
+
When finished parse all Categories subsequently.
|
232
|
+
"""
|
211
233
|
|
212
|
-
class ParseSpreadsheetThread(QRunnable):
|
213
234
|
def __init__(self, questionDB: QuestionDB, mainApp: MainWindow) -> None:
|
214
235
|
super().__init__()
|
215
236
|
self.testDB = questionDB
|
@@ -217,11 +238,10 @@ class ParseSpreadsheetThread(QRunnable):
|
|
217
238
|
|
218
239
|
@QtCore.Slot()
|
219
240
|
def run(self) -> None:
|
220
|
-
self.testDB.
|
241
|
+
self.testDB.readCategoriesMetadata(self.mainApp.spreadSheetPath)
|
242
|
+
self.testDB.asyncInitAllCategories(self.mainApp.spreadSheetPath)
|
221
243
|
self.mainApp.setStatus("[OK] Tabellen wurde eingelesen")
|
222
|
-
self.testDB.
|
223
|
-
self.mainApp.setStatus("[OK] Alle Fragen wurden erfolgreich geparsed")
|
224
|
-
self.mainApp.refreshList()
|
244
|
+
self.testDB.parseAllQuestions()
|
225
245
|
|
226
246
|
|
227
247
|
class EqCheckerWindow(QtWidgets.QWidget):
|
excel2moodle/ui/dialogs.py
CHANGED
@@ -7,7 +7,7 @@ from PySide6.QtSvgWidgets import QGraphicsSvgItem
|
|
7
7
|
from excel2moodle import e2mMetadata
|
8
8
|
from excel2moodle.core.globals import XMLTags
|
9
9
|
from excel2moodle.core.question import Question
|
10
|
-
from excel2moodle.ui.
|
10
|
+
from excel2moodle.ui.exportSettingsDialog import Ui_ExportDialog
|
11
11
|
from excel2moodle.ui.variantDialog import Ui_Dialog
|
12
12
|
|
13
13
|
|
@@ -32,43 +32,48 @@ class QuestionVariantDialog(QtWidgets.QDialog):
|
|
32
32
|
return self.ui.checkBox.isChecked()
|
33
33
|
|
34
34
|
|
35
|
-
class
|
36
|
-
def __init__(self, parent
|
35
|
+
class ExportDialog(QtWidgets.QDialog):
|
36
|
+
def __init__(self, parent) -> None:
|
37
37
|
super().__init__(parent)
|
38
|
-
self.question
|
39
|
-
self.ui =
|
38
|
+
self.setWindowTitle("Export question Selection")
|
39
|
+
self.ui = Ui_ExportDialog()
|
40
40
|
self.ui.setupUi(self)
|
41
|
-
self.setModal(True)
|
42
|
-
self.setWindowTitle(f"Question - {question.id} - Preview")
|
43
|
-
self.setupQuestion()
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
|
43
|
+
class QuestionPreview:
|
44
|
+
def __init__(self, parent) -> None:
|
45
|
+
self.ui = parent.ui
|
46
|
+
self.parent = parent
|
47
|
+
|
48
|
+
def setupQuestion(self, question: Question) -> None:
|
49
|
+
self.question: Question = question
|
50
|
+
self.ui.qNameLine.setText(f"{self.question.qtype} - {self.question.name}")
|
51
|
+
self.picScene = QtWidgets.QGraphicsScene(self.parent)
|
52
|
+
self.ui.graphicsView.setScene(self.picScene)
|
48
53
|
self.setText()
|
49
54
|
self.setAnswers()
|
55
|
+
if hasattr(self, "picItem"):
|
56
|
+
self.picScene.removeItem(self.picItem)
|
50
57
|
self.setPicture()
|
51
58
|
|
52
59
|
def setPicture(self) -> None:
|
53
60
|
if hasattr(self.question, "picture") and self.question.picture.ready:
|
54
|
-
self.picScene = QtWidgets.QGraphicsScene(self)
|
55
|
-
self.ui.graphicsView.setScene(self.picScene)
|
56
61
|
path = self.question.picture.path
|
57
62
|
if path.suffix == ".svg":
|
58
|
-
picItem = QGraphicsSvgItem(str(self.question.picture.path))
|
63
|
+
self.picItem = QGraphicsSvgItem(str(self.question.picture.path))
|
59
64
|
else:
|
60
65
|
pic = QtGui.QPixmap(self.question.picture.path)
|
61
66
|
aspRat = pic.height() // pic.width()
|
62
67
|
width = 400
|
63
68
|
scaleHeight = aspRat * width
|
64
|
-
picItem = QtWidgets.QGraphicsPixmapItem(
|
69
|
+
self.picItem = QtWidgets.QGraphicsPixmapItem(
|
65
70
|
pic.scaled(
|
66
71
|
width, scaleHeight, QtGui.Qt.AspectRatioMode.KeepAspectRatio
|
67
72
|
)
|
68
73
|
)
|
69
|
-
self.picScene.addItem(picItem)
|
70
|
-
else:
|
71
|
-
|
74
|
+
self.picScene.addItem(self.picItem)
|
75
|
+
# else:
|
76
|
+
# self.ui.graphicsView.setFixedHeight(1)
|
72
77
|
|
73
78
|
def setText(self) -> None:
|
74
79
|
t = []
|
@@ -80,23 +85,26 @@ class QuestinoPreviewDialog(QtWidgets.QDialog):
|
|
80
85
|
|
81
86
|
def setAnswers(self) -> None:
|
82
87
|
if self.question.qtype == "NFM":
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
+
list = ET.Element("ol")
|
89
|
+
for ans in self.question.answerVariants:
|
90
|
+
textEle = ET.Element("li")
|
91
|
+
textEle.text = f"Result: {ans.find('text').text}"
|
92
|
+
list.append(textEle)
|
93
|
+
self.ui.answersLabel.setText(ET.tostring(list, encoding="unicode"))
|
88
94
|
elif self.question.qtype == "NF":
|
89
95
|
ans = self.question.element.find(XMLTags.ANSWER)
|
90
|
-
|
91
|
-
text = QtWidgets.QLineEdit(a, self)
|
92
|
-
self.ui.answersFormLayout.addRow("Result", text)
|
93
|
-
|
96
|
+
self.ui.answersLabel.setText(f" Result: {ans.find('text').text}")
|
94
97
|
elif self.question.qtype == "MC":
|
95
|
-
|
96
|
-
|
98
|
+
list = ET.Element("ol")
|
99
|
+
for ans in self.question.element.findall(XMLTags.ANSWER):
|
100
|
+
textEle = ET.Element("li")
|
101
|
+
pEle = ans.find("text").text
|
97
102
|
frac = ans.get("fraction")
|
98
|
-
|
99
|
-
|
103
|
+
anstext = ET.fromstring(pEle).text
|
104
|
+
text = f"Fraction {frac}: {anstext}"
|
105
|
+
textEle.text = text
|
106
|
+
list.append(textEle)
|
107
|
+
self.ui.answersLabel.setText(ET.tostring(list, encoding="unicode"))
|
100
108
|
|
101
109
|
|
102
110
|
class AboutDialog(QtWidgets.QMessageBox):
|
@@ -107,14 +115,15 @@ class AboutDialog(QtWidgets.QMessageBox):
|
|
107
115
|
self.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Close)
|
108
116
|
|
109
117
|
self.aboutMessage: str = f"""
|
110
|
-
<h1> About {e2mMetadata["name"]}</h1><br>
|
118
|
+
<h1> About {e2mMetadata["name"]} v {e2mMetadata["version"]}</h1><br>
|
111
119
|
<p style="text-align:center">
|
112
120
|
|
113
121
|
<b><a href="{e2mMetadata["homepage"]}">{e2mMetadata["name"]}</a> - {e2mMetadata["description"]}</b>
|
114
122
|
</p>
|
115
123
|
<p style="text-align:center">
|
116
|
-
|
117
|
-
<a href="{e2mMetadata["documentation"]}">
|
124
|
+
A <b>
|
125
|
+
<a href="{e2mMetadata["documentation"]}">documentation</a></b>
|
126
|
+
is also available.
|
118
127
|
</br>
|
119
128
|
</p>
|
120
129
|
<p style="text-align:center">
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
################################################################################
|
4
|
+
## Form generated from reading UI file 'exportSettingsDialog.ui'
|
5
|
+
##
|
6
|
+
## Created by: Qt User Interface Compiler version 6.9.0
|
7
|
+
##
|
8
|
+
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
9
|
+
################################################################################
|
10
|
+
|
11
|
+
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
12
|
+
QMetaObject, QObject, QPoint, QRect,
|
13
|
+
QSize, QTime, QUrl, Qt)
|
14
|
+
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
15
|
+
QFont, QFontDatabase, QGradient, QIcon,
|
16
|
+
QImage, QKeySequence, QLinearGradient, QPainter,
|
17
|
+
QPalette, QPixmap, QRadialGradient, QTransform)
|
18
|
+
from PySide6.QtWidgets import (QAbstractButton, QApplication, QCheckBox, QDialog,
|
19
|
+
QDialogButtonBox, QFormLayout, QHBoxLayout, QLabel,
|
20
|
+
QSizePolicy, QSpinBox, QWidget)
|
21
|
+
|
22
|
+
class Ui_ExportDialog(object):
|
23
|
+
def setupUi(self, ExportDialog):
|
24
|
+
if not ExportDialog.objectName():
|
25
|
+
ExportDialog.setObjectName(u"ExportDialog")
|
26
|
+
ExportDialog.resize(572, 217)
|
27
|
+
self.horizontalLayout = QHBoxLayout(ExportDialog)
|
28
|
+
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
29
|
+
self.formLayout_2 = QFormLayout()
|
30
|
+
self.formLayout_2.setObjectName(u"formLayout_2")
|
31
|
+
self.formLayout_2.setContentsMargins(5, 5, 5, 5)
|
32
|
+
self.label_10 = QLabel(ExportDialog)
|
33
|
+
self.label_10.setObjectName(u"label_10")
|
34
|
+
|
35
|
+
self.formLayout_2.setWidget(0, QFormLayout.ItemRole.LabelRole, self.label_10)
|
36
|
+
|
37
|
+
self.spinBoxDefaultQVariant = QSpinBox(ExportDialog)
|
38
|
+
self.spinBoxDefaultQVariant.setObjectName(u"spinBoxDefaultQVariant")
|
39
|
+
|
40
|
+
self.formLayout_2.setWidget(0, QFormLayout.ItemRole.FieldRole, self.spinBoxDefaultQVariant)
|
41
|
+
|
42
|
+
self.label_9 = QLabel(ExportDialog)
|
43
|
+
self.label_9.setObjectName(u"label_9")
|
44
|
+
|
45
|
+
self.formLayout_2.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_9)
|
46
|
+
|
47
|
+
self.checkBoxIncludeCategories = QCheckBox(ExportDialog)
|
48
|
+
self.checkBoxIncludeCategories.setObjectName(u"checkBoxIncludeCategories")
|
49
|
+
|
50
|
+
self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.checkBoxIncludeCategories)
|
51
|
+
|
52
|
+
|
53
|
+
self.horizontalLayout.addLayout(self.formLayout_2)
|
54
|
+
|
55
|
+
self.buttonBox = QDialogButtonBox(ExportDialog)
|
56
|
+
self.buttonBox.setObjectName(u"buttonBox")
|
57
|
+
self.buttonBox.setOrientation(Qt.Orientation.Vertical)
|
58
|
+
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
|
59
|
+
|
60
|
+
self.horizontalLayout.addWidget(self.buttonBox)
|
61
|
+
|
62
|
+
|
63
|
+
self.retranslateUi(ExportDialog)
|
64
|
+
self.buttonBox.accepted.connect(ExportDialog.accept)
|
65
|
+
self.buttonBox.rejected.connect(ExportDialog.reject)
|
66
|
+
|
67
|
+
QMetaObject.connectSlotsByName(ExportDialog)
|
68
|
+
# setupUi
|
69
|
+
|
70
|
+
def retranslateUi(self, ExportDialog):
|
71
|
+
ExportDialog.setWindowTitle(QCoreApplication.translate("ExportDialog", u"Dialog", None))
|
72
|
+
self.label_10.setText(QCoreApplication.translate("ExportDialog", u"Default Question Variant", None))
|
73
|
+
#if QT_CONFIG(tooltip)
|
74
|
+
self.label_9.setToolTip(QCoreApplication.translate("ExportDialog", u"If enabled, all questions will be categorized, when importing into moodle. Otherwise they will all be imported into one category", None))
|
75
|
+
#endif // QT_CONFIG(tooltip)
|
76
|
+
self.label_9.setText(QCoreApplication.translate("ExportDialog", u"Include Questions in Categories", None))
|
77
|
+
self.checkBoxIncludeCategories.setText("")
|
78
|
+
# retranslateUi
|
79
|
+
|
excel2moodle/ui/windowDoc.py
CHANGED
@@ -1,35 +1,27 @@
|
|
1
|
-
import os
|
2
1
|
import sys
|
3
|
-
|
4
|
-
from
|
2
|
+
|
3
|
+
from PySide6 import QtCore, QtWebEngineWidgets, QtWidgets
|
4
|
+
|
5
|
+
from excel2moodle import e2mMetadata
|
6
|
+
|
5
7
|
|
6
8
|
class DocumentationWindow(QtWidgets.QMainWindow):
|
7
|
-
def __init__(self,
|
9
|
+
def __init__(self, parent=None) -> None:
|
8
10
|
super().__init__(parent)
|
9
11
|
|
10
12
|
self.web_view = QtWebEngineWidgets.QWebEngineView()
|
11
13
|
self.setCentralWidget(self.web_view)
|
12
14
|
|
13
15
|
# Load the HTML documentation
|
14
|
-
|
15
|
-
|
16
|
-
url = QtCore.QUrl.fromLocalFile(index_file)
|
16
|
+
url = QtCore.QUrl(e2mMetadata["documentation"])
|
17
|
+
print(f"Opening URL {url}")
|
17
18
|
self.web_view.setUrl(url)
|
18
19
|
|
19
|
-
# Set up navigation events
|
20
|
-
self.web_view.page().linkHovered.connect(self.link_hovered)
|
21
|
-
self.web_view.page().loadFinished.connect(self.load_finished)
|
22
|
-
|
23
|
-
def link_hovered(self, url):
|
24
|
-
print(f"Link hovered: {url}")
|
25
|
-
|
26
|
-
def load_finished(self, ok):
|
27
|
-
print(f"Load finished: {ok}")
|
28
20
|
|
29
21
|
if __name__ == "__main__":
|
30
22
|
app = QtWidgets.QApplication(sys.argv)
|
31
23
|
|
32
|
-
window = DocumentationWindow(
|
24
|
+
window = DocumentationWindow()
|
33
25
|
window.show()
|
34
26
|
|
35
27
|
sys.exit(app.exec())
|