excel2moodle 0.5.2__py3-none-any.whl → 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
excel2moodle/ui/appUi.py CHANGED
@@ -7,13 +7,23 @@ It needs to be seperated from ``windowMain.py`` because that file will be change
7
7
  import logging
8
8
  from pathlib import Path
9
9
 
10
- from PySide6 import QtCore, QtWidgets
11
- from PySide6.QtCore import QRunnable, QSettings, Qt, QThreadPool
10
+ from PySide6.QtCore import QRunnable, QSettings, Qt, QThreadPool, QUrl, Slot
11
+ from PySide6.QtGui import QDesktopServices
12
+ from PySide6.QtWidgets import (
13
+ QAbstractItemView,
14
+ QApplication,
15
+ QFileDialog,
16
+ QHeaderView,
17
+ QMainWindow,
18
+ QMessageBox,
19
+ )
12
20
 
13
- from excel2moodle import mainLogger
21
+ from excel2moodle import e2mMetadata, mainLogger
14
22
  from excel2moodle.core.category import Category
15
23
  from excel2moodle.core.dataStructure import QuestionDB
24
+ from excel2moodle.core.question import ParametricQuestion
16
25
  from excel2moodle.core.settings import Settings, Tags
26
+ from excel2moodle.extra.variableGenerator import VariableGeneratorDialog
17
27
  from excel2moodle.logger import LogWindowHandler
18
28
  from excel2moodle.question_types.mc import MCQuestion
19
29
  from excel2moodle.question_types.nf import NFQuestion
@@ -21,7 +31,6 @@ from excel2moodle.ui import dialogs
21
31
  from excel2moodle.ui.equationChecker import EqCheckerWindow
22
32
  from excel2moodle.ui.treewidget import CategoryItem, QuestionItem
23
33
  from excel2moodle.ui.UI_mainWindow import Ui_MoodleTestGenerator
24
- from excel2moodle.ui.windowDoc import DocumentationWindow
25
34
 
26
35
  logger = logging.getLogger(__name__)
27
36
 
@@ -29,7 +38,7 @@ loggerSignal = LogWindowHandler()
29
38
  mainLogger.addHandler(loggerSignal)
30
39
 
31
40
 
32
- class MainWindow(QtWidgets.QMainWindow):
41
+ class MainWindow(QMainWindow):
33
42
  def __init__(self, settings: Settings, testDB: QuestionDB) -> None:
34
43
  super().__init__()
35
44
  self.settings = settings
@@ -47,9 +56,9 @@ class MainWindow(QtWidgets.QMainWindow):
47
56
  self.eqChecker = EqCheckerWindow()
48
57
  self.connectEvents()
49
58
  logger.info("Settings are stored under: %s", self.qSettings.fileName())
50
- self.ui.treeWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
59
+ self.ui.treeWidget.setSelectionMode(QAbstractItemView.MultiSelection)
51
60
  self.ui.treeWidget.header().setSectionResizeMode(
52
- QtWidgets.QHeaderView.ResizeToContents,
61
+ QHeaderView.ResizeToContents,
53
62
  )
54
63
  self.ui.pointCounter.setReadOnly(True)
55
64
  self.ui.questionCounter.setReadOnly(True)
@@ -92,8 +101,13 @@ class MainWindow(QtWidgets.QMainWindow):
92
101
  self.ui.treeWidget.itemClicked.connect(self.updateQuestionPreview)
93
102
  self.ui.actionAbout.triggered.connect(self.openAboutDlg)
94
103
  self.ui.actionDocumentation.triggered.connect(self.openDocumentation)
104
+ self.ui.actionGenerateVariables.triggered.connect(self.openVariableGeneratorDlg)
105
+ self.ui.actionCopyVariables.triggered.connect(self.copyVariablesToClipboard)
106
+ self.ui.actionOpenSpreadsheetExternal.triggered.connect(
107
+ self.openSpreadsheetExternally
108
+ )
95
109
 
96
- @QtCore.Slot()
110
+ @Slot()
97
111
  def parseSpreadsheetAll(self) -> None:
