excel2moodle 0.6.5__py3-none-any.whl → 0.7.1__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/core/question.py +10 -3
- excel2moodle/core/settings.py +2 -0
- excel2moodle/extra/equationChecker.py +141 -0
- excel2moodle/extra/variableGenerator.py +1 -2
- excel2moodle/question_types/cloze.py +1 -1
- excel2moodle/question_types/mc.py +43 -5
- excel2moodle/ui/UI_equationChecker.py +105 -115
- excel2moodle/ui/UI_variableGenerator.py +1 -1
- excel2moodle/ui/appUi.py +45 -43
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/METADATA +27 -1
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/RECORD +15 -16
- excel2moodle/extra/equationVerification.py +0 -126
- excel2moodle/ui/equationChecker.py +0 -70
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/WHEEL +0 -0
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.6.5.dist-info → excel2moodle-0.7.1.dist-info}/top_level.txt +0 -0
excel2moodle/core/question.py
CHANGED
@@ -60,6 +60,8 @@ class QuestionData(dict):
|
|
60
60
|
Tags.QUESTIONPART,
|
61
61
|
Tags.TEXT,
|
62
62
|
Tags.MEDIASCRIPTS,
|
63
|
+
Tags.TRUEANSFB,
|
64
|
+
Tags.FALSEANSFB,
|
63
65
|
],
|
64
66
|
default: object = None,
|
65
67
|
) -> list: ...
|
@@ -250,7 +252,7 @@ class Parametrics:
|
|
250
252
|
self.equations: dict[int, str] = (
|
251
253
|
equation if isinstance(equation, dict) else {1: equation}
|
252
254
|
)
|
253
|
-
self.
|
255
|
+
self.resultChecker: dict[int, float] = (
|
254
256
|
firstResult if isinstance(firstResult, dict) else {1: firstResult}
|
255
257
|
)
|
256
258
|
self.id = identifier
|
@@ -258,6 +260,11 @@ class Parametrics:
|
|
258
260
|
self.results: dict[int, list[float]] = {}
|
259
261
|
self.logger = LogAdapterQuestionID(loggerObj, {"qID": self.id})
|
260
262
|
|
263
|
+
def setEquation(self, number: int, equation: str) -> None:
|
264
|
+
"""Set the new equation and update all the calculations."""
|
265
|
+
self.equations[number] = equation
|
266
|
+
self._calculateResults()
|
267
|
+
|
261
268
|
@property
|
262
269
|
def variants(self) -> int:
|
263
270
|
number = 1000
|
@@ -315,12 +322,12 @@ class Parametrics:
|
|
315
322
|
result,
|
316
323
|
)
|
317
324
|
if variant == 0 and not math.isclose(
|
318
|
-
result, self.
|
325
|
+
result, self.resultChecker[num], rel_tol=0.01
|
319
326
|
):
|
320
327
|
self.logger.warning(
|
321
328
|
"The calculated result %s differs from given firstResult: %s",
|
322
329
|
result,
|
323
|
-
self.
|
330
|
+
self.resultChecker,
|
324
331
|
)
|
325
332
|
self.logger.debug(
|
326
333
|
"Calculated result %s for variant %s", result, variant
|
excel2moodle/core/settings.py
CHANGED
@@ -95,6 +95,8 @@ class Tags(StrEnum):
|
|
95
95
|
FALSEFB = "falsefeedback", str, "Your answer is sadly wrong, try again!!!"
|
96
96
|
PCORRECFB = "partialcorrectfeedback", str, "Your answer is partially right."
|
97
97
|
GENERALFB = "feedback", str, "You answered this question."
|
98
|
+
TRUEANSFB = "trueanswerfeedback", list, None
|
99
|
+
FALSEANSFB = "falseanswerfeedback", list, None
|
98
100
|
|
99
101
|
MEDIASCRIPTS = "mediascripts", list, None
|
100
102
|
MEDIACALL = "parametricmedia", str, None
|
@@ -0,0 +1,141 @@
|
|
1
|
+
"""Verify the equations written into the ``result`` field of ParametricQuestion.
|
2
|
+
|
3
|
+
This module
|
4
|
+
|
5
|
+
#. It calculates all the answers obtained from the series of variables.
|
6
|
+
#. It compares the calculation of the first answer to the ``firstResult`` field.
|
7
|
+
|
8
|
+
Usage
|
9
|
+
=====
|
10
|
+
|
11
|
+
From the main UI
|
12
|
+
----------------
|
13
|
+
|
14
|
+
#. Start this tool from the top bar in the main Window under the *Tools* section
|
15
|
+
#. A new window will open with the currently selected question
|
16
|
+
#. For Cloze Questions select the correct equation on the top
|
17
|
+
#. You can edit the equation in the middle field
|
18
|
+
#. Click on ``Check the equation now!`` and inspect the results.
|
19
|
+
#. If the first calculated result matches the value in the field ``firstResult`` your equation is probably right.
|
20
|
+
#. Now click on ``Save equation`` to copy all modified equations to the clipboard.
|
21
|
+
"""
|
22
|
+
|
23
|
+
import logging
|
24
|
+
import math
|
25
|
+
from pathlib import Path
|
26
|
+
|
27
|
+
from PySide6.QtCore import Slot
|
28
|
+
from PySide6.QtWidgets import QApplication, QDialog, QMainWindow, QMessageBox
|
29
|
+
|
30
|
+
from excel2moodle import mainLogger
|
31
|
+
from excel2moodle.core.question import ParametricQuestion, Parametrics
|
32
|
+
from excel2moodle.extra.variableGenerator import populateDataSetTable
|
33
|
+
from excel2moodle.logger import LogWindowHandler
|
34
|
+
from excel2moodle.ui.UI_equationChecker import Ui_EquationChecker
|
35
|
+
|
36
|
+
logger = logging.getLogger(__name__)
|
37
|
+
|
38
|
+
loggerSignal = LogWindowHandler()
|
39
|
+
mainLogger.addHandler(loggerSignal)
|
40
|
+
|
41
|
+
|
42
|
+
class EqCheckerWindow(QDialog):
|
43
|
+
def __init__(self, parent: QMainWindow) -> None:
|
44
|
+
super().__init__(parent=parent)
|
45
|
+
self.mainWindow = parent
|
46
|
+
self.excelFile = Path()
|
47
|
+
self.ui = Ui_EquationChecker()
|
48
|
+
self.ui.setupUi(self)
|
49
|
+
self.ui.btnRunCheck.clicked.connect(
|
50
|
+
lambda: self.updateCalculation(),
|
51
|
+
)
|
52
|
+
self.ui.answerPartNum.valueChanged.connect(lambda: self.updateEquation())
|
53
|
+
self.ui.btnCancel.clicked.connect(self.reject)
|
54
|
+
self.ui.btnFetchQst.clicked.connect(lambda: self.updateQuestion())
|
55
|
+
self.ui.btnSave.clicked.connect(lambda: self.copyEquation())
|
56
|
+
self.parametrics: Parametrics
|
57
|
+
|
58
|
+
@Slot()
|
59
|
+
def updateQuestion(self) -> None:
|
60
|
+
"""Get the current Question from the list and set up the checker."""
|
61
|
+
question = self.mainWindow.currentQuestion
|
62
|
+
if not isinstance(question, ParametricQuestion):
|
63
|
+
mainLogger.error("Can't check a question without parametrics")
|
64
|
+
return
|
65
|
+
self.ui.labelQuestionNum.setText(f"Question: {question.id}")
|
66
|
+
self.parametrics = question.parametrics
|
67
|
+
self.ui.answerPartNum.setValue(1)
|
68
|
+
self.ui.answerPartNum.setMaximum(len(self.parametrics.equations))
|
69
|
+
self.updateEquation()
|
70
|
+
|
71
|
+
@Slot()
|
72
|
+
def updateEquation(self) -> None:
|
73
|
+
"""Update only the equation number of the same question."""
|
74
|
+
self.ui.equationText.clear()
|
75
|
+
eqVariant = self.ui.answerPartNum.value()
|
76
|
+
self.ui.lineFirstResult.setText(
|
77
|
+
str(self.parametrics.resultChecker.get(eqVariant))
|
78
|
+
)
|
79
|
+
if len(self.parametrics.equations) == 1:
|
80
|
+
self.ui.answerPartNum.hide()
|
81
|
+
self.ui.labelPartNum.hide()
|
82
|
+
else:
|
83
|
+
self.ui.answerPartNum.show()
|
84
|
+
self.ui.labelPartNum.show()
|
85
|
+
self.ui.equationText.appendPlainText(
|
86
|
+
str(self.parametrics.equations.get(eqVariant))
|
87
|
+
)
|
88
|
+
self.updateCalculation()
|
89
|
+
|
90
|
+
def updateCalculation(self) -> None:
|
91
|
+
"""Calculate the current equation written in the textedit."""
|
92
|
+
equation = self.ui.equationText.toPlainText()
|
93
|
+
eqNum = self.ui.answerPartNum.value()
|
94
|
+
self.parametrics.setEquation(number=eqNum, equation=equation)
|
95
|
+
calculatedResult = self.parametrics.getResult(number=1, equation=eqNum)
|
96
|
+
check: bool = False
|
97
|
+
check = bool(
|
98
|
+
math.isclose(
|
99
|
+
calculatedResult,
|
100
|
+
self.parametrics.resultChecker.get(eqNum, 0),
|
101
|
+
rel_tol=0.01,
|
102
|
+
)
|
103
|
+
)
|
104
|
+
self.ui.lineCalculatedRes.setText(f"{calculatedResult}")
|
105
|
+
if check:
|
106
|
+
self.ui.lineCheckResult.setStyleSheet("color: rgb(24,150,0)")
|
107
|
+
self.ui.lineCheckResult.setText(
|
108
|
+
"[OK] Value of 'first result' matches calculated set 1"
|
109
|
+
)
|
110
|
+
logger.info(
|
111
|
+
"[OK] The first calculated result matches 'firstResult'",
|
112
|
+
)
|
113
|
+
else:
|
114
|
+
self.ui.lineCheckResult.setStyleSheet("color: rgb(255,0,0)")
|
115
|
+
self.ui.lineCheckResult.setText(
|
116
|
+
"[ERROR] Value of 'first result' doesn't match set 1"
|
117
|
+
)
|
118
|
+
logger.warning(
|
119
|
+
"The first calculated result does not match 'firstResult'",
|
120
|
+
)
|
121
|
+
|
122
|
+
populateDataSetTable(self.ui.tableVariables, self.parametrics)
|
123
|
+
|
124
|
+
@Slot()
|
125
|
+
def copyEquation(self) -> None:
|
126
|
+
clipb = QApplication.clipboard()
|
127
|
+
equationlist: list[str] = []
|
128
|
+
if len(self.parametrics.equations) > 1:
|
129
|
+
for i, eq in self.parametrics.equations.items():
|
130
|
+
equationlist.append(f"result:{i} \t{eq}")
|
131
|
+
else:
|
132
|
+
equationlist.append(f"result\t{self.parametrics.equations.get(1)}")
|
133
|
+
clipb.setText("\n".join(equationlist))
|
134
|
+
mainLogger.info("Copied equations to the clipboard")
|
135
|
+
QMessageBox.information(
|
136
|
+
self.mainWindow,
|
137
|
+
"Equation Copied!",
|
138
|
+
"""All equations from the current parametric question are saved to the clipboard.\n
|
139
|
+
You can paste them with 'crtl + v' into the spreadsheet.
|
140
|
+
Make sure to import them with 'Tab' as the seperator.""",
|
141
|
+
)
|
@@ -26,7 +26,7 @@ class VariableGeneratorDialog(QDialog):
|
|
26
26
|
self.ui.setupUi(self)
|
27
27
|
self.origParametrics = parametrics
|
28
28
|
self._generatedParametrics: Parametrics = Parametrics(
|
29
|
-
parametrics.equations, parametrics.
|
29
|
+
parametrics.equations, parametrics.resultChecker, identifier="genr"
|
30
30
|
)
|
31
31
|
# Load existing rules
|
32
32
|
for rule in self.origParametrics.variableRules:
|
@@ -271,4 +271,3 @@ def populateDataSetTable(
|
|
271
271
|
QTableWidgetItem(str(res)),
|
272
272
|
)
|
273
273
|
tableWidget.resizeColumnsToContents()
|
274
|
-
|
@@ -299,7 +299,7 @@ class ClozeQuestionParser(NFMQuestionParser):
|
|
299
299
|
else:
|
300
300
|
loclogger.info("Adding new equation to parametrics")
|
301
301
|
self.question.parametrics.equations[num] = eq
|
302
|
-
self.question.parametrics.
|
302
|
+
self.question.parametrics.resultChecker[num] = firstResult.get(
|
303
303
|
num, 0.0
|
304
304
|
)
|
305
305
|
if not hasattr(part, "result"):
|
@@ -52,11 +52,21 @@ class MCQuestionParser(QuestionParser):
|
|
52
52
|
|
53
53
|
def getAnsElementsList(
|
54
54
|
self,
|
55
|
-
answerList: list,
|
55
|
+
answerList: list[str],
|
56
|
+
feedbackList: list[str],
|
56
57
|
fraction: float = 50,
|
57
58
|
format="html",
|
58
59
|
) -> list[ET.Element]:
|
60
|
+
"""Take the answer Strings and format them each into a xml-tree.
|
61
|
+
|
62
|
+
If a feedbackList is given it is used. Otherwise the standard feedbacks are used.
|
63
|
+
"""
|
59
64
|
elementList: list[ET.Element] = []
|
65
|
+
if feedbackList is None:
|
66
|
+
if fraction > 0:
|
67
|
+
feedbackList = [self.rawInput.get(Tags.FALSEFB) for _ in answerList]
|
68
|
+
else:
|
69
|
+
feedbackList = [self.rawInput.get(Tags.TRUEFB) for _ in answerList]
|
60
70
|
for i, ans in enumerate(answerList):
|
61
71
|
p = TextElements.PLEFT.create()
|
62
72
|
if self.answerType == "picture":
|
@@ -72,7 +82,7 @@ class MCQuestionParser(QuestionParser):
|
|
72
82
|
elementList[-1].append(
|
73
83
|
self.getFeedBEle(
|
74
84
|
XMLTags.ANSFEEDBACK,
|
75
|
-
text=
|
85
|
+
text=feedbackList[i],
|
76
86
|
style=TextElements.SPANRED,
|
77
87
|
),
|
78
88
|
)
|
@@ -82,7 +92,7 @@ class MCQuestionParser(QuestionParser):
|
|
82
92
|
elementList[-1].append(
|
83
93
|
self.getFeedBEle(
|
84
94
|
XMLTags.ANSFEEDBACK,
|
85
|
-
text=
|
95
|
+
text=feedbackList[i],
|
86
96
|
style=TextElements.SPANGREEN,
|
87
97
|
),
|
88
98
|
)
|
@@ -121,12 +131,40 @@ class MCQuestionParser(QuestionParser):
|
|
121
131
|
self.rawInput.get(Tags.FALSE), style=self.answerType
|
122
132
|
)
|
123
133
|
self.logger.debug(f"got the following false answers \n {falseAList=}")
|
134
|
+
trueFbs = self.rawInput.get(Tags.TRUEANSFB)
|
135
|
+
falseFbs = self.rawInput.get(Tags.FALSEANSFB)
|
136
|
+
if trueFbs is None:
|
137
|
+
trueFbs = [self.rawInput.get(Tags.FALSEFB) for _ in trueAnsList]
|
138
|
+
if falseFbs is None:
|
139
|
+
falseFbs = [self.rawInput.get(Tags.TRUEFB) for _ in falseAList]
|
140
|
+
if len(trueFbs) < len(trueAnsList):
|
141
|
+
self.logger.warning(
|
142
|
+
"There are less true-feedbacks than true-answers given. Using fallback feedback"
|
143
|
+
)
|
144
|
+
delta = len(trueAnsList) - len(trueFbs)
|
145
|
+
while delta > 0:
|
146
|
+
trueFbs.append(self.rawInput.get(Tags.TRUEFB))
|
147
|
+
delta -= 1
|
148
|
+
if len(falseFbs) < len(falseAList):
|
149
|
+
self.logger.warning(
|
150
|
+
"There are less false-feedbacks than false-answers given. Using fallback feedback"
|
151
|
+
)
|
152
|
+
delta = len(falseAList) - len(falseFbs)
|
153
|
+
while delta > 0:
|
154
|
+
falseFbs.append(self.rawInput.get(Tags.TRUEFB))
|
155
|
+
delta -= 1
|
124
156
|
truefrac = 1 / len(trueAnsList) * 100
|
125
157
|
falsefrac = 1 / len(falseAList) * (-100)
|
126
158
|
self.tmpEle.find(XMLTags.PENALTY).text = str(round(truefrac / 100, 4))
|
127
|
-
ansList = self.getAnsElementsList(
|
159
|
+
ansList = self.getAnsElementsList(
|
160
|
+
answerList=trueAnsList, feedbackList=trueFbs, fraction=round(truefrac, 4)
|
161
|
+
)
|
128
162
|
ansList.extend(
|
129
|
-
self.getAnsElementsList(
|
163
|
+
self.getAnsElementsList(
|
164
|
+
answerList=falseAList,
|
165
|
+
feedbackList=falseFbs,
|
166
|
+
fraction=round(falsefrac, 4),
|
167
|
+
),
|
130
168
|
)
|
131
169
|
return ansList
|
132
170
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
################################################################################
|
4
4
|
## Form generated from reading UI file 'UI_equationChecker.ui'
|
5
5
|
##
|
6
|
-
## Created by: Qt User Interface Compiler version 6.9.
|
6
|
+
## Created by: Qt User Interface Compiler version 6.9.2
|
7
7
|
##
|
8
8
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
9
9
|
################################################################################
|
@@ -15,9 +15,10 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
|
15
15
|
QFont, QFontDatabase, QGradient, QIcon,
|
16
16
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
17
17
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
18
|
-
from PySide6.QtWidgets import (QApplication, QFrame, QGridLayout,
|
19
|
-
|
20
|
-
|
18
|
+
from PySide6.QtWidgets import (QAbstractScrollArea, QApplication, QFrame, QGridLayout,
|
19
|
+
QHBoxLayout, QHeaderView, QLabel, QLineEdit,
|
20
|
+
QPlainTextEdit, QPushButton, QSizePolicy, QSpacerItem,
|
21
|
+
QSpinBox, QTableWidget, QTableWidgetItem, QVBoxLayout,
|
21
22
|
QWidget)
|
22
23
|
|
23
24
|
class Ui_EquationChecker(object):
|
@@ -25,7 +26,7 @@ class Ui_EquationChecker(object):
|
|
25
26
|
if not EquationChecker.objectName():
|
26
27
|
EquationChecker.setObjectName(u"EquationChecker")
|
27
28
|
EquationChecker.setWindowModality(Qt.WindowModality.WindowModal)
|
28
|
-
EquationChecker.resize(
|
29
|
+
EquationChecker.resize(650, 722)
|
29
30
|
icon = QIcon(QIcon.fromTheme(QIcon.ThemeIcon.ListRemove))
|
30
31
|
EquationChecker.setWindowIcon(icon)
|
31
32
|
EquationChecker.setAutoFillBackground(False)
|
@@ -33,29 +34,21 @@ class Ui_EquationChecker(object):
|
|
33
34
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
34
35
|
self.gridLayout = QGridLayout()
|
35
36
|
self.gridLayout.setObjectName(u"gridLayout")
|
36
|
-
self.
|
37
|
-
self.
|
38
|
-
self.label_2.setMinimumSize(QSize(200, 0))
|
39
|
-
self.label_2.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
37
|
+
self.label_4 = QLabel(EquationChecker)
|
38
|
+
self.label_4.setObjectName(u"label_4")
|
40
39
|
|
41
|
-
self.gridLayout.addWidget(self.
|
40
|
+
self.gridLayout.addWidget(self.label_4, 3, 2, 1, 1)
|
42
41
|
|
43
|
-
self.
|
44
|
-
self.
|
45
|
-
|
46
|
-
sizePolicy.setHorizontalStretch(0)
|
47
|
-
sizePolicy.setVerticalStretch(0)
|
48
|
-
sizePolicy.setHeightForWidth(self.equationText.sizePolicy().hasHeightForWidth())
|
49
|
-
self.equationText.setSizePolicy(sizePolicy)
|
50
|
-
self.equationText.setMinimumSize(QSize(0, 20))
|
51
|
-
self.equationText.setBaseSize(QSize(0, 20))
|
42
|
+
self.answerPartNum = QSpinBox(EquationChecker)
|
43
|
+
self.answerPartNum.setObjectName(u"answerPartNum")
|
44
|
+
self.answerPartNum.setMinimum(1)
|
52
45
|
|
53
|
-
self.gridLayout.addWidget(self.
|
46
|
+
self.gridLayout.addWidget(self.answerPartNum, 1, 3, 1, 1)
|
54
47
|
|
55
|
-
self.
|
56
|
-
self.
|
48
|
+
self.labelPartNum = QLabel(EquationChecker)
|
49
|
+
self.labelPartNum.setObjectName(u"labelPartNum")
|
57
50
|
|
58
|
-
self.gridLayout.addWidget(self.
|
51
|
+
self.gridLayout.addWidget(self.labelPartNum, 1, 2, 1, 1)
|
59
52
|
|
60
53
|
self.lineCalculatedRes = QLineEdit(EquationChecker)
|
61
54
|
self.lineCalculatedRes.setObjectName(u"lineCalculatedRes")
|
@@ -66,119 +59,114 @@ class Ui_EquationChecker(object):
|
|
66
59
|
self.lineCalculatedRes.setReadOnly(True)
|
67
60
|
self.lineCalculatedRes.setClearButtonEnabled(False)
|
68
61
|
|
69
|
-
self.gridLayout.addWidget(self.lineCalculatedRes,
|
70
|
-
|
71
|
-
self.lineCheckResult = QLineEdit(EquationChecker)
|
72
|
-
self.lineCheckResult.setObjectName(u"lineCheckResult")
|
73
|
-
font1 = QFont()
|
74
|
-
font1.setBold(True)
|
75
|
-
font1.setItalic(True)
|
76
|
-
self.lineCheckResult.setFont(font1)
|
77
|
-
self.lineCheckResult.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
78
|
-
self.lineCheckResult.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
79
|
-
self.lineCheckResult.setReadOnly(True)
|
80
|
-
|
81
|
-
self.gridLayout.addWidget(self.lineCheckResult, 5, 4, 1, 1)
|
82
|
-
|
83
|
-
self.buttonRunCheck = QPushButton(EquationChecker)
|
84
|
-
self.buttonRunCheck.setObjectName(u"buttonRunCheck")
|
85
|
-
font2 = QFont()
|
86
|
-
font2.setPointSize(12)
|
87
|
-
font2.setBold(True)
|
88
|
-
self.buttonRunCheck.setFont(font2)
|
62
|
+
self.gridLayout.addWidget(self.lineCalculatedRes, 3, 3, 1, 1)
|
89
63
|
|
90
|
-
self.
|
91
|
-
|
92
|
-
self.
|
93
|
-
self.
|
94
|
-
|
95
|
-
|
96
|
-
self.label_8.setFont(font3)
|
64
|
+
self.lineFirstResult = QLineEdit(EquationChecker)
|
65
|
+
self.lineFirstResult.setObjectName(u"lineFirstResult")
|
66
|
+
self.lineFirstResult.setEnabled(True)
|
67
|
+
self.lineFirstResult.setFont(font)
|
68
|
+
self.lineFirstResult.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
69
|
+
self.lineFirstResult.setReadOnly(True)
|
97
70
|
|
98
|
-
self.gridLayout.addWidget(self.
|
71
|
+
self.gridLayout.addWidget(self.lineFirstResult, 2, 3, 1, 1)
|
99
72
|
|
100
|
-
self.
|
101
|
-
self.
|
73
|
+
self.label_3 = QLabel(EquationChecker)
|
74
|
+
self.label_3.setObjectName(u"label_3")
|
102
75
|
|
103
|
-
self.gridLayout.addWidget(self.
|
76
|
+
self.gridLayout.addWidget(self.label_3, 2, 2, 1, 1)
|
104
77
|
|
105
|
-
self.
|
106
|
-
self.
|
107
|
-
self.label_9.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
78
|
+
self.btnFetchQst = QPushButton(EquationChecker)
|
79
|
+
self.btnFetchQst.setObjectName(u"btnFetchQst")
|
108
80
|
|
109
|
-
self.gridLayout.addWidget(self.
|
81
|
+
self.gridLayout.addWidget(self.btnFetchQst, 0, 3, 1, 1)
|
110
82
|
|
111
|
-
self.
|
112
|
-
self.
|
83
|
+
self.labelQuestionNum = QLabel(EquationChecker)
|
84
|
+
self.labelQuestionNum.setObjectName(u"labelQuestionNum")
|
85
|
+
self.labelQuestionNum.setFont(font)
|
113
86
|
|
114
|
-
self.gridLayout.addWidget(self.
|
87
|
+
self.gridLayout.addWidget(self.labelQuestionNum, 0, 2, 1, 1)
|
115
88
|
|
116
|
-
self.answerPart = QSpinBox(EquationChecker)
|
117
|
-
self.answerPart.setObjectName(u"answerPart")
|
118
89
|
|
119
|
-
self.
|
90
|
+
self.verticalLayout.addLayout(self.gridLayout)
|
120
91
|
|
121
|
-
self.
|
92
|
+
self.label_2 = QLabel(EquationChecker)
|
93
|
+
self.label_2.setObjectName(u"label_2")
|
122
94
|
|
123
|
-
self.
|
95
|
+
self.verticalLayout.addWidget(self.label_2)
|
124
96
|
|
125
|
-
self.
|
126
|
-
self.
|
97
|
+
self.equationText = QPlainTextEdit(EquationChecker)
|
98
|
+
self.equationText.setObjectName(u"equationText")
|
99
|
+
sizePolicy = QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
100
|
+
sizePolicy.setHorizontalStretch(0)
|
101
|
+
sizePolicy.setVerticalStretch(0)
|
102
|
+
sizePolicy.setHeightForWidth(self.equationText.sizePolicy().hasHeightForWidth())
|
103
|
+
self.equationText.setSizePolicy(sizePolicy)
|
104
|
+
self.equationText.setMinimumSize(QSize(0, 20))
|
105
|
+
self.equationText.setBaseSize(QSize(0, 20))
|
127
106
|
|
128
|
-
self.
|
107
|
+
self.verticalLayout.addWidget(self.equationText)
|
129
108
|
|
130
|
-
self.
|
131
|
-
self.
|
132
|
-
self.
|
133
|
-
self.
|
134
|
-
self.
|
135
|
-
|
109
|
+
self.horizontalLayout = QHBoxLayout()
|
110
|
+
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
111
|
+
self.horizontalLayout.setContentsMargins(-1, 5, -1, -1)
|
112
|
+
self.btnRunCheck = QPushButton(EquationChecker)
|
113
|
+
self.btnRunCheck.setObjectName(u"btnRunCheck")
|
114
|
+
font1 = QFont()
|
115
|
+
font1.setPointSize(12)
|
116
|
+
font1.setBold(True)
|
117
|
+
self.btnRunCheck.setFont(font1)
|
136
118
|
|
137
|
-
self.
|
119
|
+
self.horizontalLayout.addWidget(self.btnRunCheck)
|
138
120
|
|
139
|
-
self.
|
140
|
-
self.
|
141
|
-
|
142
|
-
|
143
|
-
|
121
|
+
self.lineCheckResult = QLineEdit(EquationChecker)
|
122
|
+
self.lineCheckResult.setObjectName(u"lineCheckResult")
|
123
|
+
font2 = QFont()
|
124
|
+
font2.setBold(True)
|
125
|
+
font2.setItalic(True)
|
126
|
+
self.lineCheckResult.setFont(font2)
|
127
|
+
self.lineCheckResult.setLayoutDirection(Qt.LayoutDirection.LeftToRight)
|
128
|
+
self.lineCheckResult.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
129
|
+
self.lineCheckResult.setReadOnly(True)
|
144
130
|
|
145
|
-
self.
|
131
|
+
self.horizontalLayout.addWidget(self.lineCheckResult)
|
146
132
|
|
147
|
-
self.label_5 = QLabel(EquationChecker)
|
148
|
-
self.label_5.setObjectName(u"label_5")
|
149
|
-
self.label_5.setFont(font4)
|
150
133
|
|
151
|
-
self.
|
134
|
+
self.verticalLayout.addLayout(self.horizontalLayout)
|
152
135
|
|
136
|
+
self.line = QFrame(EquationChecker)
|
137
|
+
self.line.setObjectName(u"line")
|
138
|
+
self.line.setFrameShape(QFrame.Shape.HLine)
|
139
|
+
self.line.setFrameShadow(QFrame.Shadow.Sunken)
|
153
140
|
|
154
|
-
self.verticalLayout.
|
141
|
+
self.verticalLayout.addWidget(self.line)
|
155
142
|
|
156
|
-
self.
|
143
|
+
self.tableVariables = QTableWidget(EquationChecker)
|
144
|
+
self.tableVariables.setObjectName(u"tableVariables")
|
145
|
+
sizePolicy.setHeightForWidth(self.tableVariables.sizePolicy().hasHeightForWidth())
|
146
|
+
self.tableVariables.setSizePolicy(sizePolicy)
|
147
|
+
self.tableVariables.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustToContents)
|
157
148
|
|
158
|
-
self.verticalLayout.
|
149
|
+
self.verticalLayout.addWidget(self.tableVariables)
|
159
150
|
|
160
|
-
self.
|
161
|
-
self.
|
162
|
-
self.
|
151
|
+
self.horizontalLayout_2 = QHBoxLayout()
|
152
|
+
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
|
153
|
+
self.horizontalLayout_2.setContentsMargins(-1, 5, -1, -1)
|
154
|
+
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
163
155
|
|
164
|
-
self.
|
156
|
+
self.horizontalLayout_2.addItem(self.horizontalSpacer)
|
165
157
|
|
166
|
-
self.
|
167
|
-
self.
|
168
|
-
self.line.setFrameShape(QFrame.Shape.HLine)
|
169
|
-
self.line.setFrameShadow(QFrame.Shadow.Sunken)
|
158
|
+
self.btnSave = QPushButton(EquationChecker)
|
159
|
+
self.btnSave.setObjectName(u"btnSave")
|
170
160
|
|
171
|
-
self.
|
161
|
+
self.horizontalLayout_2.addWidget(self.btnSave)
|
172
162
|
|
173
|
-
self.
|
174
|
-
self.
|
175
|
-
self.textResultsOutput = QTextEdit(EquationChecker)
|
176
|
-
self.textResultsOutput.setObjectName(u"textResultsOutput")
|
163
|
+
self.btnCancel = QPushButton(EquationChecker)
|
164
|
+
self.btnCancel.setObjectName(u"btnCancel")
|
177
165
|
|
178
|
-
self.
|
166
|
+
self.horizontalLayout_2.addWidget(self.btnCancel)
|
179
167
|
|
180
168
|
|
181
|
-
self.verticalLayout.addLayout(self.
|
169
|
+
self.verticalLayout.addLayout(self.horizontalLayout_2)
|
182
170
|
|
183
171
|
|
184
172
|
self.retranslateUi(EquationChecker)
|
@@ -188,20 +176,22 @@ class Ui_EquationChecker(object):
|
|
188
176
|
|
189
177
|
def retranslateUi(self, EquationChecker):
|
190
178
|
EquationChecker.setWindowTitle(QCoreApplication.translate("EquationChecker", u"Equation Checker", None))
|
191
|
-
self.label_2.setText(QCoreApplication.translate("EquationChecker", u"Equation:", None))
|
192
179
|
self.label_4.setText(QCoreApplication.translate("EquationChecker", u"calculated first Result", None))
|
180
|
+
self.labelPartNum.setText(QCoreApplication.translate("EquationChecker", u"Answer Part (Cloze)", None))
|
193
181
|
self.lineCalculatedRes.setText(QCoreApplication.translate("EquationChecker", u"0.00", None))
|
194
|
-
self.lineCheckResult.setText("")
|
195
|
-
self.lineCheckResult.setPlaceholderText(QCoreApplication.translate("EquationChecker", u"waiting for check", None))
|
196
|
-
self.buttonRunCheck.setText(QCoreApplication.translate("EquationChecker", u"Run Check now", None))
|
197
|
-
self.label_8.setText(QCoreApplication.translate("EquationChecker", u"Check", None))
|
198
|
-
self.label_9.setText(QCoreApplication.translate("EquationChecker", u"Variables:", None))
|
199
|
-
self.label_10.setText(QCoreApplication.translate("EquationChecker", u"Answer Part (Cloze)", None))
|
200
|
-
self.label_3.setText(QCoreApplication.translate("EquationChecker", u"firstResult from spreadsheet", None))
|
201
182
|
self.lineFirstResult.setText(QCoreApplication.translate("EquationChecker", u"0.00", None))
|
202
183
|
self.lineFirstResult.setPlaceholderText(QCoreApplication.translate("EquationChecker", u"waiting", None))
|
203
|
-
self.
|
204
|
-
|
205
|
-
self.
|
184
|
+
self.label_3.setText(QCoreApplication.translate("EquationChecker", u"firstResult from spreadsheet", None))
|
185
|
+
#if QT_CONFIG(tooltip)
|
186
|
+
self.btnFetchQst.setToolTip(QCoreApplication.translate("EquationChecker", u"Get the last selected question from the main windows list.", None))
|
187
|
+
#endif // QT_CONFIG(tooltip)
|
188
|
+
self.btnFetchQst.setText(QCoreApplication.translate("EquationChecker", u"Fetch current question", None))
|
189
|
+
self.labelQuestionNum.setText(QCoreApplication.translate("EquationChecker", u"Question: ", None))
|
190
|
+
self.label_2.setText(QCoreApplication.translate("EquationChecker", u"Edit the equation below", None))
|
191
|
+
self.btnRunCheck.setText(QCoreApplication.translate("EquationChecker", u"Check the equation now !", None))
|
192
|
+
self.lineCheckResult.setText("")
|
193
|
+
self.lineCheckResult.setPlaceholderText(QCoreApplication.translate("EquationChecker", u"waiting for check", None))
|
194
|
+
self.btnSave.setText(QCoreApplication.translate("EquationChecker", u"Save equation", None))
|
195
|
+
self.btnCancel.setText(QCoreApplication.translate("EquationChecker", u"Close", None))
|
206
196
|
# retranslateUi
|
207
197
|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
################################################################################
|
4
4
|
## Form generated from reading UI file 'UI_variableGenerator.ui'
|
5
5
|
##
|
6
|
-
## Created by: Qt User Interface Compiler version 6.9.
|
6
|
+
## Created by: Qt User Interface Compiler version 6.9.2
|
7
7
|
##
|
8
8
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
9
9
|
################################################################################
|
excel2moodle/ui/appUi.py
CHANGED
@@ -22,14 +22,12 @@ from excel2moodle import e2mMetadata, mainLogger
|
|
22
22
|
from excel2moodle.core.category import Category
|
23
23
|
from excel2moodle.core.dataStructure import QuestionDB
|
24
24
|
from excel2moodle.core.exceptions import InvalidFieldException
|
25
|
-
from excel2moodle.core.question import ParametricQuestion
|
25
|
+
from excel2moodle.core.question import ParametricQuestion, Question
|
26
26
|
from excel2moodle.core.settings import Settings, Tags
|
27
|
+
from excel2moodle.extra.equationChecker import EqCheckerWindow
|
27
28
|
from excel2moodle.extra.variableGenerator import VariableGeneratorDialog
|
28
29
|
from excel2moodle.logger import LogWindowHandler
|
29
|
-
from excel2moodle.question_types.mc import MCQuestion
|
30
|
-
from excel2moodle.question_types.nf import NFQuestion
|
31
30
|
from excel2moodle.ui import dialogs
|
32
|
-
from excel2moodle.ui.equationChecker import EqCheckerWindow
|
33
31
|
from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
|
34
32
|
from excel2moodle.ui.UI_mainWindow import Ui_MoodleTestGenerator
|
35
33
|
|
@@ -53,7 +51,8 @@ class MainWindow(QMainWindow):
|
|
53
51
|
self.ui.setupUi(self)
|
54
52
|
self.exportDialog = dialogs.ExportDialog(self)
|
55
53
|
self.questionPreview = dialogs.QuestionPreview(self)
|
56
|
-
self.eqChecker = EqCheckerWindow()
|
54
|
+
self.eqChecker = EqCheckerWindow(self)
|
55
|
+
self.eqChecker.setModal(False)
|
57
56
|
self.connectEvents()
|
58
57
|
logger.info("Settings are stored under: %s", self.qSettings.fileName())
|
59
58
|
self.ui.treeWidget.setSelectionMode(QAbstractItemView.MultiSelection)
|
@@ -155,6 +154,15 @@ class MainWindow(QMainWindow):
|
|
155
154
|
self.settings.get(Tags.QUESTIONVARIANT),
|
156
155
|
)
|
157
156
|
|
157
|
+
@property
|
158
|
+
def currentQuestion(self) -> Question | None:
|
159
|
+
"""Get the current question."""
|
160
|
+
item = self.ui.treeWidget.currentItem()
|
161
|
+
if isinstance(item, QuestionItem):
|
162
|
+
return item.question
|
163
|
+
logger.info("No Question Item selected.")
|
164
|
+
return None
|
165
|
+
|
158
166
|
@Slot()
|
159
167
|
def onSelectionChanged(self, **args) -> None:
|
160
168
|
"""Whenever the selection changes the total of selected points needs to be recalculated."""
|
@@ -166,8 +174,9 @@ class MainWindow(QMainWindow):
|
|
166
174
|
count += q.question.points
|
167
175
|
self.ui.pointCounter.setValue(count)
|
168
176
|
self.ui.questionCounter.setValue(questions)
|
169
|
-
|
170
|
-
|
177
|
+
# This would automatically update the question of the checker
|
178
|
+
# if self.eqChecker.isVisible():
|
179
|
+
# self.openEqCheckerDlg()
|
171
180
|
|
172
181
|
@Slot()
|
173
182
|
def toggleQuestionSelectionState(self, state) -> None:
|
@@ -249,11 +258,9 @@ class MainWindow(QMainWindow):
|
|
249
258
|
|
250
259
|
@Slot()
|
251
260
|
def updateQuestionPreview(self) -> None:
|
252
|
-
|
253
|
-
if
|
254
|
-
self.questionPreview.setupQuestion(
|
255
|
-
else:
|
256
|
-
logger.info("current Item is not a Question, can't preview")
|
261
|
+
question = self.currentQuestion
|
262
|
+
if question is not None:
|
263
|
+
self.questionPreview.setupQuestion(question)
|
257
264
|
|
258
265
|
def setStatus(self, status) -> None:
|
259
266
|
self.ui.statusbar.clearMessage()
|
@@ -261,17 +268,15 @@ class MainWindow(QMainWindow):
|
|
261
268
|
|
262
269
|
@Slot()
|
263
270
|
def openEqCheckerDlg(self) -> None:
|
264
|
-
|
265
|
-
if
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
self.eqChecker.setup(item.question)
|
272
|
-
self.eqChecker.show()
|
271
|
+
question = self.currentQuestion
|
272
|
+
if question is None:
|
273
|
+
return
|
274
|
+
if isinstance(question, ParametricQuestion):
|
275
|
+
logger.debug("opening wEquationChecker \n")
|
276
|
+
self.eqChecker.show()
|
277
|
+
self.eqChecker.updateQuestion()
|
273
278
|
else:
|
274
|
-
logger.debug("
|
279
|
+
logger.debug("Can't check an MC or NF Question")
|
275
280
|
|
276
281
|
@Slot()
|
277
282
|
def openAboutDlg(self) -> None:
|
@@ -286,24 +291,21 @@ class MainWindow(QMainWindow):
|
|
286
291
|
|
287
292
|
@Slot()
|
288
293
|
def openVariableGeneratorDlg(self) -> None:
|
289
|
-
|
290
|
-
if
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
)
|
301
|
-
else:
|
302
|
-
logger.warning("No variable sets were generated.")
|
294
|
+
question = self.currentQuestion
|
295
|
+
if question is None:
|
296
|
+
return
|
297
|
+
if isinstance(question, ParametricQuestion):
|
298
|
+
dialog = VariableGeneratorDialog(self, parametrics=question.parametrics)
|
299
|
+
dialog.setModal(False)
|
300
|
+
if dialog.exec():
|
301
|
+
self.questionPreview.setupQuestion(question)
|
302
|
+
self.treeRefreshCategory(question.category)
|
303
|
+
logger.info("Updated QuestionItem display for %s", question.id)
|
304
|
+
self.copyVariablesToClipboard(variables=question.parametrics.variables)
|
303
305
|
else:
|
304
|
-
logger.
|
306
|
+
logger.warning("No variable sets were generated.")
|
305
307
|
else:
|
306
|
-
logger.info("
|
308
|
+
logger.info("Selected item is not a ParametricQuestion.")
|
307
309
|
|
308
310
|
@Slot()
|
309
311
|
def copyVariablesToClipboard(
|
@@ -312,11 +314,11 @@ class MainWindow(QMainWindow):
|
|
312
314
|
if variables is None:
|
313
315
|
variables = {}
|
314
316
|
if not variables:
|
315
|
-
|
316
|
-
if
|
317
|
-
|
318
|
-
|
319
|
-
|
317
|
+
question = self.currentQuestion
|
318
|
+
if question is None:
|
319
|
+
return
|
320
|
+
if isinstance(question, ParametricQuestion):
|
321
|
+
variables = question.parametrics.variables
|
320
322
|
varsList = [
|
321
323
|
f"{name}\t{'; '.join(map(str, vals))}"
|
322
324
|
for name, vals in variables.items()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: excel2moodle
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.1
|
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
|
@@ -90,6 +90,32 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
|
|
90
90
|
|
91
91
|
# Changelogs
|
92
92
|
|
93
|
+
## 0.7.1 (2025-10-04)
|
94
|
+
feedbacking improved
|
95
|
+
|
96
|
+
### documentation (1 change)
|
97
|
+
|
98
|
+
- [documentation improvement](https://gitlab.com/jbosse3/excel2moodle/-/commit/1a1110d05b49175e049a9ca18a027216a765e277)
|
99
|
+
|
100
|
+
### feature (1 change)
|
101
|
+
|
102
|
+
- [Added MC answer feedback support](https://gitlab.com/jbosse3/excel2moodle/-/commit/4f5fe550786cf29839ba54fbdfedbf03c72d3009)
|
103
|
+
|
104
|
+
## 0.7.0 (2025-09-30)
|
105
|
+
Rework of the equation checker done!
|
106
|
+
|
107
|
+
### feature (1 change)
|
108
|
+
|
109
|
+
- [finalized equation Checker](https://gitlab.com/jbosse3/excel2moodle/-/commit/55e3891d3da56357ad00f672a86c2690d551d21c)
|
110
|
+
|
111
|
+
### documentation (1 change)
|
112
|
+
|
113
|
+
- [Documented feedback usages](https://gitlab.com/jbosse3/excel2moodle/-/commit/6f50ce023a3235b3f34b6272a4ce5057346f8751)
|
114
|
+
|
115
|
+
### improvement (1 change)
|
116
|
+
|
117
|
+
- [MainWindow got a currentQuestion property](https://gitlab.com/jbosse3/excel2moodle/-/commit/59b18e158201357b767a36dab0f300f88cb5e9ad)
|
118
|
+
|
93
119
|
## 0.6.5 (2025-09-02)
|
94
120
|
Added Scripted Media Support now with the module
|
95
121
|
|
@@ -9,34 +9,33 @@ excel2moodle/core/etHelpers.py,sha256=LzimWGuX6RH2TbfEnWUoAXT2Tr0z6P7bCANjxuANSX
|
|
9
9
|
excel2moodle/core/exceptions.py,sha256=9xfsaIcm6Yej6QAZga0d3DK3jLQejdfgJARuAaG-uZY,739
|
10
10
|
excel2moodle/core/globals.py,sha256=gvkl8Obq4XBW40B1L68Ewg06sonK27l-KIiodwFv8ic,2393
|
11
11
|
excel2moodle/core/parser.py,sha256=hjbA0i7N1oHoJHhOmvEtVl4Ryaqd0eqUS26bXS47CBo,8467
|
12
|
-
excel2moodle/core/question.py,sha256=
|
13
|
-
excel2moodle/core/settings.py,sha256=
|
12
|
+
excel2moodle/core/question.py,sha256=HQ6rC5JWfr4XF3m7ZHk1IKjjZZ_1AhvI77wqaTuq8fY,14585
|
13
|
+
excel2moodle/core/settings.py,sha256=rYGJZeXAQ4d3NKkme_YiG5uLi-cheIaAsoV6EDDGQsI,6631
|
14
14
|
excel2moodle/core/stringHelpers.py,sha256=OzFZ6Eu3PeBLKb61K-aeVfUZmVuBerr9KfyOsuNRd7Y,2403
|
15
15
|
excel2moodle/core/validator.py,sha256=6nZIyTwcXPT2jgi31QrBGur3Cq7A3Q9btLp5ALFEsKw,4998
|
16
16
|
excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
|
17
|
-
excel2moodle/extra/
|
17
|
+
excel2moodle/extra/equationChecker.py,sha256=oNsysXieZZNJYuA8E-kQYdWxKuzNRRCVWHogy64yORo,5581
|
18
18
|
excel2moodle/extra/scriptCaller.py,sha256=nJsUxz0EzoChobCJbAbfS8NSMghdUFV6oEPdO6J6KlU,850
|
19
19
|
excel2moodle/extra/updateQuery.py,sha256=kD_L23Qea9Cx4zUwfQVNJXXFbybd9cwE8sSbZrz7VF8,1554
|
20
|
-
excel2moodle/extra/variableGenerator.py,sha256=
|
20
|
+
excel2moodle/extra/variableGenerator.py,sha256=jcXbyA1B3-uZHsovU9QhZ879FoemeHqAe_BxPT4ZXoY,11484
|
21
21
|
excel2moodle/question_types/__init__.py,sha256=81mss0g7SVtnlb-WkydE28G_dEAAf6oT1uB8lpK2-II,1041
|
22
|
-
excel2moodle/question_types/cloze.py,sha256=
|
23
|
-
excel2moodle/question_types/mc.py,sha256=
|
22
|
+
excel2moodle/question_types/cloze.py,sha256=WGeA37Dq405Zda_WNU2SrAhfW9DNjkdnLBfM-JFM4lw,14656
|
23
|
+
excel2moodle/question_types/mc.py,sha256=qslqLRfqQuo_57IFXFMDGD4IJSCL3Ig29rppvKTplgc,7103
|
24
24
|
excel2moodle/question_types/nf.py,sha256=HAolGy13-FbLVJskAUXCFy76NJu91IG9wtq6OI05Igw,1374
|
25
25
|
excel2moodle/question_types/nfm.py,sha256=D5-aE4C7TAuwHFidXR15DLWNZ4JT-HVbPXI0CzGWOS0,3013
|
26
|
-
excel2moodle/ui/UI_equationChecker.py,sha256=
|
26
|
+
excel2moodle/ui/UI_equationChecker.py,sha256=jA5gKemrgeertbp2iip-icQCRFKnV-dR-f0eODBBAp0,8910
|
27
27
|
excel2moodle/ui/UI_exportSettingsDialog.py,sha256=I0Vqw2TCWoUhDKxTgLoGaAo4_L77vfN8G7_zi7b_5lY,8254
|
28
28
|
excel2moodle/ui/UI_mainWindow.py,sha256=9w8bRgOrVEX7BRGQvMuVhPCiSOsXYkMb4rxLDeRErII,21544
|
29
29
|
excel2moodle/ui/UI_updateDlg.py,sha256=kPv6XyyTqKs2Avsji9peVUrsp0di4B41mXI0Qz_uLfA,5261
|
30
|
-
excel2moodle/ui/UI_variableGenerator.py,sha256=
|
30
|
+
excel2moodle/ui/UI_variableGenerator.py,sha256=9sUrlZN3IMIlLYYrYtvTNK1iV06iPFPznd3DtOBzQao,11205
|
31
31
|
excel2moodle/ui/UI_variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
|
32
32
|
excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
|
33
|
-
excel2moodle/ui/appUi.py,sha256=
|
33
|
+
excel2moodle/ui/appUi.py,sha256=jlurK6sb_CVEV1vNAC_mL8lmX9H9Ex-QZ3BQXgAisJM,14887
|
34
34
|
excel2moodle/ui/dialogs.py,sha256=du6v17lh6LhgDDK0QltPzD-z8wUn3aD4QzaAQBmiTBQ,7314
|
35
|
-
excel2moodle/ui/equationChecker.py,sha256=RII9DlZAlHqe5udBWzeUaozhtyi3ZkCZs8h3-oO6pEw,2700
|
36
35
|
excel2moodle/ui/treewidget.py,sha256=3hZRLlrhp4FMXFyNY0LGDy7k1RSuKH87QyqB1N4qOqg,2335
|
37
|
-
excel2moodle-0.
|
38
|
-
excel2moodle-0.
|
39
|
-
excel2moodle-0.
|
40
|
-
excel2moodle-0.
|
41
|
-
excel2moodle-0.
|
42
|
-
excel2moodle-0.
|
36
|
+
excel2moodle-0.7.1.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
|
37
|
+
excel2moodle-0.7.1.dist-info/METADATA,sha256=mAWPtJes2MEC75ot_dMaxGjC_pmFTwanHfh-B8Z8Mbg,13065
|
38
|
+
excel2moodle-0.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
39
|
+
excel2moodle-0.7.1.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
|
40
|
+
excel2moodle-0.7.1.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
|
41
|
+
excel2moodle-0.7.1.dist-info/RECORD,,
|
@@ -1,126 +0,0 @@
|
|
1
|
-
"""Script for verifying the equations written into the ``result`` field of NFM-type Question.
|
2
|
-
|
3
|
-
This script does two things.
|
4
|
-
|
5
|
-
#. It calculates all the answers obtained from the series of variables.
|
6
|
-
#. It compares the calculation of the first answer to the ``firstResult`` field.
|
7
|
-
|
8
|
-
Usage
|
9
|
-
=====
|
10
|
-
|
11
|
-
From the main UI
|
12
|
-
----------------
|
13
|
-
|
14
|
-
#. Start this tool from the top bar in the main Window under the *Tools* section
|
15
|
-
#. A new window will open inside you:
|
16
|
-
#. Enter the Number of the Category
|
17
|
-
#. Enter the Number of the Question
|
18
|
-
#. Click on ``Run check now`` and Inspect the results
|
19
|
-
#. Rinse and repeat
|
20
|
-
|
21
|
-
As Script
|
22
|
-
---------
|
23
|
-
|
24
|
-
#. Start this script with ``py -m excel2moodle.extra.equationVerification`` inside the top-level Directory
|
25
|
-
#. Enter the Number of the Category
|
26
|
-
#. Enter the Number of the Question
|
27
|
-
#. Inspect the results
|
28
|
-
#. Rinse and repeat
|
29
|
-
"""
|
30
|
-
|
31
|
-
from pathlib import Path
|
32
|
-
|
33
|
-
import pandas as pd
|
34
|
-
|
35
|
-
# Hier Bitte die Frage angeben, die getestet Werden soll:
|
36
|
-
|
37
|
-
# ===========================================================
|
38
|
-
|
39
|
-
|
40
|
-
def checkResult(checkerValue: float, calculation: float, tolerance=0.01) -> bool:
|
41
|
-
"""Checks if the two Arguments are within the tolerance the same value.
|
42
|
-
|
43
|
-
:param checkerValue: the value the calculation is compared against
|
44
|
-
:type checkerValue: fleat
|
45
|
-
:param calculation: the value to be compared against checker Value
|
46
|
-
:type calculation: float
|
47
|
-
:param tolerance: the standart tolerance is 0.01 -> 1%
|
48
|
-
:type tolerance: float, optional
|
49
|
-
|
50
|
-
:returns:
|
51
|
-
True if checkerValue == calculation
|
52
|
-
False if checkerValue != calculation
|
53
|
-
:rtype: bool
|
54
|
-
"""
|
55
|
-
upper = abs(checkerValue + checkerValue * tolerance)
|
56
|
-
lower = abs(checkerValue - checkerValue * tolerance)
|
57
|
-
return bool(abs(calculation) > lower and abs(calculation) < upper)
|
58
|
-
|
59
|
-
|
60
|
-
def equationChecker(
|
61
|
-
categoryName: str, qNumber: int, spreadsheetFile
|
62
|
-
) -> tuple[list[str], list[float], float]:
|
63
|
-
"""This Function calculates all Results an invokes the checkResult function.
|
64
|
-
|
65
|
-
Parameters
|
66
|
-
----------
|
67
|
-
categoryName : str
|
68
|
-
The category in which the question to be tested is
|
69
|
-
This must match a sheet name of the spreadsheet
|
70
|
-
qNumber : int
|
71
|
-
The number of the question which results are tested
|
72
|
-
spreadsheetFile : Path
|
73
|
-
The Filepath to the spreadsheet
|
74
|
-
|
75
|
-
Returns
|
76
|
-
-------
|
77
|
-
bulletPointsString : list[str]
|
78
|
-
The string list with the bullet points, with numeric values instead of variables
|
79
|
-
results : list[str]
|
80
|
-
The list with the calculated results
|
81
|
-
checkerValue : float
|
82
|
-
The value taken from the ``firstResult`` field
|
83
|
-
|
84
|
-
"""
|
85
|
-
spreadsheetFile.resolve()
|
86
|
-
df = pd.read_excel(spreadsheetFile, sheet_name=categoryName, index_col=0)
|
87
|
-
eq = df.loc["result"][qNumber]
|
88
|
-
bps = df.loc["bulletPoints"][qNumber]
|
89
|
-
try:
|
90
|
-
res = float(df.loc["firstResult"][qNumber])
|
91
|
-
except Exception:
|
92
|
-
res = 0
|
93
|
-
bps, calcs = nmq.parseNumericMultiQuestion(df, bps, eq, qNumber)
|
94
|
-
return bps, calcs, res
|
95
|
-
|
96
|
-
|
97
|
-
def main(
|
98
|
-
spreadsheetFile=Path("../Fragensammlung/Main_question_all.xlsx"),
|
99
|
-
catN=None,
|
100
|
-
qNumber=None,
|
101
|
-
) -> None:
|
102
|
-
"""Takes the Spreadsheet, and asks for a category and a question number."""
|
103
|
-
if catN is None:
|
104
|
-
catN = input("Geben Sie die Kategorie an: KAT_")
|
105
|
-
categoryName = f"KAT_{catN}"
|
106
|
-
if qNumber is None:
|
107
|
-
qNumber = int(input("Geben Sie die Fragennummer an: "))
|
108
|
-
bullets, results, firstResult = equationChecker(
|
109
|
-
categoryName, qNumber, spreadsheetFile=spreadsheetFile
|
110
|
-
)
|
111
|
-
check = False
|
112
|
-
|
113
|
-
for i, calculation in enumerate(results):
|
114
|
-
if i == 0 and firstResult != 0:
|
115
|
-
check = checkResult(firstResult, calculation)
|
116
|
-
|
117
|
-
if check:
|
118
|
-
pass
|
119
|
-
else:
|
120
|
-
pass
|
121
|
-
|
122
|
-
|
123
|
-
if __name__ == "__main__":
|
124
|
-
spreadsheet = input("Geben Sie den Pfad zu dem spreadsheet an:")
|
125
|
-
while True:
|
126
|
-
main(Path(spreadsheet))
|
@@ -1,70 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
import math
|
3
|
-
from pathlib import Path
|
4
|
-
|
5
|
-
from PySide6.QtWidgets import QWidget
|
6
|
-
|
7
|
-
from excel2moodle import mainLogger
|
8
|
-
from excel2moodle.core.question import ParametricQuestion
|
9
|
-
from excel2moodle.core.settings import Tags
|
10
|
-
from excel2moodle.logger import LogWindowHandler
|
11
|
-
from excel2moodle.question_types.nfm import NFMQuestionParser
|
12
|
-
|
13
|
-
from .UI_equationChecker import Ui_EquationChecker
|
14
|
-
|
15
|
-
logger = logging.getLogger(__name__)
|
16
|
-
|
17
|
-
loggerSignal = LogWindowHandler()
|
18
|
-
mainLogger.addHandler(loggerSignal)
|
19
|
-
|
20
|
-
|
21
|
-
class EqCheckerWindow(QWidget):
|
22
|
-
def __init__(self) -> None:
|
23
|
-
super().__init__()
|
24
|
-
self.excelFile = Path()
|
25
|
-
self.ui = Ui_EquationChecker()
|
26
|
-
self.ui.setupUi(self)
|
27
|
-
self.ui.buttonRunCheck.clicked.connect(
|
28
|
-
lambda: self.updateCalculation(),
|
29
|
-
)
|
30
|
-
self.question: ParametricQuestion
|
31
|
-
|
32
|
-
def updateCalculation(self) -> None:
|
33
|
-
equation = self.ui.equationText.toPlainText()
|
34
|
-
results: list[float] = []
|
35
|
-
firstResult = self.question.rawData.get(Tags.FIRSTRESULT)
|
36
|
-
for i in range(self.question.variants):
|
37
|
-
NFMQuestionParser.setupAstIntprt(self.question.variables, i)
|
38
|
-
results.append(float(NFMQuestionParser.astEval(equation)))
|
39
|
-
check: bool = False
|
40
|
-
self.ui.textResultsOutput.insertHtml(f"<hr><br><h2>{equation:^30}</h2><br>")
|
41
|
-
for i, calculation in enumerate(results):
|
42
|
-
if i == 0 and firstResult != 0:
|
43
|
-
check = bool(math.isclose(calculation, firstResult, rel_tol=0.01))
|
44
|
-
self.ui.lineCalculatedRes.setText(f"{calculation}")
|
45
|
-
self.ui.textResultsOutput.append(f"<h3>Result {check = }</h3><br>")
|
46
|
-
self.ui.textResultsOutput.append(
|
47
|
-
f"Ergebnis {i + 1}: \t{calculation}",
|
48
|
-
)
|
49
|
-
if check:
|
50
|
-
self.ui.lineCheckResult.setText("[OK]")
|
51
|
-
logger.info(
|
52
|
-
" [OK] The first calculated result matches 'firstResult'",
|
53
|
-
)
|
54
|
-
else:
|
55
|
-
self.ui.lineCheckResult.setText("[ERROR]")
|
56
|
-
logger.warning(
|
57
|
-
"The first calculated result does not match 'firstResult'",
|
58
|
-
)
|
59
|
-
|
60
|
-
def setup(self, question: ParametricQuestion) -> None:
|
61
|
-
self.question = question
|
62
|
-
self.ui.textResultsOutput.clear()
|
63
|
-
self.ui.equationText.clear()
|
64
|
-
bullets = question.rawData.get(Tags.BPOINTS)
|
65
|
-
firstResult = question.rawData.get(Tags.FIRSTRESULT)
|
66
|
-
self.ui.lineFirstResult.setText(str(firstResult))
|
67
|
-
self.ui.equationText.appendPlainText(question.rawData.get(Tags.EQUATION))
|
68
|
-
for bullet in bullets:
|
69
|
-
self.ui.textResultsOutput.append(bullet)
|
70
|
-
self.updateCalculation()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|