excel2moodle 0.6.1__py3-none-any.whl → 0.6.2__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.
@@ -5,6 +5,7 @@ All Answers are calculated off an equation using the same variables.
5
5
  """
6
6
 
7
7
  import logging
8
+ import math
8
9
  import re
9
10
  from typing import Literal, overload
10
11
 
@@ -18,6 +19,7 @@ from excel2moodle.core.globals import (
18
19
  from excel2moodle.core.question import (
19
20
  ParametricQuestion,
20
21
  Parametrics,
22
+ QuestionData,
21
23
  )
22
24
  from excel2moodle.core.settings import Tags
23
25
  from excel2moodle.logger import LogAdapterQuestionID
@@ -67,7 +69,8 @@ class ClozePart:
67
69
  return
68
70
  if self.typ == "NFM":
69
71
  result = self.result.getResult(variant)
70
- self._element.text = ClozeQuestionParser.getNumericAnsStr(
72
+ self._element.text = self.getNumericAnsStr(
73
+ self.question.rawData,
71
74
  result,
72
75
  self.question.rawData.get(Tags.TOLERANCE),
73
76
  wrongSignCount=self.question.rawData.get(Tags.WRONGSIGNPERCENT),
@@ -99,16 +102,20 @@ class ClozePart:
99
102
  return f"{self.question.id}-{self.num}"
100
103
 
101
104
  @property
102
- def points(self) -> float:
105
+ def points(self) -> int:
106
+ """Points of clozes can be only integers.
107
+
108
+ Otherwise the moodle import fails.
109
+ """
103
110
  if hasattr(self, "_points"):
104
111
  return self._points
105
- return 0.0
112
+ return 0
106
113
  self.question.logger.error("Invalid call to points of unparsed cloze part")
107
- return 0.0
114
+ return 0
108
115
 
109
116
  @points.setter
110
- def points(self, points: float) -> None:
111
- self._points = points if points > 0 else 0.0
117
+ def points(self, points: int) -> None:
118
+ self._points = max(0, points)
112
119
 
113
120
  @property
114
121
  def mcAnswerString(self) -> str:
@@ -131,6 +138,73 @@ class ClozePart:
131
138
  def __repr__(self) -> str:
132
139
  return f"Cloze Part {self.id}-{self.typ}"
133
140
 
141
+ @staticmethod
142
+ def getNumericAnsStr(
143
+ questionData: QuestionData,
144
+ result: float,
145
+ tolerance: float = 0.0,
146
+ points: int = 1,
147
+ wrongSignCount: int = 0,
148
+ wrongSignFeedback: str | None = None,
149
+ ) -> str:
150
+ """Generate the answer string from `result`.
151
+
152
+ Parameters.
153
+ ----------
154
+ wrongSignCount:
155
+ If the wrong sign `+` or `-` is given, how much of the points should be given.
156
+ Interpreted as percent.
157
+ tolerance:
158
+ The relative tolerance, as fraction
159
+
160
+ """
161
+ if wrongSignFeedback is None:
162
+ wrongSignFeedback = questionData.get(Tags.WRONGSIGNFB)
163
+ if wrongSignCount == 0:
164
+ wrongSignCount = questionData.get(Tags.WRONGSIGNPERCENT)
165
+ if tolerance == 0.0:
166
+ tolerance = questionData.get(Tags.TOLERANCE)
167
+ absTol = f":{round(result * tolerance, 3)}"
168
+ answerParts: list[str | float] = [
169
+ "{",
170
+ points,
171
+ ":NUMERICAL:=",
172
+ round(result, 3),
173
+ absTol,
174
+ "~%",
175
+ wrongSignCount,
176
+ "%",
177
+ round(result * (-1), 3),
178
+ absTol,
179
+ f"#{wrongSignFeedback}",
180
+ "}",
181
+ ]
182
+ answerPStrings = [str(part) for part in answerParts]
183
+ return "".join(answerPStrings)
184
+
185
+ @staticmethod
186
+ def getMCAnsStr(
187
+ true: list[str],
188
+ false: list[str],
189
+ points: int = 1,
190
+ ) -> str:
191
+ """Generate the answer string for the MC answers."""
192
+ truePercent: float = round(100 / len(true), 1)
193
+ falsePercent: float = round(100 / len(false), 1)
194
+ falseList: list[str] = [f"~%-{falsePercent}%{ans}" for ans in false]
195
+ trueList: list[str] = [f"~%{truePercent}%{ans}" for ans in true]
196
+ answerParts: list[str | float] = [
197
+ "{",
198
+ points,
199
+ ":MULTIRESPONSE:",
200
+ ]
201
+ answerParts.extend(trueList)
202
+ answerParts.extend(falseList)
203
+ answerParts.append("}")
204
+
205
+ answerPStrings = [str(part) for part in answerParts]
206
+ return "".join(answerPStrings)
207
+
134
208
 
135
209
  class ClozeQuestion(ParametricQuestion):
136
210
  """Cloze Question Type."""
@@ -146,13 +220,18 @@ class ClozeQuestion(ParametricQuestion):
146
220
  return len(self.questionParts)
147
221
 
148
222
  @property
149
- def points(self) -> float:
150
- pts: float = 0
151
- if self.isParsed:
152
- for p in self.questionParts.values():
153
- pts = pts + p.points
154
- else:
155
- pts = self.rawData.get(Tags.POINTS)
223
+ def points(self) -> int:
224
+ """Points for the cloze question. The sum of all its parts points.
225
+
226
+ Returns only integer values.
227
+ """
228
+ pts: int = 0
229
+ if not self.isParsed:
230
+ msg = "The Cloze question has no points because it is not yet parsed"
231
+ logger.warning(msg)
232
+ return pts
233
+ for p in self.questionParts.values():
234
+ pts = pts + p.points
156
235
  return pts
157
236
 
158
237
  def getUpdatedElement(self, variant: int = 0) -> ET.Element:
@@ -232,17 +311,30 @@ class ClozeQuestionParser(NFMQuestionParser):
232
311
  raise QNotParsedException(msg, self.question.id)
233
312
  if len(points) == 0:
234
313
  pts = round(self.rawInput.get(Tags.POINTS) / partsNum, 3)
314
+ point = self._roundClozePartPoints(pts)
235
315
  for part in parts.values():
236
- part.points = pts
237
- elif len(points) != partsNum:
316
+ part.points = point
317
+ else:
238
318
  logger.warning(
239
319
  "Some Answer parts are missing the points, they will get the standard points"
240
320
  )
241
321
  for num, part in parts.items():
242
- p = points.get(num)
243
- part.points = p if p is not None else self.rawInput.get(Tags.POINTS)
322
+ part.points = self._roundClozePartPoints(points=points.get(num))
244
323
  self.question.questionParts = parts
245
324
 
325
+ def _roundClozePartPoints(self, points: float | None = None) -> int:
326
+ """Get the integer points for the cloze part."""
327
+ if points is None:
328
+ points = self.rawInput.get(Tags.POINTS)
329
+ corrPoints: int = round(points)
330
+ if not math.isclose(corrPoints, points):
331
+ logger.warning(
332
+ "Type cloze supports only integers as points. %s was round to %s",
333
+ points,
334
+ corrPoints,
335
+ )
336
+ return corrPoints
337
+
246
338
  @overload
247
339
  def _getPartValues(self, Tag: Literal[Tags.RESULT]) -> dict[int, str]: ...
248
340
  @overload
@@ -270,17 +362,18 @@ class ClozeQuestionParser(NFMQuestionParser):
270
362
  for partNum, part in self.question.questionParts.items():
271
363
  if part.typ == "NFM":
272
364
  result = self.question.parametrics.getResult(1, partNum)
273
- ansStr = self.getNumericAnsStr(
274
- result,
275
- self.rawInput.get(Tags.TOLERANCE),
276
- wrongSignCount=self.rawInput.get(Tags.WRONGSIGNPERCENT),
365
+ ansStr = ClozePart.getNumericAnsStr(
366
+ self.rawInput,
367
+ result=result,
277
368
  points=part.points,
278
369
  )
279
370
  self.logger.info("NF answer part: %s ", ansStr)
280
371
  logger.debug("Appended NF part %s result", partNum)
281
372
  elif part.typ == "MC":
282
- ansStr = self.getMCAnsStr(
283
- part.trueAnswers, part.falseAnswers, points=part.points
373
+ ansStr = ClozePart.getMCAnsStr(
374
+ part.trueAnswers,
375
+ part.falseAnswers,
376
+ points=part.points,
284
377
  )
285
378
  part.mcAnswerString = ansStr
286
379
  logger.debug("Appended MC part %s: %s", partNum, ansStr)
@@ -311,63 +404,3 @@ class ClozeQuestionParser(NFMQuestionParser):
311
404
  raise QNotParsedException(msg, self.question.id)
312
405
  else:
313
406
  return int(num)
314
-
315
- @staticmethod
316
- def getNumericAnsStr(
317
- result: float,
318
- tolerance: float,
319
- points: float = 1,
320
- wrongSignCount: int = 50,
321
- wrongSignFeedback: str = "your result has the wrong sign (+-)",
322
- ) -> str:
323
- """Generate the answer string from `result`.
324
-
325
- Parameters.
326
- ----------
327
- wrongSignCount:
328
- If the wrong sign `+` or `-` is given, how much of the points should be given.
329
- Interpreted as percent.
330
- tolerance:
331
- The relative tolerance, as fraction
332
-
333
- """
334
- absTol = f":{round(result * tolerance, 3)}"
335
- answerParts: list[str | float] = [
336
- "{",
337
- points,
338
- ":NUMERICAL:=",
339
- round(result, 3),
340
- absTol,
341
- "~%",
342
- wrongSignCount,
343
- "%",
344
- round(result * (-1), 3),
345
- absTol,
346
- f"#{wrongSignFeedback}",
347
- "}",
348
- ]
349
- answerPStrings = [str(part) for part in answerParts]
350
- return "".join(answerPStrings)
351
-
352
- @staticmethod
353
- def getMCAnsStr(
354
- true: list[str],
355
- false: list[str],
356
- points: float = 1,
357
- ) -> str:
358
- """Generate the answer string for the MC answers."""
359
- truePercent: float = round(100 / len(true), 1)
360
- falsePercent: float = round(100 / len(false), 1)
361
- falseList: list[str] = [f"~%-{falsePercent}%{ans}" for ans in false]
362
- trueList: list[str] = [f"~%{truePercent}%{ans}" for ans in true]
363
- answerParts: list[str | float] = [
364
- "{",
365
- points,
366
- ":MULTIRESPONSE:",
367
- ]
368
- answerParts.extend(trueList)
369
- answerParts.extend(falseList)
370
- answerParts.append("}")
371
-
372
- answerPStrings = [str(part) for part in answerParts]
373
- return "".join(answerPStrings)
@@ -12,7 +12,6 @@ from excel2moodle.core.globals import (
12
12
  Tags,
13
13
  TextElements,
14
14
  XMLTags,
15
- feedbackStr,
16
15
  )
17
16
  from excel2moodle.core.parser import QuestionParser
18
17
  from excel2moodle.core.question import Picture, Question
@@ -46,11 +45,6 @@ class MCQuestionParser(QuestionParser):
46
45
 
47
46
  def __init__(self) -> None:
48
47
  super().__init__()
49
- self.genFeedbacks = [
50
- XMLTags.CORFEEDB,
51
- XMLTags.PCORFEEDB,
52
- XMLTags.INCORFEEDB,
53
- ]
54
48
 
55
49
  def setup(self, question: MCQuestion) -> None:
56
50
  self.question: MCQuestion = question
@@ -76,9 +70,9 @@ class MCQuestionParser(QuestionParser):
76
70
  elementList[-1].append(text)
77
71
  if fraction < 0:
78
72
  elementList[-1].append(
79
- eth.getFeedBEle(
73
+ self.getFeedBEle(
80
74
  XMLTags.ANSFEEDBACK,
81
- text=feedbackStr["wrong"],
75
+ text=self.rawInput.get(Tags.FALSEFB),
82
76
  style=TextElements.SPANRED,
83
77
  ),
84
78
  )
@@ -86,9 +80,9 @@ class MCQuestionParser(QuestionParser):
86
80
  elementList[-1].append(self.falseImgs[i].element)
87
81
  elif fraction > 0:
88
82
  elementList[-1].append(
89
- eth.getFeedBEle(
83
+ self.getFeedBEle(
90
84
  XMLTags.ANSFEEDBACK,
91
- text=feedbackStr["right"],
85
+ text=self.rawInput.get(Tags.TRUEFB),
92
86
  style=TextElements.SPANGREEN,
93
87
  ),
94
88
  )
@@ -135,3 +129,14 @@ class MCQuestionParser(QuestionParser):
135
129
  self.getAnsElementsList(falseAList, fraction=round(falsefrac, 4)),
136
130
  )
137
131
  return ansList
132
+
133
+ def parse(self) -> None:
134
+ super().parse()
135
+ feedBacks = {
136
+ XMLTags.CORFEEDB: Tags.TRUEFB,
137
+ XMLTags.PCORFEEDB: Tags.PCORRECFB,
138
+ XMLTags.INCORFEEDB: Tags.FALSEFB,
139
+ }
140
+ for feedb, tag in feedBacks.items():
141
+ self.tmpEle.append(self.getFeedBEle(feedb, text=self.rawInput.get(tag)))
142
+ self._finalizeParsing()
@@ -30,7 +30,7 @@ class NFQuestionParser(QuestionParser):
30
30
 
31
31
  def __init__(self) -> None:
32
32
  super().__init__()
33
- self.genFeedbacks = [XMLTags.GENFEEDB]
33
+ self.feedBackList = {XMLTags.GENFEEDB: Tags.GENERALFB}
34
34
 
35
35
  def setup(self, question: NFQuestion) -> None:
36
36
  self.question: NFQuestion = question
@@ -41,3 +41,9 @@ class NFQuestionParser(QuestionParser):
41
41
  ansEle: list[ET.Element] = []
42
42
  ansEle.append(self.getNumericAnsElement(result=result))
43
43
  return ansEle
44
+
45
+ def _finalizeParsing(self) -> None:
46
+ self.tmpEle.append(
47
+ self.getFeedBEle(XMLTags.GENFEEDB, text=self.rawInput.get(Tags.GENERALFB))
48
+ )
49
+ return super()._finalizeParsing()
@@ -22,6 +22,7 @@ class NFMQuestion(ParametricQuestion):
22
22
  def __init__(self, *args, **kwargs) -> None:
23
23
  super().__init__(*args, **kwargs)
24
24
  self.answerElement: ET.Element
25
+ self.answerElementWrongSign: ET.Element
25
26
 
26
27
  def getUpdatedElement(self, variant: int = 1) -> ET.Element:
27
28
  """Update and get the Question Elements to reflect the version.
