excel2moodle 0.6.1__tar.gz → 0.6.2__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.
Files changed (51) hide show
  1. {excel2moodle-0.6.1/excel2moodle.egg-info → excel2moodle-0.6.2}/PKG-INFO +46 -19
  2. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/README.md +44 -18
  3. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/category.py +24 -6
  4. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/dataStructure.py +116 -27
  5. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/etHelpers.py +0 -20
  6. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/globals.py +0 -9
  7. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/parser.py +21 -13
  8. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/question.py +43 -23
  9. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/settings.py +11 -4
  10. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/extra/equationVerification.py +0 -2
  11. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/question_types/cloze.py +116 -83
  12. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/question_types/mc.py +15 -10
  13. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/question_types/nf.py +7 -1
  14. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/question_types/nfm.py +14 -10
  15. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/UI_exportSettingsDialog.py +60 -14
  16. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/appUi.py +21 -7
  17. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/dialogs.py +12 -0
  18. {excel2moodle-0.6.1 → excel2moodle-0.6.2/excel2moodle.egg-info}/PKG-INFO +46 -19
  19. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle.egg-info/SOURCES.txt +1 -1
  20. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle.egg-info/requires.txt +1 -0
  21. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/pyproject.toml +3 -2
  22. excel2moodle-0.6.2/test/test_feedbacking.py +73 -0
  23. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/test/test_nfmParsing.py +41 -12
  24. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/test/test_parseQuestion.py +0 -14
  25. excel2moodle-0.6.1/MANIFEST.in +0 -1
  26. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/LICENSE +0 -0
  27. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/__init__.py +0 -0
  28. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/__main__.py +0 -0
  29. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/__init__.py +0 -0
  30. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/bullets.py +0 -0
  31. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/exceptions.py +0 -0
  32. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/stringHelpers.py +0 -0
  33. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/core/validator.py +0 -0
  34. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/extra/__init__.py +0 -0
  35. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/extra/variableGenerator.py +0 -0
  36. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/logger.py +0 -0
  37. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/question_types/__init__.py +0 -0
  38. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/UI_equationChecker.py +0 -0
  39. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/UI_mainWindow.py +0 -0
  40. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/UI_variableGenerator.py +0 -0
  41. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/UI_variantDialog.py +0 -0
  42. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/__init__.py +0 -0
  43. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/equationChecker.py +0 -0
  44. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle/ui/treewidget.py +0 -0
  45. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle.egg-info/dependency_links.txt +0 -0
  46. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle.egg-info/entry_points.txt +0 -0
  47. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/excel2moodle.egg-info/top_level.txt +0 -0
  48. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/setup.cfg +0 -0
  49. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/test/test_bullets.py +0 -0
  50. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/test/test_picture.py +0 -0
  51. {excel2moodle-0.6.1 → excel2moodle-0.6.2}/test/test_questionDataGet.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: excel2moodle
3
- Version: 0.6.1
3
+ Version: 0.6.2
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
@@ -17,16 +17,20 @@ Requires-Dist: lxml>=5.4.0
17
17
  Requires-Dist: asteval>=1.0.6
18
18
  Requires-Dist: python-calamine>=0.3.2
19
19
  Requires-Dist: pyside6-essentials>=6.8.0
20
+ Requires-Dist: pyyaml>=6.0.2
20
21
  Dynamic: license-file
21
22
 
22
23
  # excel 2 Moodle
