excel2moodle 0.6.0__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.
- excel2moodle/__main__.py +3 -2
- excel2moodle/core/category.py +24 -6
- excel2moodle/core/dataStructure.py +116 -27
- excel2moodle/core/etHelpers.py +0 -20
- excel2moodle/core/globals.py +0 -9
- excel2moodle/core/parser.py +21 -13
- excel2moodle/core/question.py +43 -23
- excel2moodle/core/settings.py +11 -4
- excel2moodle/extra/equationVerification.py +0 -2
- excel2moodle/question_types/cloze.py +116 -83
- excel2moodle/question_types/mc.py +15 -10
- excel2moodle/question_types/nf.py +7 -1
- excel2moodle/question_types/nfm.py +14 -10
- excel2moodle/ui/UI_exportSettingsDialog.py +60 -14
- excel2moodle/ui/appUi.py +21 -7
- excel2moodle/ui/dialogs.py +12 -0
- {excel2moodle-0.6.0.dist-info → excel2moodle-0.6.2.dist-info}/METADATA +53 -19
- excel2moodle-0.6.2.dist-info/RECORD +39 -0
- excel2moodle-0.6.0.dist-info/RECORD +0 -39
- {excel2moodle-0.6.0.dist-info → excel2moodle-0.6.2.dist-info}/WHEEL +0 -0
- {excel2moodle-0.6.0.dist-info → excel2moodle-0.6.2.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.6.0.dist-info → excel2moodle-0.6.2.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.6.0.dist-info → excel2moodle-0.6.2.dist-info}/top_level.txt +0 -0
@@ -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 =
|
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) ->
|
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
|
112
|
+
return 0
|
106
113
|
self.question.logger.error("Invalid call to points of unparsed cloze part")
|
107
|
-
return 0
|
114
|
+
return 0
|
108
115
|
|
109
116
|
@points.setter
|
110
|
-
def points(self, points:
|
111
|
-
self._points =
|
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) ->
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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 =
|
237
|
-
|
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
|
-
|
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 =
|
274
|
-
|
275
|
-
|
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 =
|
283
|
-
part.trueAnswers,
|
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
|
-
|
73
|
+
self.getFeedBEle(
|
80
74
|
XMLTags.ANSFEEDBACK,
|
81
|
-
text=
|
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
|
-
|
83
|
+
self.getFeedBEle(
|
90
84
|
XMLTags.ANSFEEDBACK,
|
91
|
-
text=
|
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.
|
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.
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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 '
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
-
|
74
|
-
|
75
|
-
self.label_2.setFont(
|
77
|
+
font1 = QFont()
|
78
|
+
font1.setPointSize(12)
|
79
|
+
self.label_2.setFont(font1)
|
76
80
|
|
77
|
-
self.formLayout_2.setWidget(
|
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(
|
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(
|
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(
|
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.
|
77
|
-
|
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.
|
181
|
-
self.
|
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
|
-
|
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(
|
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()
|
excel2moodle/ui/dialogs.py
CHANGED
@@ -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,
|