@@ -33,14 +34,16 @@ class NFMQuestion(ParametricQuestion):
33
34
  result = self.parametrics.getResult(variant)
34
35
  tolerance = round(self.rawData.get(Tags.TOLERANCE) * result, 3)
35
36
  self.answerElement.find(XMLTags.TEXT).text = str(result)
37
+ self.answerElementWrongSign.find(XMLTags.TEXT).text = str(result * (-1))
36
38
  self.answerElement.find(XMLTags.TOLERANCE).text = str(tolerance)
39
+ self.answerElementWrongSign.find(XMLTags.TOLERANCE).text = str(tolerance)
37
40
  return super().getUpdatedElement(variant)
38
41
 
39
42
 
40
43
  class NFMQuestionParser(QuestionParser):
41
44
  def __init__(self) -> None:
42
45
  super().__init__()
43
- self.genFeedbacks = [XMLTags.GENFEEDB]
46
+ self.feedBackList = {XMLTags.GENFEEDB: Tags.GENERALFB}
44
47
  self.question: NFMQuestion
45
48
 
46
49
  def setup(self, question: NFMQuestion) -> None:
@@ -61,13 +64,14 @@ class NFMQuestionParser(QuestionParser):
61
64
  )
62
65
  self.question.parametrics.variables = variables