23
- ![Logo](excel2moodleLogo.png "Logo excel2moodle"){width=35%}
24
+ ![Logo](https://gitlab.com/jbosse3/-/blob/master/excel2moodleLogo.png "Logo excel2moodle"){width=35%}
24
25
 
25
- This Python program helps to create Moodle questions in less time.
26
- The idea is to write the questions data into a spreadsheet file, from which the program generates the moodle compliant xml Files.
27
- All questions or a selection of questions can be exported into one xml file to be imported into moodle.
26
+ *excel2moodle* helps to create Moodle questions in less time.
27
+ The idea is to write the questions data into a spreadsheet file, from which the *excel2moodle* generates moodle compliant xml Files.
28
+ All questions or a selection of questions can then be imported into moodle.
28
29
 
29
30
  ## Concept
31
+ At the heart the *excel2moodle* is a simple key-value-pair "syntax", where the key is set once in the first spreadsheet column.
32
+ Each key can be provided with a value for each question in its column
33
+ To enhance reusability, key values which are correct for more than one question can be set per category or for all questions.
30
34
  The concept is, to store the different questions into categories of similar types and difficulties of questions, for each of which, a separated sheet in the Spreadsheet document should be created.
31
35
 
32
36
  A `settings` sheet contains global settings to be used for all questions and categories.
@@ -41,33 +45,38 @@ If you already have python and uv installed, it is as easy as running `uv tool i
41
45
 
42
46
  ### [ Documentation ](https://jbosse3.gitlab.io/excel2moodle/index.html)
43
47
  Once excel2moodle is installed you can checkout the [example question sheet](https://gitlab.com/jbosse3/excel2moodle/-/tree/master/example?ref_type=heads)
44
- in the repository.
48
+ in the repository. You need to download all Files in the `example` directory and save them together.
45
49
 
46
- Some steps are already documented as [ tutorials ](https://jbosse3.gitlab.io/excel2moodle/howto.html)
50
+ Most steps are already documented as [ tutorials ](https://jbosse3.gitlab.io/excel2moodle/howto.html)
47
51
  you can follow along.
48
52
 
49
53
  And please have a look into the [**user Reference**](https://jbosse3.gitlab.io/excel2moodle/userReference.html)
50
54
  of the documentation.
51
- That part explains each part of defining a question.
55
+ That part explains in more detail each part of defining a question.
52
56
 
53
57
 
54
- ## Functionality
55
- * Equation Verification:
56
- + this tool helps you to validate the correct equation for the parametrized Questions.
57
- * Question Preview:
58
+ ## Features
59
+ - Fully parametrized numeric questions:
60
+ * Formulas for the calculated results can be coded into extensive python modules, which can be loaded.
61
+ - Question Preview:
58
62
  + This helps you when selecting the correct questions for the export.
59
- * Export Options:
63
+ - Equation Verification:
64
+ + this tool helps you to validate the correct equation for the parametrized Questions.
65
+ - Variable Generation:
66
+ + You can generate variables for the parametric Question to easily create hundreds of different variants of the same question.
67
+ - Export Options:
60
68
  + you can export the questions preserving the categories in moodle
61
69
 
62
70
  ### Question Types
63
- * Generate multiple Choice Questions:
64
- + The answers can be pictures or normal text
65
- * Generate Numeric Questions
66
- * Generate parametrized numeric Questions
67
- * Generate parametrized cloze Questions
71
+ - Generate multiple Choice Questions:
72
+ * The answers can be pictures or normal text
73
+ - Generate Numeric Questions
74
+ - Generate parametrized numeric Questions:
75
+ * With the parametrization *excel2moodle* calculats the numeric answer from a given formula based on a set of variables.
76
+ - Generate parametrized cloze Questions
68
77
 
69
78
 
70
- ![MainWindow](mainWindow.png "Logo excel2moodle"){width=80%}
79
+ ![MainWindow](https://gitlab.com/jbosse3/-/blob/master/mainWindow.png "Logo excel2moodle"){width=80%}
71
80
 
72
81
  ## Licensing and authorship
73
82
  excel2moodle is lincensed under the latest [GNU GPL license](https://gitlab.com/jbosse3/excel2moodle/-/blob/master/LICENSE)
@@ -81,6 +90,24 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
81
90
 
82
91
  # Changelogs
83
92
 
93
+ ## 0.6.2 (2025-08-02)
94
+ Adding export options and fixing cloze points bug
95
+
96
+ ### feature (4 changes)
97
+
98
+ - [Added export options to include all Question Variants and generate report](https://gitlab.com/jbosse3/excel2moodle/-/commit/6433615de23174451748b69669a9dce748dd5b4d)
99
+ - [Implemented export dialog generator method](https://gitlab.com/jbosse3/excel2moodle/-/commit/a8eda982309bf9a6dae7ef2b261a59654f2c8910)
100
+ - [Answer Feedback strings settable in sheet](https://gitlab.com/jbosse3/excel2moodle/-/commit/ad90da49ac60e429ad3243f54846b08f0caf5bc7)
101
+ - [Inverted result and feedback for NFM & Cloze questions](https://gitlab.com/jbosse3/excel2moodle/-/commit/57d77c83a661398b0082f84d25e5447000df9096)
102
+
103
+ ### improvement (1 change)
104
+
105
+ - [Missing `settings` sheet raises an error](https://gitlab.com/jbosse3/excel2moodle/-/commit/e1cc42c1d31981bf74582b23c24c6ac378e9256d)
106
+
107
+ ### bugfix (1 change)
108
+
109
+ - [resolve cloze moodle import error due to float points](https://gitlab.com/jbosse3/excel2moodle/-/commit/f13b7b9df39df55d65b6063a9deb1fc1c72f5ebb)
110
+
84
111
  ## 0.6.1 (2025-07-12)
85
112
  Fixing import error caused by dumping pyside meta package
86
113
 
@@ -1,11 +1,14 @@
1
1
  # excel 2 Moodle
2
- ![Logo](excel2moodleLogo.png "Logo excel2moodle"){width=35%}
2
+ ![Logo](https://gitlab.com/jbosse3/-/blob/master/excel2moodleLogo.png "Logo excel2moodle"){width=35%}
3
3
 
4
- This Python program helps to create Moodle questions in less time.
5
- The idea is to write the questions data into a spreadsheet file, from which the program generates the moodle compliant xml Files.
6
- All questions or a selection of questions can be exported into one xml file to be imported into moodle.
4
+ *excel2moodle* helps to create Moodle questions in less time.
5
+ The idea is to write the questions data into a spreadsheet file, from which the *excel2moodle* generates moodle compliant xml Files.
6
+ All questions or a selection of questions can then be imported into moodle.
7
7
 
8
8
  ## Concept
9
+ At the heart the *excel2moodle* is a simple key-value-pair "syntax", where the key is set once in the first spreadsheet column.
10
+ Each key can be provided with a value for each question in its column
11
+ To enhance reusability, key values which are correct for more than one question can be set per category or for all questions.
9
12
  The concept is, to store the different questions into categories of similar types and difficulties of questions, for each of which, a separated sheet in the Spreadsheet document should be created.
10
13
 
11
14
  A `settings` sheet contains global settings to be used for all questions and categories.
@@ -20,33 +23,38 @@ If you already have python and uv installed, it is as easy as running `uv tool i
20
23
 
21
24
  ### [ Documentation ](https://jbosse3.gitlab.io/excel2moodle/index.html)
22
25
  Once excel2moodle is installed you can checkout the [example question sheet](https://gitlab.com/jbosse3/excel2moodle/-/tree/master/example?ref_type=heads)
23
- in the repository.
26
+ in the repository. You need to download all Files in the `example` directory and save them together.
24
27
 
25
- Some steps are already documented as [ tutorials ](https://jbosse3.gitlab.io/excel2moodle/howto.html)
28
+ Most steps are already documented as [ tutorials ](https://jbosse3.gitlab.io/excel2moodle/howto.html)
26
29
  you can follow along.
27
30
 
28
31
  And please have a look into the [**user Reference**](https://jbosse3.gitlab.io/excel2moodle/userReference.html)
29
32
  of the documentation.
30
- That part explains each part of defining a question.
33
+ That part explains in more detail each part of defining a question.
31
34
 
32
35
 
33
- ## Functionality
34
- * Equation Verification:
35
- + this tool helps you to validate the correct equation for the parametrized Questions.
36
- * Question Preview:
36
+ ## Features
37
+ - Fully parametrized numeric questions:
38
+ * Formulas for the calculated results can be coded into extensive python modules, which can be loaded.
39
+ - Question Preview:
37
40
  + This helps you when selecting the correct questions for the export.
38
- * Export Options:
41
+ - Equation Verification:
42
+ + this tool helps you to validate the correct equation for the parametrized Questions.
43
+ - Variable Generation:
44
+ + You can generate variables for the parametric Question to easily create hundreds of different variants of the same question.
45
+ - Export Options:
39
46
  + you can export the questions preserving the categories in moodle
40
47
 
41
48
  ### Question Types
42
- * Generate multiple Choice Questions:
43
- + The answers can be pictures or normal text
44
- * Generate Numeric Questions
45
- * Generate parametrized numeric Questions
46
- * Generate parametrized cloze Questions
49
+ - Generate multiple Choice Questions:
50
+ * The answers can be pictures or normal text
51
+ - Generate Numeric Questions
52
+ - Generate parametrized numeric Questions:
53
+ * With the parametrization *excel2moodle* calculats the numeric answer from a given formula based on a set of variables.
54
+ - Generate parametrized cloze Questions
47
55
 
48
56
 
49
- ![MainWindow](mainWindow.png "Logo excel2moodle"){width=80%}
57
+ ![MainWindow](https://gitlab.com/jbosse3/-/blob/master/mainWindow.png "Logo excel2moodle"){width=80%}
50
58
 
51
59
  ## Licensing and authorship
52
60
  excel2moodle is lincensed under the latest [GNU GPL license](https://gitlab.com/jbosse3/excel2moodle/-/blob/master/LICENSE)
@@ -60,6 +68,24 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
60
68
 
61
69
  # Changelogs
62
70
 
71
+ ## 0.6.2 (2025-08-02)
72
+ Adding export options and fixing cloze points bug
73
+
74
+ ### feature (4 changes)
75
+
76
+ - [Added export options to include all Question Variants and generate report](https://gitlab.com/jbosse3/excel2moodle/-/commit/6433615de23174451748b69669a9dce748dd5b4d)
77
+ - [Implemented export dialog generator method](https://gitlab.com/jbosse3/excel2moodle/-/commit/a8eda982309bf9a6dae7ef2b261a59654f2c8910)
78
+ - [Answer Feedback strings settable in sheet](https://gitlab.com/jbosse3/excel2moodle/-/commit/ad90da49ac60e429ad3243f54846b08f0caf5bc7)
79
+ - [Inverted result and feedback for NFM & Cloze questions](https://gitlab.com/jbosse3/excel2moodle/-/commit/57d77c83a661398b0082f84d25e5447000df9096)
80
+
81
+ ### improvement (1 change)
82
+
83
+ - [Missing `settings` sheet raises an error](https://gitlab.com/jbosse3/excel2moodle/-/commit/e1cc42c1d31981bf74582b23c24c6ac378e9256d)
84
+
85
+ ### bugfix (1 change)
86
+
87
+ - [resolve cloze moodle import error due to float points](https://gitlab.com/jbosse3/excel2moodle/-/commit/f13b7b9df39df55d65b6063a9deb1fc1c72f5ebb)
88
+
63
89
  ## 0.6.1 (2025-07-12)
64
90
  Fixing import error caused by dumping pyside meta package
65
91
 
@@ -5,10 +5,9 @@ from typing import TYPE_CHECKING
5
5
  import lxml.etree as ET
6
6
  import pandas as pd
7
7
 
8
- from excel2moodle.core.settings import Tags
9
-
10
8
  if TYPE_CHECKING:
11
9
  from excel2moodle.core.question import Question
10
+ from excel2moodle.core.settings import Tags
12
11
 
13
12
  loggerObj = logging.getLogger(__name__)
14
13
 
@@ -30,7 +29,7 @@ class Category:
30
29
  self.desc = str(description)
31
30
  self.dataframe: pd.DataFrame = dataframe
32
31
  self.settings: dict[str, float | str] = settings if settings else {}
33
- self.questions: dict[int, Question] = {}
32
+ self._questions: dict[int, Question]
34
33
  self.maxVariants: int | None = None
35
34
  loggerObj.info("initializing Category %s", self.NAME)
36
35
 
@@ -50,6 +49,18 @@ class Category:
50
49
  def id(self) -> str:
51
50
  return f"{self.n:02d}"
52
51
 
52
+ @property
53
+ def questions(self) -> dict:
54
+ if not hasattr(self, "_questions"):
55
+ msg = "Category question are not yet initialized"
56
+ raise ValueError(msg)
57
+ return self._questions
58
+
59
+ def appendQuestion(self, questionNumber: int, question) -> None:
60
+ if not hasattr(self, "_questions"):
61
+ self._questions: dict[int, Question] = {}
62
+ self._questions[questionNumber] = question
63
+
53
64
  def __hash__(self) -> int:
54
65
  return hash(self.NAME)
55
66
 
@@ -58,13 +69,20 @@ class Category:
58
69
  return self.NAME == other.NAME
59
70
  return False
60
71
 
61
- def getCategoryHeader(self) -> ET.Element:
72
+ def getCategoryHeader(self, subCategory: str | None = None) -> ET.Element:
62
73
  """Insert an <question type='category'> before all Questions of this Category."""
63
74
  header = ET.Element("question", type="category")
64
75
  cat = ET.SubElement(header, "category")
65
76
  info = ET.SubElement(header, "info", format="html")
66
- ET.SubElement(cat, "text").text = f"$module$/top/{self.NAME}"
77
+ catStr = (
78
+ f"$module$/top/{self.NAME}"
79
+ if subCategory is None
80
+ else f"$module$/top/{self.NAME}/Question-{subCategory[2:]}_Variants"
81
+ )
82
+ ET.SubElement(cat, "text").text = catStr
67
83
  ET.SubElement(info, "text").text = str(self.desc)
68
- ET.SubElement(header, "idnumber").text = self.id
84
+ ET.SubElement(header, "idnumber").text = (
85
+ f"cat-{self.id}" if subCategory is None else f"variants-{subCategory}"
86
+ )
69
87
  ET.indent(header)
70
88
  return header
@@ -3,6 +3,7 @@
3
3
  At the heart is the class ``xmlTest``
4
4
  """
5
5
 
6
+ import datetime as dt
6
7
  import logging
7
8
  import sys
8
9
  from concurrent.futures import ProcessPoolExecutor, as_completed
@@ -11,14 +12,16 @@ from typing import TYPE_CHECKING
11
12
 
12
13
  import lxml.etree as ET # noqa: N812
13
14
  import pandas as pd
15
+ import yaml
14
16
  from PySide6.QtCore import QObject, Signal
15
17
  from PySide6.QtWidgets import QDialog
16
18
 
19
+ from excel2moodle import __version__
17
20
  from excel2moodle.core import stringHelpers
18
21
  from excel2moodle.core.category import Category
19
22
  from excel2moodle.core.exceptions import InvalidFieldException, QNotParsedException
20
23
  from excel2moodle.core.globals import Tags
21
- from excel2moodle.core.question import Question
24
+ from excel2moodle.core.question import ParametricQuestion, Question
22
25
  from excel2moodle.core.settings import Settings
23
26
  from excel2moodle.core.validator import Validator
24
27
  from excel2moodle.logger import LogAdapterQuestionID
@@ -72,9 +75,10 @@ class QuestionDB:
72
75
  def __init__(self, settings: Settings) -> None:
73
76
  self.settings = settings
74
77
  self.window: QMainWindow | None = None
75
- self.version = None
76
78
  self.categoriesMetaData: pd.DataFrame
77
79
  self.categories: dict[str, Category]
80
+ self._exportedQuestions: list[Question] = []
81
+ self._exportedAll: bool = False
78
82
 
79
83
  @property
80
84
  def spreadsheet(self) -> Path:
@@ -94,22 +98,39 @@ class QuestionDB:
94
98
  ``categoriesMetaData`` dataframe
95
99
  Setup the categories and store them in ``self.categories = {}``
96
100
  Pass the question data to the categories.
101
+
102
+ Raises
103
+ ------
104
+ ValueError
105
+ When there is no 'seetings' worksheet in the file.
106
+ InvalidFieldException
107
+ When the settings are invalid
108
+
109
+ Before raising it logges the exceptions with a meaningful message.
110
+
97
111
  """
98
112
  sheetPath = sheetPath if sheetPath else self.spreadsheet
99
113
  logger.info("Start Parsing the Excel Metadata Sheet\n")
100
- with Path(sheetPath).open("rb") as f:
101
- settingDf = pd.read_excel(
102
- f,
103
- sheet_name="settings",
104
- index_col=0,
105
- engine="calamine",
114
+ try:
115
+ with Path(sheetPath).open("rb") as f:
116
+ settingDf = pd.read_excel(
117
+ f,
118
+ sheet_name="settings",
119
+ index_col=0,
120
+ engine="calamine",
121
+ )
122
+ except ValueError:
123
+ logger.exception(
124
+ "Did you forget to specify a 'settings' sheet in the file?"
106
125
  )
107
- logger.debug("Found the settings: \n\t%s", settingDf)
108
- settingDf = self.harmonizeDFIndex(settingDf)
109
- for tag, value in settingDf.iterrows():
110
- val = value.iloc[0]
111
- if pd.notna(val):
112
- self.settings.set(tag, val)
126
+ raise
127
+ logger.debug("Found the settings: \n\t%s", settingDf)
128
+ settingDf = self.harmonizeDFIndex(settingDf)
129
+ for tag, value in settingDf.iterrows():
130
+ val = value.iloc[0]
131
+ if pd.notna(val):
132
+ self.settings.set(tag, val)
133
+
113
134
  try:
114
135
  self._validateProjectSettings(sheetPath=sheetPath)
115
136
  except InvalidFieldException:
@@ -342,13 +363,18 @@ class QuestionDB:
342
363
  else:
343
364
  msg = "couldn't setup Parser"
344
365
  raise QNotParsedException(msg, question.id)
345
- category.questions[qNumber] = question
366
+ category.appendQuestion(qNumber, question)
346
367
  return question
347
368
 
348
369
  def appendQuestions(
349
- self, questions: list[QuestionItem], file: Path | None = None
370
+ self,
371
+ questions: list[QuestionItem],
372
+ file: Path | None = None,
373
+ pCount: int = 0,
374
+ qCount: int = 0,
350
375
  ) -> None:
351
376
  """Append selected question Elements to the tree."""
377
+ self._exportedQuestions.clear()
352
378
  tree = ET.Element("quiz")
353
379
  catdict: dict[Category, list[Question]] = {}
354
380
  for q in questions:
@@ -365,6 +391,8 @@ class QuestionDB:
365
391
  includeHeader=self.settings.get(Tags.INCLUDEINCATS),
366
392
  )
367
393
  stringHelpers.printDom(tree, file=file)
394
+ if self.settings.get(Tags.GENEXPORTREPORT):
395
+ self.generateExportReport(file, pCount=pCount, qCount=qCount)
368
396
 
369
397
  def _appendQElements(
370
398
  self,
@@ -373,17 +401,78 @@ class QuestionDB:
373
401
  tree: ET.Element,
374
402
  includeHeader: bool = True,
375
403
  ) -> None:
376
- if includeHeader:
404
+ variant: int = self.settings.get(Tags.QUESTIONVARIANT)
405
+ if includeHeader or variant == -1:
377
406
  tree.append(cat.getCategoryHeader())
378
407
  logger.debug(f"Appended a new category item {cat=}")
379
- variant: int = self.settings.get(Tags.QUESTIONVARIANT)
408
+ self._exportedAll: bool = True
380
409
  for q in qList:
381
- if hasattr(q, "variants") and q.variants is not None:
382
- if variant == 0 or variant > q.variants:
383
- dialog = QuestionVariantDialog(self.window, q)
384
- if dialog.exec() == QDialog.Accepted:
385
- variant = dialog.variant
386
- logger.debug("Die Fragen-Variante %s wurde gewählt", variant)
387
- else:
388
- logger.warning("Keine Fragenvariante wurde gewählt.")
389
- tree.append(q.getUpdatedElement(variant=variant))
410
+ if not isinstance(q, ParametricQuestion):
411
+ tree.append(q.getUpdatedElement())
412
+ self._exportedQuestions.append(q)
413
+ continue
414
+ if variant == -1:
415
+ tree.append(cat.getCategoryHeader(subCategory=q.id))
416
+ for var in range(q.parametrics.variants):
417
+ tree.append(q.getUpdatedElement(variant=var))
418
+ elif variant == 0 or variant > q.parametrics.variants:
419
+ dialog = QuestionVariantDialog(self.window, q)
420
+ if dialog.exec() == QDialog.Accepted:
421
+ variant = dialog.variant
422
+ logger.debug("Die Fragen-Variante %s wurde gewählt", variant)
423
+ else:
424
+ logger.warning("Keine Fragenvariante wurde gewählt.")
425
+ tree.append(q.getUpdatedElement(variant=variant))
426
+ self._exportedQuestions.append(q)
427
+
428
+ def generateExportReport(
429
+ self, file: Path | None = None, pCount: int = 0, qCount: int = 0
430
+ ) -> None:
431
+ """Generate a YAML report of the exported questions."""
432
+ if not self._exportedQuestions:
433
+ return
434
+ if file:
435
+ base_path = file.with_name(f"{file.stem}_export_report.yaml")
436
+ else:
437
+ base_path = self.spreadsheet.parent / "export_report.yaml"
438
+
439
+ for i in range(99):
440
+ report_path = base_path.with_name(f"{base_path.stem}-{i:02d}.yaml")
441
+ if not report_path.resolve().exists():
442
+ break
443
+
444
+ report_data = {
445
+ "export_metadata": {
446
+ "export_time": dt.datetime.now(tz=None).strftime("%Y-%m-%d %H:%M:%S"),
447
+ "excel2moodle_version": __version__,
448
+ },
449
+ "categories": {},
450
+ }
451
+ if qCount != 0:
452
+ report_data["export_metadata"]["question_count"] = qCount
453
+ if pCount != 0:
454
+ report_data["export_metadata"]["total_point_count"] = pCount
455
+
456
+ sorted_questions = sorted(
457
+ self._exportedQuestions, key=lambda q: (q.category.name, q.id)
458
+ )
459
+
460
+ for question in sorted_questions:
461
+ category_name = question.category.name
462
+ if category_name not in report_data["categories"]:
463
+ report_data["categories"][category_name] = {
464
+ "description": question.category.desc,
465
+ "questions": [],
466
+ }
467
+
468
+ question_data = {"id": question.id, "name": question.name}
469
+ if isinstance(question, ParametricQuestion) and question.currentVariant > 0:
470
+ if self._exportedAll:
471
+ question_data["exported_variant"] = "all"
472
+ else:
473
+ question_data["exported_variant"] = question.currentVariant + 1
474
+
475
+ report_data["categories"][category_name]["questions"].append(question_data)
476
+
477
+ with report_path.open("w") as f:
478
+ yaml.dump(report_data, f, sort_keys=False)
@@ -5,9 +5,6 @@ This module host different functions. All of them will return an ``lxml.etree.El
5
5
 
6
6
  import lxml.etree as ET
7
7
 
8
- import excel2moodle.core.etHelpers as eth
9
- from excel2moodle.core.globals import TextElements, feedbackStr, feedBElements
10
-
11
8
  from .globals import Tags, XMLTags
12
9
 
13
10
 
@@ -48,20 +45,3 @@ def getCdatTxtElement(subEle: ET._Element | list[ET._Element]) -> ET.Element:
48
45
  ET.tostring(subEle, encoding="unicode", pretty_print=True),
49
46
  )
50
47
  return textEle
51
-
52
-
53
- def getFeedBEle(
54
- feedback: XMLTags,
55
- text: str | None = None,
56
- style: TextElements | None = None,
57
- ) -> ET.Element:
58
- """Gets ET Elements with the feedback for the question."""
59
- span = feedBElements[feedback] if style is None else style.create()
60
- if text is None:
61
- text = feedbackStr[feedback]
62
- ele = ET.Element(feedback, format="html")
63
- par = TextElements.PLEFT.create()
64
- span.text = text
65
- par.append(span)
66
- ele.append(eth.getCdatTxtElement(par))
67
- return ele
@@ -89,12 +89,3 @@ feedBElements = {
89
89
  XMLTags.ANSFEEDBACK: TextElements.SPANGREEN.create(),
90
90
  XMLTags.GENFEEDB: TextElements.SPANGREEN.create(),
91
91
  }
92
- feedbackStr = {
93
- XMLTags.CORFEEDB: "Die Frage wurde richtig beantwortet",
94
- XMLTags.PCORFEEDB: "Die Frage wurde teilweise richtig beantwortet",
95
- XMLTags.INCORFEEDB: "Die Frage wurde Falsch beantwortet",
96
- XMLTags.GENFEEDB: "Sie haben eine Antwort abgegeben",
97
- "right": "richtig",
98
- "wrong": "falsch",
99
- "right1Percent": "Gratultaion, die Frage wurde im Rahmen der Toleranz richtig beantwortet",
100
- }
@@ -9,7 +9,6 @@ from excel2moodle.core.globals import (
9
9
  Tags,
10
10
  TextElements,
11
11
  XMLTags,
12
- feedbackStr,
13
12
  feedBElements,
14
13
  )
15
14
  from excel2moodle.core.question import Picture, Question
@@ -30,7 +29,6 @@ class QuestionParser:
30
29
 
31
30
  def __init__(self) -> None:
32
31
  """Initialize the general Question parser."""
33
- self.genFeedbacks: list[XMLTags] = []
34
32
  self.logger: logging.LoggerAdapter
35
33
 
36
34
  def setup(self, question: Question) -> None:
@@ -84,7 +82,7 @@ class QuestionParser:
84
82
  It uses the data from ``self.rawInput`` if ``text`` is type``DFIndex``
85
83
  Otherwise the value of ``text`` will be inserted.
86
84
  """
87
- t = self.rawInput[text] if isinstance(text, Tags) else text
85
+ t = self.rawInput.get(text) if isinstance(text, Tags) else text
88
86
  if txtEle is False:
89
87
  self.tmpEle.append(eth.getElement(eleName, t, **attribs))
90
88
  elif txtEle is True:
@@ -111,12 +109,11 @@ class QuestionParser:
111
109
  self.appendToTmpEle(XMLTags.NAME, text=Tags.NAME, txtEle=True)
112
110
  self.appendToTmpEle(XMLTags.ID, text=self.question.id)
113
111
  textRootElem = ET.Element(XMLTags.QTEXT, format="html")
114
- mainTextEle = ET.SubElement(textRootElem, "text")
112
+ self.mainTextEle = ET.SubElement(textRootElem, "text")
115
113
  self.tmpEle.append(textRootElem)
116
- self.appendToTmpEle(XMLTags.POINTS, text=str(self.question.points))
114
+ if self.question.qtype != "CLOZE":
115
+ self.appendToTmpEle(XMLTags.POINTS, text=str(self.question.points))
117
116
  self._appendStandardTags()
118
- for feedb in self.genFeedbacks:
119
- self.tmpEle.append(eth.getFeedBEle(feedb))
120
117
 
121
118
  self.htmlRoot = ET.Element("div")
122
119
  self.htmlRoot.append(self.getMainTextElement())
@@ -139,10 +136,17 @@ class QuestionParser:
139
136
  if ansList is not None:
140
137
  for ele in ansList:
141
138
  self.tmpEle.append(ele)
139
+ self._finalizeParsing()
140
+
141
+ def _finalizeParsing(self) -> None:
142
+ """Pass the parsed element trees to the question.
143
+
144
+ Intended for the subclasses to do extra stuff.
145
+ """
142
146
  self.question._element = self.tmpEle
143
147
  self.question.htmlRoot = self.htmlRoot
144
148
  self.question.isParsed = True
145
- self.question.textElement = mainTextEle
149
+ self.question.textElement = self.mainTextEle
146
150
  self.logger.info("Sucessfully parsed")
147
151
 
148
152
  def getFeedBEle(
@@ -153,7 +157,8 @@ class QuestionParser:
153
157
  ) -> ET.Element:
154
158
  span = feedBElements[feedback] if style is None else style.create()
155
159
  if text is None:
156
- text = feedbackStr[feedback]
160
+ self.logger.error("Giving a feedback without providing text is nonsens")
161
+ text = self.rawInput.get(Tags.GENERALFB)
157
162
  ele = ET.Element(feedback, format="html")
158
163
  par = TextElements.PLEFT.create()
159
164
  span.text = text
@@ -169,6 +174,7 @@ class QuestionParser:
169
174
  self,
170
175
  result: float = 0.0,
171
176
  fraction: float = 100,
177
+ feedback: str | None = None,
172
178
  format: str = "moodle_auto_format",
173
179
  ) -> ET.Element:
174
180
  """Get ``<answer/>`` Element specific for the numerical Question.
@@ -184,11 +190,13 @@ class QuestionParser:
184
190
  fraction=str(fraction),
185
191
  format=format,
186
192
  )
193
+ if feedback is None:
194
+ feedback = self.rawInput.get(Tags.TRUEFB)
187
195
  ansEle.append(
188
- eth.getFeedBEle(
189
- XMLTags.ANSFEEDBACK,
190
- feedbackStr["right1Percent"],
191
- TextElements.SPANGREEN,
196
+ self.getFeedBEle(
197
+ feedback=XMLTags.ANSFEEDBACK,
198
+ text=feedback,
199
+ style=TextElements.SPANGREEN,
192
200
  ),
193
201
  )
194
202
  absTolerance = round(result * self.rawInput.get(Tags.TOLERANCE), 4)