excel2moodle 0.6.4__tar.gz → 0.7.0__tar.gz
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-0.6.4 → excel2moodle-0.7.0}/PKG-INFO +21 -1
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/README.md +20 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/question.py +8 -3
- excel2moodle-0.7.0/excel2moodle/extra/equationChecker.py +141 -0
- excel2moodle-0.7.0/excel2moodle/extra/scriptCaller.py +27 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/extra/variableGenerator.py +1 -2
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/question_types/cloze.py +1 -1
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_equationChecker.py +105 -115
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_variableGenerator.py +1 -1
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/appUi.py +45 -43
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/PKG-INFO +21 -1
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/SOURCES.txt +2 -2
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/pyproject.toml +1 -1
- excel2moodle-0.6.4/excel2moodle/extra/equationVerification.py +0 -126
- excel2moodle-0.6.4/excel2moodle/ui/equationChecker.py +0 -70
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/LICENSE +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/__init__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/__main__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/__init__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/bullets.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/category.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/dataStructure.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/etHelpers.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/exceptions.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/globals.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/parser.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/settings.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/stringHelpers.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/core/validator.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/extra/__init__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/extra/updateQuery.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/logger.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/question_types/__init__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/question_types/mc.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/question_types/nf.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/question_types/nfm.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_exportSettingsDialog.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_mainWindow.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_updateDlg.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/UI_variantDialog.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/__init__.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/dialogs.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle/ui/treewidget.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/dependency_links.txt +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/entry_points.txt +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/requires.txt +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/excel2moodle.egg-info/top_level.txt +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/setup.cfg +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_bullets.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_feedbacking.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_nfmParsing.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_parseQuestion.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_picture.py +0 -0
- {excel2moodle-0.6.4 → excel2moodle-0.7.0}/test/test_questionDataGet.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: excel2moodle
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
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,26 @@ 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.0 (2025-09-30)
|
94
|
+
Rework of the equation checker done!
|
95
|
+
|
96
|
+
### feature (1 change)
|
97
|
+
|
98
|
+
- [finalized equation Checker](https://gitlab.com/jbosse3/excel2moodle/-/commit/55e3891d3da56357ad00f672a86c2690d551d21c)
|
99
|
+
|
100
|
+
### documentation (1 change)
|
101
|
+
|
102
|
+
- [Documented feedback usages](https://gitlab.com/jbosse3/excel2moodle/-/commit/6f50ce023a3235b3f34b6272a4ce5057346f8751)
|
103
|
+
|
104
|
+
### improvement (1 change)
|
105
|
+
|
106
|
+
- [MainWindow got a currentQuestion property](https://gitlab.com/jbosse3/excel2moodle/-/commit/59b18e158201357b767a36dab0f300f88cb5e9ad)
|
107
|
+
|
108
|
+
## 0.6.5 (2025-09-02)
|
109
|
+
Added Scripted Media Support now with the module
|
110
|
+
|
111
|
+
No changes.
|
112
|
+
|
93
113
|
## 0.6.4 (2025-09-02)
|
94
114
|
Added Scripted Media Support
|
95
115
|
|
@@ -68,6 +68,26 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
|
|
68
68
|
|
69
69
|
# Changelogs
|
70
70
|
|
71
|
+
## 0.7.0 (2025-09-30)
|
72
|
+
Rework of the equation checker done!
|
73
|
+
|
74
|
+
### feature (1 change)
|
75
|
+
|
76
|
+
- [finalized equation Checker](https://gitlab.com/jbosse3/excel2moodle/-/commit/55e3891d3da56357ad00f672a86c2690d551d21c)
|
77
|
+
|
78
|
+
### documentation (1 change)
|
79
|
+
|
80
|
+
- [Documented feedback usages](https://gitlab.com/jbosse3/excel2moodle/-/commit/6f50ce023a3235b3f34b6272a4ce5057346f8751)
|
81
|
+
|
82
|
+
### improvement (1 change)
|
83
|
+
|
84
|
+
- [MainWindow got a currentQuestion property](https://gitlab.com/jbosse3/excel2moodle/-/commit/59b18e158201357b767a36dab0f300f88cb5e9ad)
|
85
|
+
|
86
|
+
## 0.6.5 (2025-09-02)
|
87
|
+
Added Scripted Media Support now with the module
|
88
|
+
|
89
|
+
No changes.
|
90
|
+
|
71
91
|
## 0.6.4 (2025-09-02)
|
72
92
|
Added Scripted Media Support
|
73
93
|
|
@@ -250,7 +250,7 @@ class Parametrics:
|
|
250
250
|
self.equations: dict[int, str] = (
|
251
251
|
equation if isinstance(equation, dict) else {1: equation}
|
252
252
|
)
|
253
|
-
self.
|
253
|
+
self.resultChecker: dict[int, float] = (
|
254
254
|
firstResult if isinstance(firstResult, dict) else {1: firstResult}
|
255
255
|
)
|
256
256
|
self.id = identifier
|
@@ -258,6 +258,11 @@ class Parametrics:
|
|
258
258
|
self.results: dict[int, list[float]] = {}
|
259
259
|
self.logger = LogAdapterQuestionID(loggerObj, {"qID": self.id})
|
260
260
|
|
261
|
+
def setEquation(self, number: int, equation: str) -> None:
|
262
|
+
"""Set the new equation and update all the calculations."""
|
263
|
+
self.equations[number] = equation
|
264
|
+
self._calculateResults()
|
265
|
+
|
261
266
|
@property
|
262
267
|
def variants(self) -> int:
|
263
268
|
number = 1000
|
@@ -315,12 +320,12 @@ class Parametrics:
|
|
315
320
|
result,
|
316
321
|
)
|
317
322
|
if variant == 0 and not math.isclose(
|
318
|
-
result, self.
|
323
|
+
result, self.resultChecker[num], rel_tol=0.01
|
319
324
|
):
|
320
325
|
self.logger.warning(
|
321
326
|
"The calculated result %s differs from given firstResult: %s",
|
322
327
|
result,
|
323
|
-
self.
|
328
|
+
self.resultChecker,
|
324
329
|
)
|
325
330
|
self.logger.debug(
|
326
331
|
"Calculated result %s for variant %s", result, variant
|
@@ -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
|
+
)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import logging
|
2
|
+
import re
|
3
|
+
|
4
|
+
import lxml.etree as ET
|
5
|
+
|
6
|
+
from excel2moodle.core.question import Parametrics
|
7
|
+
|
8
|
+
loggerObj = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
class MediaCall:
|
12
|
+
def __init__(self, callString: str, divId: str) -> None:
|
13
|
+
self.call: str = re.sub(r"\(", f'("{divId}", ', callString)
|
14
|
+
self._scriptElem = ET.Element("script")
|
15
|
+
self._scriptElem.text = callString
|
16
|
+
self.divId = divId
|
17
|
+
|
18
|
+
@property
|
19
|
+
def element(self) -> ET.Element:
|
20
|
+
return self._scriptElem
|
21
|
+
|
22
|
+
def update(self, parametrics: Parametrics, variant: int = 0) -> None:
|
23
|
+
updatedCall = self.call
|
24
|
+
for var, val in parametrics.variables.items():
|
25
|
+
updatedCall = re.sub(rf",\s+{var}\s*", f", {val[variant]}", updatedCall)
|
26
|
+
self._scriptElem.text = updatedCall
|
27
|
+
loggerObj.info("Inserted function call: %s", updatedCall)
|
@@ -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"):
|
@@ -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
|
################################################################################
|
@@ -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.0
|
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,26 @@ 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.0 (2025-09-30)
|
94
|
+
Rework of the equation checker done!
|
95
|
+
|
96
|
+
### feature (1 change)
|
97
|
+
|
98
|
+
- [finalized equation Checker](https://gitlab.com/jbosse3/excel2moodle/-/commit/55e3891d3da56357ad00f672a86c2690d551d21c)
|
99
|
+
|
100
|
+
### documentation (1 change)
|
101
|
+
|
102
|
+
- [Documented feedback usages](https://gitlab.com/jbosse3/excel2moodle/-/commit/6f50ce023a3235b3f34b6272a4ce5057346f8751)
|
103
|
+
|
104
|
+
### improvement (1 change)
|
105
|
+
|
106
|
+
- [MainWindow got a currentQuestion property](https://gitlab.com/jbosse3/excel2moodle/-/commit/59b18e158201357b767a36dab0f300f88cb5e9ad)
|
107
|
+
|
108
|
+
## 0.6.5 (2025-09-02)
|
109
|
+
Added Scripted Media Support now with the module
|
110
|
+
|
111
|
+
No changes.
|
112
|
+
|
93
113
|
## 0.6.4 (2025-09-02)
|
94
114
|
Added Scripted Media Support
|
95
115
|
|
@@ -23,7 +23,8 @@ excel2moodle/core/settings.py
|
|
23
23
|
excel2moodle/core/stringHelpers.py
|
24
24
|
excel2moodle/core/validator.py
|
25
25
|
excel2moodle/extra/__init__.py
|
26
|
-
excel2moodle/extra/
|
26
|
+
excel2moodle/extra/equationChecker.py
|
27
|
+
excel2moodle/extra/scriptCaller.py
|
27
28
|
excel2moodle/extra/updateQuery.py
|
28
29
|
excel2moodle/extra/variableGenerator.py
|
29
30
|
excel2moodle/question_types/__init__.py
|
@@ -40,7 +41,6 @@ excel2moodle/ui/UI_variantDialog.py
|
|
40
41
|
excel2moodle/ui/__init__.py
|
41
42
|
excel2moodle/ui/appUi.py
|
42
43
|
excel2moodle/ui/dialogs.py
|
43
|
-
excel2moodle/ui/equationChecker.py
|
44
44
|
excel2moodle/ui/treewidget.py
|
45
45
|
test/test_bullets.py
|
46
46
|
test/test_feedbacking.py
|
@@ -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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|