63
66
  self.question.answerElement = self.getNumericAnsElement()
64
- return [self.question.answerElement]
67
+ self.question.answerElementWrongSign = self.getNumericAnsElement(
68
+ fraction=self.rawInput.get(Tags.WRONGSIGNPERCENT),
69
+ feedback=self.rawInput.get(Tags.WRONGSIGNFB),
70
+ )
71
+ return [self.question.answerElement, self.question.answerElementWrongSign]
65
72
 
66
- # TODO: @jbosse3: Implement a new _setVariants() method, to show in treewidget
67
- # def _setVariants(self, number: int) -> None:
68
- # self.question.variants = number
69
- # mvar = self.question.category.maxVariants
70
- # if mvar is None:
71
- # self.question.category.maxVariants = number
72
- # else:
73
- # self.question.category.maxVariants = min(number, mvar)
73
+ def _finalizeParsing(self) -> None:
74
+ self.tmpEle.append(
75
+ self.getFeedBEle(XMLTags.GENFEEDB, text=self.rawInput.get(Tags.GENERALFB))
76
+ )
77
+ return super()._finalizeParsing()
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  ################################################################################
4
- ## Form generated from reading UI file 'exportSettingsDialog.ui'
4
+ ## Form generated from reading UI file 'UI_exportSettingsDialog.ui'
5
5
  ##