98
112
  """Event triggered by the *Tools/Parse all Questions* Event.
99
113
 
@@ -134,7 +148,7 @@ class MainWindow(QtWidgets.QMainWindow):
134
148
  self.settings.get(Tags.QUESTIONVARIANT),
135
149
  )
136
150
 
137
- @QtCore.Slot()
151
+ @Slot()
138
152
  def onSelectionChanged(self, **args) -> None:
139
153
  """Whenever the selection changes the total of selected points needs to be recalculated."""
140
154
  count: int = 0
@@ -148,7 +162,7 @@ class MainWindow(QtWidgets.QMainWindow):
148
162
  if self.eqChecker.isVisible():
149
163
  self.openEqCheckerDlg()
150
164
 
151
- @QtCore.Slot()
165
+ @Slot()
152
166
  def toggleQuestionSelectionState(self, state) -> None:
153
167
  setter = state == Qt.Checked
154
168
  root = self.ui.treeWidget.invisibleRootItem()
@@ -158,7 +172,7 @@ class MainWindow(QtWidgets.QMainWindow):
158
172
  for q in range(qs):
159
173
  root.child(i).child(q).setSelected(setter)
160
174
 
161
- @QtCore.Slot()
175
+ @Slot()
162
176
  def onButGenTest(self) -> None:
163
177
  """Open a file Dialog so the export file may be choosen."""
164
178
  selection: list[QuestionItem] = self.ui.treeWidget.selectedItems()
@@ -180,9 +194,9 @@ class MainWindow(QtWidgets.QMainWindow):
180
194
  else:
181
195
  logger.info("Aborting Export")
182
196
 
183
- @QtCore.Slot()
197
+ @Slot()
184
198
  def actionSpreadsheet(self) -> None:
185
- file = QtWidgets.QFileDialog.getOpenFileName(
199
+ file = QFileDialog.getOpenFileName(
186
200
  self,
187
201
  self.tr("Open Spreadsheet"),
188
202
  dir=str(self.mainPath),
@@ -192,7 +206,7 @@ class MainWindow(QtWidgets.QMainWindow):
192
206
  path = Path(file[0]).resolve(strict=True)
193
207
  self.setSheetPath(path)
194
208
 
195
- @QtCore.Slot(Category)
209
+ @Slot(Category)
196
210
  def treeRefreshCategory(self, cat: Category) -> None:
197
211
  """Append Category with its Questions to the treewidget."""
198
212
  catItem = CategoryItem(self.ui.treeWidget, cat)
@@ -201,7 +215,7 @@ class MainWindow(QtWidgets.QMainWindow):
201
215
  QuestionItem(catItem, q)
202
216
  self.ui.treeWidget.sortItems(0, Qt.SortOrder.AscendingOrder)
203
217
 
204
- @QtCore.Slot()
218
+ @Slot()
205
219
  def updateQuestionPreview(self) -> None:
206
220
  item = self.ui.treeWidget.currentItem()
207
221
  if isinstance(item, QuestionItem):
@@ -213,7 +227,7 @@ class MainWindow(QtWidgets.QMainWindow):
213
227
  self.ui.statusbar.clearMessage()
214
228
  self.ui.statusbar.showMessage(self.tr(status))
215
229
 
216
- @QtCore.Slot()
230
+ @Slot()
217
231
  def openEqCheckerDlg(self) -> None:
218
232
  item = self.ui.treeWidget.currentItem()
219
233
  if isinstance(item, QuestionItem):
@@ -227,18 +241,71 @@ class MainWindow(QtWidgets.QMainWindow):
227
241
  else:
228
242
  logger.debug("No Question Item selected: %s", type(item))
229
243
 
230
- @QtCore.Slot()
244
+ @Slot()
231
245
  def openAboutDlg(self) -> None:
232
246
  about = dialogs.AboutDialog(self)
233
247
  about.exec()
234
248
 
235
- @QtCore.Slot()
249
+ @Slot()
236
250
  def openDocumentation(self) -> None:
237
- if hasattr(self, "documentationWindow"):
238
- self.documentationWindow.show()
251
+ url = QUrl(e2mMetadata["documentation"])
252
+ QDesktopServices.openUrl(url)
253
+
254
+ @Slot()
255
+ def openVariableGeneratorDlg(self) -> None:
256
+ item = self.ui.treeWidget.currentItem()
257
+ if isinstance(item, QuestionItem):
258
+ question = item.getQuestion()
259
+ if isinstance(question, ParametricQuestion):
260
+ dialog = VariableGeneratorDialog(self, parametrics=question.parametrics)
261
+ if dialog.exec():
262
+ self.questionPreview.setupQuestion(question)
263
+ logger.info("Updated QuestionItem display for %s", question.id)
264
+ self.copyVariablesToClipboard(
265
+ variables=question.parametrics.variables
266
+ )
267
+ else:
268
+ logger.warning("No variable sets were generated.")
269
+ else:
270
+ logger.info("Selected item is not a ParametricQuestion.")
239
271
  else:
240
- self.documentationWindow = DocumentationWindow(self)
241
- self.documentationWindow.show()
272
+ logger.info("No Question Item selected.")
273
+
274
+ @Slot()
275
+ def copyVariablesToClipboard(
276
+ self, variables: dict[str, list[float | int]] | None = None
277
+ ) -> None:
278
+ if variables is None:
279
+ variables = {}
280
+ if not variables:
281
+ item = self.ui.treeWidget.currentItem()
282
+ if isinstance(item, QuestionItem):
283
+ question = item.getQuestion()
284
+ if isinstance(question, ParametricQuestion):
285
+ variables = question.parametrics.variables
286
+ varsList = [
287
+ f"{name}\t{'; '.join(map(str, vals))}"
288
+ for name, vals in variables.items()
289
+ ]
290
+ clipb = QApplication.clipboard()
291
+ variablesStr = "\n".join(varsList)
292
+ clipb.setText(variablesStr)
293
+ logger.info("Copied all variables to the clipboard")
294
+ QMessageBox.information(
295
+ self,
296
+ "Variables Copied.",
297
+ """All variables from the parametric Question are saved to the system clipboard.\n
298
+ You can paste them into the spreadsheet.
299
+ Make sure to import them with 'Tab' as the seperator.""",
300
+ )
301
+
302
+ @Slot()
303
+ def openSpreadsheetExternally(self) -> None:
304
+ if self.excelPath is None:
305
+ return
306
+ spreadsheetPath = QUrl(f"file://{self.excelPath.absolute()}")
307
+ logger.info("Opening: %s", spreadsheetPath)
308
+ QDesktopServices.openUrl(spreadsheetPath)
242
309
 
243
310
 
244
311
  class ParseAllThread(QRunnable):
@@ -252,7 +319,7 @@ class ParseAllThread(QRunnable):
252
319
  self.testDB = questionDB
253
320
  self.mainApp = mainApp
254
321
 
255
- @QtCore.Slot()
322
+ @Slot()
256
323
  def run(self) -> None:
257
324
  self.testDB.readCategoriesMetadata()
258
325
  self.testDB.asyncInitAllCategories(self.mainApp.excelPath)
@@ -5,12 +5,13 @@ from pathlib import Path
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  import lxml.etree as ET
8
- from PySide6 import QtGui, QtWidgets
9
- from PySide6.QtSvgWidgets import QGraphicsSvgItem
8
+ from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox, QWidget
10
9
 
11
10
  from excel2moodle import e2mMetadata
12
11
  from excel2moodle.core.globals import XMLTags
13
- from excel2moodle.core.question import Question
12
+ from excel2moodle.core.question import ParametricQuestion, Question
13
+ from excel2moodle.core.settings import Tags
14
+ from excel2moodle.extra import variableGenerator
14
15
  from excel2moodle.ui.UI_exportSettingsDialog import Ui_ExportDialog
15
16
  from excel2moodle.ui.UI_variantDialog import Ui_Dialog
16
17
 
@@ -20,11 +21,11 @@ if TYPE_CHECKING:
20
21
  logger = logging.getLogger(__name__)
21
22
 
22
23
 
23
- class QuestionVariantDialog(QtWidgets.QDialog):
24
- def __init__(self, parent, question: Question) -> None:
24
+ class QuestionVariantDialog(QDialog):
25
+ def __init__(self, parent, question: ParametricQuestion) -> None:
25
26
  super().__init__(parent)
26
27
  self.setWindowTitle("Question Variant Dialog")
27
- self.maxVal = question.variants
28
+ self.maxVal = question.parametrics.variants
28
29
  self.ui = Ui_Dialog()
29
30
  self.ui.setupUi(self)
30
31
  self.ui.spinBox.setRange(1, self.maxVal)
@@ -41,7 +42,7 @@ class QuestionVariantDialog(QtWidgets.QDialog):
41
42
  return self.ui.checkBox.isChecked()
42
43
 
43
44
 
44
- class ExportDialog(QtWidgets.QDialog):
45
+ class ExportDialog(QDialog):
45
46
  def __init__(self, parent) -> None:
46
47
  super().__init__(parent)
47
48
  self.setWindowTitle("Export question Selection")
@@ -62,7 +63,7 @@ class ExportDialog(QtWidgets.QDialog):
62
63
  )
63
64
 
64
65
  def getExportFile(self) -> None:
65
- path = QtWidgets.QFileDialog.getSaveFileName(
66
+ path = QFileDialog.getSaveFileName(
66
67
  self,
67
68
  "Select Output File",
68
69
  dir=str(self.exportFile),
@@ -83,85 +84,51 @@ class QuestionPreview:
83
84
  self.ui = parent.ui
84
85
  self.parent = parent
85
86
 
87
+ def _replaceImgPlaceholder(self, elementStr: str) -> str:
88
+ """Replaces '@@PLUGINFILE@@' with the questions Img Folder path."""
89
+ return elementStr.replace(
90
+ "@@PLUGINFILE@@",
91
+ f"{self.pictureFolder}/{self.question.category.NAME}",
92
+ )
93
+
86
94
  def setupQuestion(self, question: Question) -> None:
95
+ self.pictureFolder = self.parent.settings.get(Tags.PICTUREFOLDER)
96
+ self.ui.previewTextEdit.clear()
87
97
  self.question: Question = question
88
98
  self.ui.qNameLine.setText(f"{self.question.qtype} - {self.question.name}")
89
- self.picScene = QtWidgets.QGraphicsScene(self.parent)
90
- self.ui.graphicsView.setScene(self.picScene)
91
- self.setText()
99
+ self.ui.previewTextEdit.append(
100
+ self._replaceImgPlaceholder(
101
+ ET.tostring(self.question.htmlRoot, encoding="unicode")
102
+ )
103
+ )
104
+ self.parent.ui.tableVariables.hide()
92
105
  self.setAnswers()
93
- if hasattr(self, "picItem") and self.picItem.scene() == self.picScene:
94
- logger.debug("removing Previous picture")
95
- self.picScene.removeItem(self.picItem)
96
- del self.picItem
97
- self.setPicture()
98
-
99
- def setPicture(self) -> None:
100
- if hasattr(self.question, "picture") and self.question.picture.ready:
101
- path = self.question.picture.path
102
- if path.suffix == ".svg":
103
- self.picItem = QGraphicsSvgItem(str(path))
104
- else:
105
- pic = QtGui.QPixmap(str(path))
106
- self.picItem = QtWidgets.QGraphicsPixmapItem(pic)
107
- if pic.isNull():
108
- logger.warning("Picture null")
109
- scale = self._getImgFittingScale()
110
- self.picItem.setScale(scale)
111
- self.picScene.addItem(self.picItem)
112
-
113
- def _getImgFittingScale(self) -> float:
114
- view_size = self.ui.graphicsView.viewport().size()
115
- view_width = view_size.width()
116
- view_height = view_size.height()
117
- if isinstance(self.picItem, QtWidgets.QGraphicsPixmapItem):
118
- original_size = self.picItem.pixmap().size()
119
- elif isinstance(self.picItem, QGraphicsSvgItem):
120
- original_size = self.picItem.renderer().defaultSize()
121
- else:
122
- return 1 # Unknown item type
123
- scale_x = view_width / original_size.width()
124
- scale_y = view_height / original_size.height()
125
- return min(scale_x, scale_y)
126
-
127
- def setText(self) -> None:
128
- t = []
129
- for text in self.question.qtextParagraphs:
130
- t.append(ET.tostring(text, encoding="unicode"))
131
- if self.question.bulletList is not None:
132
- t.append(ET.tostring(self.question.bulletList, encoding="unicode"))
133
- self.ui.questionText.setText("\n".join(t))
134
106
 
135
107
  def setAnswers(self) -> None:
136
- if self.question.qtype == "NFM":
137
- list = ET.Element("ol")
138
- for ans in self.question.answerVariants:
139
- textEle = ET.Element("li")
140
- textEle.text = f"Result: {ans.find('text').text}"
141
- list.append(textEle)
142
- self.ui.answersLabel.setText(ET.tostring(list, encoding="unicode"))
108
+ if isinstance(self.question, ParametricQuestion):
109
+ variableGenerator.populateDataSetTable(
110
+ self.parent.ui.tableVariables, parametrics=self.question.parametrics
111
+ )
112
+ self.parent.ui.tableVariables.show()
143
113
  elif self.question.qtype == "NF":
144
- ans = self.question.element.find(XMLTags.ANSWER)
145
- self.ui.answersLabel.setText(f" Result: {ans.find('text').text}")
114
+ ans = self.question._element.find(XMLTags.ANSWER)
115
+ self.ui.previewTextEdit.append(f" Result: {ans.find('text').text}")
146
116
  elif self.question.qtype == "MC":
147
- list = ET.Element("ol")
148
- for ans in self.question.element.findall(XMLTags.ANSWER):
149
- textEle = ET.Element("li")
150
- pEle = ans.find("text").text
151
- frac = ans.get("fraction")
152
- anstext = ET.fromstring(pEle).text
153
- text = f"Fraction {frac}: {anstext}"
154
- textEle.text = text
155
- list.append(textEle)
156
- self.ui.answersLabel.setText(ET.tostring(list, encoding="unicode"))
157
-
158
-
159
- class AboutDialog(QtWidgets.QMessageBox):
160
- def __init__(self, parent: QtWidgets.QWidget) -> None:
117
+ for n, ans in enumerate(self.question._element.findall(XMLTags.ANSWER)):
118
+ self.ui.previewTextEdit.append(
119
+ f"<b>Answer {n + 1}, Fraction {ans.get('fraction')}:</b>"
120
+ )
121
+ self.ui.previewTextEdit.append(
122
+ self._replaceImgPlaceholder(ans.find("text").text)
123
+ )
124
+
125
+
126
+ class AboutDialog(QMessageBox):
127
+ def __init__(self, parent: QWidget) -> None:
161
128
  super().__init__(parent)
162
129
  self.setWindowTitle(f"About {e2mMetadata['name']}")
163
- self.setIcon(QtWidgets.QMessageBox.Information)
164
- self.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Close)
130
+ self.setIcon(QMessageBox.Information)
131
+ self.setStandardButtons(QMessageBox.StandardButton.Close)
165
132
 
166
133
  self.aboutMessage: str = f"""
167
134
  <h1> About {e2mMetadata["name"]} v{e2mMetadata["version"]}</h1><br>
@@ -2,7 +2,7 @@ import logging
2
2
  import math
3
3
  from pathlib import Path
4
4
 
5
- from PySide6 import QtWidgets
5
+ from PySide6.QtWidgets import QWidget
6
6
 
7
7
  from excel2moodle import mainLogger
8
8
  from excel2moodle.core.question import ParametricQuestion
@@ -18,7 +18,7 @@ loggerSignal = LogWindowHandler()
18
18
  mainLogger.addHandler(loggerSignal)
19
19
 
20
20
 
21
- class EqCheckerWindow(QtWidgets.QWidget):
21
+ class EqCheckerWindow(QWidget):
22
22
  def __init__(self) -> None:
23
23
  super().__init__()
24
24
  self.excelFile = Path()
@@ -1,11 +1,17 @@
1
- from PySide6 import QtWidgets
1
+ """The `treewidget` Module provides the `QuestionItem` and the `CategoryItem` item.
2
+
3
+ Those two are subclasses of `QTreeWidgetItem`, to provide an easy interface
4
+ of accessing the corresponding questions from the items.
5
+ """
6
+
2
7
  from PySide6.QtCore import Qt
8
+ from PySide6.QtWidgets import QTreeWidgetItem
3
9
 