6
6
  ## Created by: Qt User Interface Compiler version 6.9.1
7
7
  ##
@@ -43,38 +43,42 @@ class Ui_ExportDialog(object):
43
43
  self.label_9 = QLabel(ExportDialog)
44
44
  self.label_9.setObjectName(u"label_9")
45
45
 
46
- self.formLayout_2.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_9)
46
+ self.formLayout_2.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label_9)
47
47
 
48
48
  self.checkBoxIncludeCategories = QCheckBox(ExportDialog)
49
49
  self.checkBoxIncludeCategories.setObjectName(u"checkBoxIncludeCategories")
50
50
 
51
- self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.checkBoxIncludeCategories)
51
+ self.formLayout_2.setWidget(2, QFormLayout.ItemRole.FieldRole, self.checkBoxIncludeCategories)
52
52
 
53
53
  self.label = QLabel(ExportDialog)
54
54
  self.label.setObjectName(u"label")
55
+ font = QFont()
56
+ font.setPointSize(12)
57
+ font.setBold(True)
58
+ self.label.setFont(font)
55
59
 
56
- self.formLayout_2.setWidget(2, QFormLayout.ItemRole.LabelRole, self.label)
60
+ self.formLayout_2.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label)
57
61
 
58
62
  self.btnExportFile = QPushButton(ExportDialog)
59
63
  self.btnExportFile.setObjectName(u"btnExportFile")
60
64
  self.btnExportFile.setMinimumSize(QSize(200, 0))
61
65
 
62
- self.formLayout_2.setWidget(2, QFormLayout.ItemRole.FieldRole, self.btnExportFile)
66
+ self.formLayout_2.setWidget(4, QFormLayout.ItemRole.FieldRole, self.btnExportFile)
63
67
 
64
68
  self.line_2 = QFrame(ExportDialog)
65
69
  self.line_2.setObjectName(u"line_2")
66
70
  self.line_2.setFrameShape(QFrame.Shape.HLine)
67
71
  self.line_2.setFrameShadow(QFrame.Shadow.Sunken)
68
72
 
69
- self.formLayout_2.setWidget(3, QFormLayout.ItemRole.LabelRole, self.line_2)
73
+ self.formLayout_2.setWidget(5, QFormLayout.ItemRole.LabelRole, self.line_2)
70
74
 
71
75
  self.label_2 = QLabel(ExportDialog)
72
76
  self.label_2.setObjectName(u"label_2")
73
- font = QFont()
74
- font.setPointSize(12)
75
- self.label_2.setFont(font)
77
+ font1 = QFont()
78
+ font1.setPointSize(12)
79
+ self.label_2.setFont(font1)
76
80
 