4
10
  from excel2moodle.core.dataStructure import Category
5
11
  from excel2moodle.core.question import ParametricQuestion, Question
6
12
 
7
13
 
8
- class QuestionItem(QtWidgets.QTreeWidgetItem):
14
+ class QuestionItem(QTreeWidgetItem):
9
15
  def __init__(self, parent, question: Question | ParametricQuestion) -> None:
10
16
  super().__init__(parent)
11
17
  self.setData(2, Qt.UserRole, question)
@@ -20,7 +26,7 @@ class QuestionItem(QtWidgets.QTreeWidgetItem):
20
26
  return self.data(2, Qt.UserRole)
21
27
 
22
28
 
23
- class CategoryItem(QtWidgets.QTreeWidgetItem):
29
+ class CategoryItem(QTreeWidgetItem):
24
30
  def __init__(self, parent, category: Category) -> None:
25
31
  super().__init__(parent)
26
32
  self.setData(2, Qt.UserRole, category)
@@ -45,24 +51,3 @@ class CategoryItem(QtWidgets.QTreeWidgetItem):
45
51
 
46
52
  def getCategory(self) -> Category:
47
53
  return self.data(2, Qt.UserRole)
48
-
49
-
50
- # class SpinBoxDelegate(QtWidgets.QStyledItemDelegate):
51
- # def __init__(self, parent=None):
52
- # super().__init__(parent)
53
- #
54
- # def createEditor(self, parent, option, index):
55
- # # Create a QSpinBox when the item is being edited
56
- # spinbox = QtWidgets.QSpinBox(parent)
57
- # spinbox.setMinimum(0)
58
- # spinbox.setMaximum(100)
59
- # return spinbox
60
- #
61
- # def setEditorData(self, editor, index):
62
- # # Set the current value of the QSpinBox based on the item's data
63
- # value = index.model().data(index, Qt.EditRole)
64
- # editor.setValue(value)
65
- #
66
- # def setModelData(self, editor, model, index):
67
- # # When editing is done, update the model data with the QSpinBox value
68
- # model.setData(index, editor.value(), Qt.EditRole)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: excel2moodle
3
- Version: 0.5.2
3
+ Version: 0.6.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
@@ -12,11 +12,11 @@ Classifier: Operating System :: OS Independent
12
12
  Requires-Python: >=3.10
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: pyside6>=6.8.0
16
15
  Requires-Dist: pandas>=2.1.3