77
- self.formLayout_2.setWidget(4, QFormLayout.ItemRole.LabelRole, self.label_2)
81
+ self.formLayout_2.setWidget(6, QFormLayout.ItemRole.LabelRole, self.label_2)
78
82
 
79
83
  self.questionCount = QSpinBox(ExportDialog)
80
84
  self.questionCount.setObjectName(u"questionCount")
@@ -82,7 +86,7 @@ class Ui_ExportDialog(object):
82
86
  self.questionCount.setButtonSymbols(QAbstractSpinBox.ButtonSymbols.NoButtons)
83
87
  self.questionCount.setMaximum(999)
84
88
 
85
- self.formLayout_2.setWidget(5, QFormLayout.ItemRole.LabelRole, self.questionCount)
89
+ self.formLayout_2.setWidget(7, QFormLayout.ItemRole.LabelRole, self.questionCount)
86
90
 
87
91
  self.pointCount = QDoubleSpinBox(ExportDialog)
88
92
  self.pointCount.setObjectName(u"pointCount")
@@ -93,7 +97,27 @@ class Ui_ExportDialog(object):
93
97
  self.pointCount.setDecimals(1)
94
98
  self.pointCount.setMaximum(1000.000000000000000)
95
99
 
96
- self.formLayout_2.setWidget(5, QFormLayout.ItemRole.FieldRole, self.pointCount)
100
+ self.formLayout_2.setWidget(7, QFormLayout.ItemRole.FieldRole, self.pointCount)
101
+
102
+ self.label_3 = QLabel(ExportDialog)
103
+ self.label_3.setObjectName(u"label_3")
104
+
105
+ self.formLayout_2.setWidget(1, QFormLayout.ItemRole.LabelRole, self.label_3)
106
+
107
+ self.checkBoxExportAll = QCheckBox(ExportDialog)
108
+ self.checkBoxExportAll.setObjectName(u"checkBoxExportAll")
109
+
110
+ self.formLayout_2.setWidget(1, QFormLayout.ItemRole.FieldRole, self.checkBoxExportAll)
111
+
112
+ self.label_4 = QLabel(ExportDialog)
113
+ self.label_4.setObjectName(u"label_4")
114
+
115
+ self.formLayout_2.setWidget(3, QFormLayout.ItemRole.LabelRole, self.label_4)
116
+
117
+ self.checkBoxGenerateReport = QCheckBox(ExportDialog)
118
+ self.checkBoxGenerateReport.setObjectName(u"checkBoxGenerateReport")
119
+
120
+ self.formLayout_2.setWidget(3, QFormLayout.ItemRole.FieldRole, self.checkBoxGenerateReport)
97
121
 
98
122
 
99
123
  self.horizontalLayout.addLayout(self.formLayout_2)
@@ -117,14 +141,36 @@ class Ui_ExportDialog(object):
117
141
  ExportDialog.setWindowTitle(QCoreApplication.translate("ExportDialog", u"Dialog", None))
118
142
  self.label_10.setText(QCoreApplication.translate("ExportDialog", u"Default Question Variant", None))