17
16
  Requires-Dist: lxml>=5.4.0
18
17
  Requires-Dist: asteval>=1.0.6
19
18
  Requires-Dist: python-calamine>=0.3.2
19
+ Requires-Dist: pyside6-essentials>=6.8.0
20
20
  Dynamic: license-file
21
21
 
22
22
  # excel 2 Moodle
@@ -81,6 +81,31 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
81
81
 
82
82
  # Changelogs
83
83
 
84
+ ## 0.6.0 (2025-07-12)
85
+ Added variable generator and other architechtural improvements
86
+
87
+ ### documentation (1 change)
88
+
89
+ - [Documenting variable generator usage](https://gitlab.com/jbosse3/excel2moodle/-/commit/3e4d3019b29872b5cfddf5539d5ebe7638bca049)
90
+
91
+ ### feature (5 changes)
92
+
93
+ - [Opening spreadsheet file works from within excel2moodle](https://gitlab.com/jbosse3/excel2moodle/-/commit/9470f12ea5f098745a3210b281a5144a938ae8b5)
94
+ - [Variables are copied to clipboard](https://gitlab.com/jbosse3/excel2moodle/-/commit/87a7e5ec75f899b293e89ad3c1742567e3ec1c29)
95
+ - [Removed dependence on pyside6-addons](https://gitlab.com/jbosse3/excel2moodle/-/commit/2b3a7cf48581c14bd9cb570cd61d1d41aa410e11)
96
+ - [Var Generator ready](https://gitlab.com/jbosse3/excel2moodle/-/commit/ea97f0639dc35a4c99a64ae3976ccc8a0ac5d109)
97
+ - [Merge development of BulletsObj, Parametrization and VarGenerator](https://gitlab.com/jbosse3/excel2moodle/-/commit/40b46f3c143e082f1bb985d6c8c4e68bb6b6a7a8)
98
+
99
+ ### improvement (7 changes)
100
+
101
+ - [Adapted Param. Parser to use bullet Obj](https://gitlab.com/jbosse3/excel2moodle/-/commit/194cab7cc6aecb2d25d1cb9c1538ed7d607dd9e1)
102
+ - [Added bulleList Object](https://gitlab.com/jbosse3/excel2moodle/-/commit/4ea982b8d8dc270675d2cb059c59fa980ce38894)
103
+ - [Parametrics in beta stage](https://gitlab.com/jbosse3/excel2moodle/-/commit/7d04d8ef2fc603c1b12b6934c827ce079df5d540)
104
+ - [Refactor parse() method, to construct complete xml-Tree](https://gitlab.com/jbosse3/excel2moodle/-/commit/8dc4bea9aa0673d39357115254dd55b02c04114e)
105
+ - [Refactored question assembly to only update fields.](https://gitlab.com/jbosse3/excel2moodle/-/commit/d7accb69be3b4a1e65f59eeecfb463f2663fabd4)
106
+ - [Adapted NFM Question to parametricResult](https://gitlab.com/jbosse3/excel2moodle/-/commit/fe552cd2b538ca8886415c200e4a2a3ecc1fbb2f) ([merge request](https://gitlab.com/jbosse3/excel2moodle/-/merge_requests/5))
107
+ - [Implemented ParametricResult Object](https://gitlab.com/jbosse3/excel2moodle/-/commit/e36d025955f1cab8e0542d66263ab70e3d8980df) ([merge request](https://gitlab.com/jbosse3/excel2moodle/-/merge_requests/5))
108
+
84
109
  ## 0.5.2 (2025-06-30)
85
110
  Extended Documentation and bugfix for import Module
86
111
 
@@ -2,37 +2,38 @@ excel2moodle/__init__.py,sha256=mnb-RWgmWIPSBk4S65a_jP6rxntAkTeYxN0ObUJalbQ,1801
2
2
  excel2moodle/__main__.py,sha256=sG4ygwfVFskLQorBn-v98SvasNcPmwl_vLYpruT5Hk8,1175
3
3
  excel2moodle/logger.py,sha256=fq8ZOkCI1wj38v8IyrZsUlpt16onlSH_phqbVvYUwBQ,3725
4
4
  excel2moodle/core/__init__.py,sha256=87BwhtZse72Tk17Ib-V9X2k9wkhmtVnEj2ZmJ9JBAnI,63
5
+ excel2moodle/core/bullets.py,sha256=F9g0dZfkMjoYQ5jneT47GwM1-RAyhPF8y203W5mOYpU,3505
5
6
  excel2moodle/core/category.py,sha256=wLzpbweQbzaItdbp2NCPI_Zmk94fy1EDOwEEN8zPvkU,2123
6
- excel2moodle/core/dataStructure.py,sha256=0XVu6NqKKNrjL0B9UVD9di-uqriGIdF-57lwx0ckeDI,16026
7
+ excel2moodle/core/dataStructure.py,sha256=eMQAuTTKXLmE72I6MP1255BP15N-35Qb4bESyIy4K7w,16011
7
8
  excel2moodle/core/etHelpers.py,sha256=G37qplp8tPJxqHNCBrf2Wo0jJZ0aDbxE9slQavqYqd8,2293
8
9
  excel2moodle/core/exceptions.py,sha256=9xfsaIcm6Yej6QAZga0d3DK3jLQejdfgJARuAaG-uZY,739
9
- excel2moodle/core/globals.py,sha256=Zm1wcrzQTRnhjrkwgBvo7VjKCFdPMjh-VLSSI5_QCO8,2837
10
- excel2moodle/core/numericMultiQ.py,sha256=vr-gYogu2sf2a_Bhvhnu1ZSZFZXM32MfhJesjTkoOQM,2618
11
- excel2moodle/core/parser.py,sha256=y0BXXt5j-4gRZO8otmEZ1Rmb0DW7hziesUoZ2kVpo9Y,8235
12
- excel2moodle/core/question.py,sha256=_BSaMdDB269Q4Ag7izegMiExjYRTYOuhYe-qo92EAAg,11726
10
+ excel2moodle/core/globals.py,sha256=URrDtWUeaZvhv38ETk_MGRDZpWF0J5cTWO_Jpf0rS9E,2807
11
+ excel2moodle/core/parser.py,sha256=ExTgPkRXevXDcnQlWE2oGoEp6mbpzP1GBmzY4zs-OPU,7220
12
+ excel2moodle/core/question.py,sha256=pltXKN52tBpFoPKEt9cIdGzZzVKpCQcXun2g9Eu1ljE,13726
13
13
  excel2moodle/core/settings.py,sha256=27D-P44rYk-DMrwI1dNpxHcznpFQf1W3XZrOc8e6rX4,5855
14
14
  excel2moodle/core/stringHelpers.py,sha256=OzFZ6Eu3PeBLKb61K-aeVfUZmVuBerr9KfyOsuNRd7Y,2403
15
15
  excel2moodle/core/validator.py,sha256=ssgkyUwrR-0AGPX1cUqvRwZsGja13J7HQ2W72ltqN-Y,4683
16
16
  excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
17
17
  excel2moodle/extra/equationVerification.py,sha256=GLJl1r90d8AAiNy0H2hooZrg3D6aEwNfifYKAe3aGxM,3921
18
+ excel2moodle/extra/variableGenerator.py,sha256=fiXaTeFaC--1KRyMNcsjBgUlUZ8H1rGS6f_WenN60fQ,9851
18
19
  excel2moodle/question_types/__init__.py,sha256=81mss0g7SVtnlb-WkydE28G_dEAAf6oT1uB8lpK2-II,1041
19
- excel2moodle/question_types/cloze.py,sha256=-F2CozuNHRvTDQM69DgkKXTvw9wMLXfipkdhfTNWSSs,11969
20
+ excel2moodle/question_types/cloze.py,sha256=SvUS4gVKTr6z26KI17zV7pNIGIWQ2JdIVtPr-hgOOo8,13602
20
21
  excel2moodle/question_types/mc.py,sha256=2kn6dPjFVg97H8SlUBFbcPjzDk84vgDGCMOtSABseu0,5225
21
22
  excel2moodle/question_types/nf.py,sha256=bMP4IXrhnXmAI0NmjEc7DtX4xGaUbxzLicE2LjeaUho,1150
22
- excel2moodle/question_types/nfm.py,sha256=LTQ60qrhYUqKXoZW4AKz352rD4pgN0wAX_tm5FLFbV0,5022
23
+ excel2moodle/question_types/nfm.py,sha256=p1yP6QWtFzQpVVfpjZ7R8GrsEtUSUPklbvjC8_VgYmQ,2734
23
24
  excel2moodle/ui/UI_equationChecker.py,sha256=evQDlqCHeooJcAnYjhFCyjlPhfknr7ULGKQwMmqQeJ4,8947
24
25
  excel2moodle/ui/UI_exportSettingsDialog.py,sha256=71xxXEqtewN0ReMfJ5t4gbrX_Bf0VEuxJ_DIV7ZtH94,6045
25
- excel2moodle/ui/UI_mainWindow.py,sha256=asWUmKIYqufKUvRuCuA1JoMyv4qfRXyoR70F0331lww,19291
26
+ excel2moodle/ui/UI_mainWindow.py,sha256=9w8bRgOrVEX7BRGQvMuVhPCiSOsXYkMb4rxLDeRErII,21544
27
+ excel2moodle/ui/UI_variableGenerator.py,sha256=DjpZnBELSqyOjJdwjXNP2V71rCPm3tr6_XkNT9F3e34,11205
26
28
  excel2moodle/ui/UI_variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
27
29
  excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
28
- excel2moodle/ui/appUi.py,sha256=caNdBb4_C2Ptl0He2iCQqoxw6Tm0wmJL9eOoKfjMah8,10681
29
- excel2moodle/ui/dialogs.py,sha256=0h6aD4tguph1P07dorkn1A5B7_Z5SJZQ2_8xBYWK6MU,7689
30
- excel2moodle/ui/equationChecker.py,sha256=ANpN7S0llkp6pGL1sKHII1Jc8YUvgDR458UnGVnZZOo,2702
31
- excel2moodle/ui/treewidget.py,sha256=az64swVj1yQUsioeaZys32AauvQDdC4EKcqdbbWgL6s,2489
32
- excel2moodle/ui/windowDoc.py,sha256=WvzHj6F4JvHP82WlTsyFeOXW024Xq3BUqtp--T4twuI,661
33
- excel2moodle-0.5.2.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
34
- excel2moodle-0.5.2.dist-info/METADATA,sha256=pskuhRVnt-PEmvxVxvPUvN03gHmilnZqeFluoPOkPrk,6330
35
- excel2moodle-0.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- excel2moodle-0.5.2.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
37
- excel2moodle-0.5.2.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
38
- excel2moodle-0.5.2.dist-info/RECORD,,
30
+ excel2moodle/ui/appUi.py,sha256=0jxpgGiDU82AEDrj-BFgD9XdktrY_0EB7bAVMqWzJLY,13283
31
+ excel2moodle/ui/dialogs.py,sha256=8m1_hBGHt56T581PpPDsCrAd66cIy_Kxo_8NIy9kw_0,6179
32
+ excel2moodle/ui/equationChecker.py,sha256=RII9DlZAlHqe5udBWzeUaozhtyi3ZkCZs8h3-oO6pEw,2700
33
+ excel2moodle/ui/treewidget.py,sha256=aLR_B1iT2j1AAGk_9Dc6WtRbq1XijK8pICG8BEOW_Ak,1916
34
+ excel2moodle-0.6.0.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
35
+ excel2moodle-0.6.0.dist-info/METADATA,sha256=ry39D3WK7oORTNVo4H1zmK9SU3d1QYEPNGBFwEpoRLI,8429
36
+ excel2moodle-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ excel2moodle-0.6.0.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
38
+ excel2moodle-0.6.0.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
39
+ excel2moodle-0.6.0.dist-info/RECORD,,
@@ -1,80 +0,0 @@
1
- """Numeric Multi Questions Module to calculate results from a formula.
2
-
3
- This module calculates a series of results from al matrix of variables.
4
- For each column in the matrix there will be one result.
5
- As well it returns a bullet points string that shows the numerical values corresponding to the set of variables
6
- """
7
-
8
- import re
9
-
10
- import pandas as pd
11
- from asteval import Interpreter
12
-
13
- astEval = Interpreter()
14
-
15
-
16
- def getVariablesDict(df: pd.DataFrame, keyList: list, index: int) -> dict:
17
- """Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]."""
18
- dic = {}
19
- for k in keyList:
20
- val = df.loc[str(k)][index]
21
- if isinstance(val, str) and val is not None:
22
- li = val.split(";")
23
- dic[str(k)] = li
24
- else:
25
- dic[str(k)] = [str(val)]
26
- return dic
27
-
28
-
29
- def setParameters(parameters: dict, index: int) -> None:
30
- """Ubergibt die Parameter mit entsprechenden Variablen-Namen an den asteval-Interpreter.
31
-
32
- Dann kann dieser die equation loesen.
33
- """
34
- for k, v in parameters.items():
35
- comma = re.compile(r",")
36
- value = comma.sub(".", v[index])
37
- astEval.symtable[k] = float(value)
38
-
39
-
40
- def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int) -> str:
41
- """Für jeden Eintrag im varDict, wird im bulletPoints String der
42
- Substring "{key}" durch value[index] ersetzt.
43
- """
44
- for k, v in varDict.items():
45
- s = r"{" + str(k) + r"}"
46
- matcher = re.compile(s)
47
- bulletPoints = matcher.sub(str(v[index]), bulletPoints)
48
- return bulletPoints
49
-
50
-
51
- def getVarsList(bps: str) -> list:
52
- """Durchsucht den bulletPoints String nach den Variablen `{var}`."""
53
- vars = re.findall(r"\{\w\}", str(bps))
54
- variablen = []
55
- for v in vars:
56
- variablen.append(v.strip("{}"))
57
- return variablen
58
-
59
-
60
- def parseNumericMultiQuestion(
61
- datFrame: pd.DataFrame,
62
- bulletPoints: str,
63
- equation: str,
64
- questionIndex: int,
65
- ) -> tuple[list[str], list[float]]:
66
- """Berechnet die Ergebnisse anhand der Variablen in *bulletPoints*.
67
-
68
- Gibt eine Liste mit allen Ergebnissen zurück
69
- und eine Liste mit den bulletPoints-Strings, die die Numerischen Variablen enthalten
70
- """
71
- results = []
72
- bps = []
73
- varNames = getVarsList(bulletPoints)
74
- variables = getVariablesDict(datFrame, varNames, questionIndex)
75
- length = len(next(iter(variables.values())))
76
- for n in range(length):
77
- setParameters(variables, n)
78
- results.append(astEval(equation))
79
- bps.append(insertVariablesToBPoints(variables, bulletPoints, n))
80
- return bps, results