119
143
  #if QT_CONFIG(tooltip)
120
- self.label_9.setToolTip(QCoreApplication.translate("ExportDialog", u"If enabled, all questions will be categorized, when importing into moodle. Otherwise they will all be imported into one category", None))
144
+ self.label_9.setToolTip("")
121
145
  #endif // QT_CONFIG(tooltip)
122
146
  self.label_9.setText(QCoreApplication.translate("ExportDialog", u"Include Categories", None))
147
+ #if QT_CONFIG(tooltip)
148
+ self.checkBoxIncludeCategories.setToolTip(QCoreApplication.translate("ExportDialog", u"If enabled, all questions will be categorized, when importing into moodle. Otherwise they will all be imported into one category", None))
149
+ #endif // QT_CONFIG(tooltip)
123
150
  self.checkBoxIncludeCategories.setText("")
124
- self.label.setText(QCoreApplication.translate("ExportDialog", u"Export File:", None))
151
+ self.label.setText(QCoreApplication.translate("ExportDialog", u"Select Export File:", None))
152
+ #if QT_CONFIG(tooltip)
153
+ self.btnExportFile.setToolTip(QCoreApplication.translate("ExportDialog", u"Select the file to write the questions into.", None))
154
+ #endif // QT_CONFIG(tooltip)
125
155
  self.btnExportFile.setText("")
126
156
  self.label_2.setText(QCoreApplication.translate("ExportDialog", u"Export Information:", None))
127
157
  self.questionCount.setSuffix(QCoreApplication.translate("ExportDialog", u" Qst.", None))
128
158
  self.pointCount.setSuffix(QCoreApplication.translate("ExportDialog", u" Pts.", None))
159
+ #if QT_CONFIG(tooltip)
160
+ self.label_3.setToolTip("")
161
+ #endif // QT_CONFIG(tooltip)
162
+ self.label_3.setText(QCoreApplication.translate("ExportDialog", u"Export all Variants", None))
163
+ #if QT_CONFIG(tooltip)
164
+ self.checkBoxExportAll.setToolTip(QCoreApplication.translate("ExportDialog", u"If enable all variants for each question are exported into a seperate category for each question", None))
165
+ #endif // QT_CONFIG(tooltip)
166
+ self.checkBoxExportAll.setText("")
167
+ #if QT_CONFIG(tooltip)
168
+ self.label_4.setToolTip("")
169
+ #endif // QT_CONFIG(tooltip)
170
+ self.label_4.setText(QCoreApplication.translate("ExportDialog", u"Generate export report", None))
171
+ #if QT_CONFIG(tooltip)
172
+ self.checkBoxGenerateReport.setToolTip(QCoreApplication.translate("ExportDialog", u"If enabled a yaml report file will be saved with a list of all exported questions and variants.", None))
173
+ #endif // QT_CONFIG(tooltip)
174
+ self.checkBoxGenerateReport.setText("")
129
175
  # retranslateUi
130
176
 
excel2moodle/ui/appUi.py CHANGED
@@ -73,9 +73,12 @@ class MainWindow(QMainWindow):
73
73
  self.exportDialog.ui.checkBoxIncludeCategories.setChecked(
74
74
  self.qSettings.value(Tags.INCLUDEINCATS, defaultValue=True, type=bool)
75
75
  )
76
- self.exportDialog.ui.spinBoxDefaultQVariant.setValue(
77
- self.qSettings.value(Tags.QUESTIONVARIANT, defaultValue=1, type=int)
78
- )
76
+ variant = self.qSettings.value(Tags.QUESTIONVARIANT, defaultValue=1, type=int)
77
+ if variant == -1:
78
+ self.exportDialog.ui.checkBoxExportAll.setChecked(True)
79
+ self.exportDialog.ui.spinBoxDefaultQVariant.setEnabled(False)
80
+ else:
81
+ self.exportDialog.ui.spinBoxDefaultQVariant.setValue(variant)
79
82
  try:
80
83
  self.resize(self.qSettings.value("windowSize"))
81
84
  self.move(self.qSettings.value("windowPosition"))
@@ -177,8 +180,10 @@ class MainWindow(QMainWindow):
177
180
  """Open a file Dialog so the export file may be choosen."""
178
181
  selection: list[QuestionItem] = self.ui.treeWidget.selectedItems()
179
182
  self.exportDialog.exportFile = Path(self.mainPath / "TestFile.xml")
180
- self.exportDialog.ui.questionCount.setValue(self.ui.questionCounter.value())
181
- self.exportDialog.ui.pointCount.setValue(self.ui.pointCounter.value())
183
+ qCount = self.ui.questionCounter.value()
184
+ pCount = self.ui.pointCounter.value()
185
+ self.exportDialog.ui.questionCount.setValue(qCount)
186
+ self.exportDialog.ui.pointCount.setValue(pCount)
182
187
  if self.exportDialog.exec():
183
188
  self.exportFile = self.exportDialog.exportFile
184
189
  self.settings.set(
@@ -187,10 +192,18 @@ class MainWindow(QMainWindow):
187
192
  )
188
193
  self.settings.set(
189
194
  Tags.QUESTIONVARIANT,
190
- self.exportDialog.ui.spinBoxDefaultQVariant.value(),
195
+ -1
196
+ if self.exportDialog.ui.checkBoxExportAll.isChecked()
197
+ else self.exportDialog.ui.spinBoxDefaultQVariant.value(),
198
+ )
199
+ self.settings.set(
200
+ Tags.GENEXPORTREPORT,
201
+ self.exportDialog.ui.checkBoxGenerateReport.isChecked(),
191
202
  )
192
203
  logger.info("New Export File is set %s", self.exportFile)
193
- self.testDB.appendQuestions(selection, self.exportFile)
204
+ self.testDB.appendQuestions(
205
+ selection, self.exportFile, pCount=pCount, qCount=qCount
206
+ )
194
207
  else:
195
208
  logger.info("Aborting Export")
196
209
 
@@ -249,6 +262,7 @@ class MainWindow(QMainWindow):
249
262
  @Slot()
250
263
  def openDocumentation(self) -> None:
251
264
  url = QUrl(e2mMetadata["documentation"])
265
+ logger.info("Opening documentation in your Webbrowser...%s", url)
252
266
  QDesktopServices.openUrl(url)
253
267
 
254
268
  @Slot()
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  import lxml.etree as ET
8
+ from PySide6.QtCore import Slot
8
9
  from PySide6.QtWidgets import QDialog, QFileDialog, QMessageBox, QWidget
9
10
 
10
11
  from excel2moodle import e2mMetadata
@@ -50,6 +51,7 @@ class ExportDialog(QDialog):
50
51
  self.ui = Ui_ExportDialog()
51
52
  self.ui.setupUi(self)
52
53
  self.ui.btnExportFile.clicked.connect(self.getExportFile)
54
+ self.ui.checkBoxExportAll.clicked.connect(self.toggleExportAll)
53
55
 
54
56
  @property
55
57
  def exportFile(self) -> Path:
@@ -62,6 +64,16 @@ class ExportDialog(QDialog):
62
64
  f"../{(self.exportFile.parent.name)}/{self.exportFile.name}"
63
65
  )
64
66
 
67
+ @Slot()
68
+ def toggleExportAll(self) -> None:
69
+ self.ui.spinBoxDefaultQVariant.setEnabled(
70
+ not self.ui.checkBoxExportAll.isChecked()
71
+ )
72
+ self.ui.checkBoxIncludeCategories.setChecked(
73
+ self.ui.checkBoxExportAll.isChecked()
74
+ )
75
+
76
+ @Slot()
65
77
  def getExportFile(self) -> None:
66
78
  path = QFileDialog.getSaveFileName(
67
79
  self,