excel2moodle 0.3.3__py3-none-any.whl → 0.3.5__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/__init__.py +38 -96
- excel2moodle/__main__.py +5 -5
- excel2moodle/core/__init__.py +1 -3
- excel2moodle/core/category.py +67 -47
- excel2moodle/core/dataStructure.py +89 -73
- excel2moodle/core/etHelpers.py +26 -26
- excel2moodle/core/exceptions.py +12 -5
- excel2moodle/core/globals.py +43 -29
- excel2moodle/core/numericMultiQ.py +28 -24
- excel2moodle/core/parser.py +228 -147
- excel2moodle/core/question.py +100 -69
- excel2moodle/core/questionValidator.py +56 -54
- excel2moodle/core/questionWriter.py +232 -139
- excel2moodle/core/stringHelpers.py +38 -34
- excel2moodle/extra/__init__.py +1 -3
- excel2moodle/extra/equationVerification.py +37 -33
- excel2moodle/logger.py +102 -0
- excel2moodle/ui/appUi.py +133 -125
- excel2moodle/ui/dialogs.py +71 -18
- excel2moodle/ui/settings.py +108 -21
- excel2moodle/ui/treewidget.py +13 -10
- excel2moodle/ui/windowMain.py +18 -57
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.5.dist-info}/METADATA +4 -3
- excel2moodle-0.3.5.dist-info/RECORD +33 -0
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.5.dist-info}/WHEEL +1 -1
- excel2moodle-0.3.5.dist-info/entry_points.txt +2 -0
- excel2moodle-0.3.3.dist-info/RECORD +0 -31
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.5.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.5.dist-info}/top_level.txt +0 -0
excel2moodle/core/exceptions.py
CHANGED
@@ -1,20 +1,27 @@
|
|
1
|
-
|
2
1
|
from excel2moodle.core.globals import DFIndex
|
3
2
|
|
4
3
|
|
5
4
|
class QNotParsedException(Exception):
|
6
|
-
def __init__(self, message:str, qID:str|int, *args, **kwargs)->None:
|
5
|
+
def __init__(self, message: str, qID: str | int, *args, **kwargs) -> None:
|
7
6
|
super().__init__(message, *args, **kwargs)
|
8
7
|
self.qID = qID
|
9
8
|
|
9
|
+
|
10
10
|
class NanException(QNotParsedException):
|
11
|
-
def __init__(self, message, qID, field, *args, **kwargs):
|
11
|
+
def __init__(self, message, qID, field, *args, **kwargs) -> None:
|
12
12
|
super().__init__(message, qID, *args, **kwargs)
|
13
13
|
self.field = field
|
14
14
|
|
15
|
+
|
15
16
|
class InvalidFieldException(Exception):
|
16
|
-
def __init__(
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
message: str,
|
20
|
+
qID: str,
|
21
|
+
field: DFIndex | list[DFIndex],
|
22
|
+
*args: object,
|
23
|
+
**kwargs,
|
24
|
+
) -> None:
|
17
25
|
super().__init__(message, *args, **kwargs)
|
18
26
|
self.field = field
|
19
27
|
self.index = qID
|
20
|
-
|
excel2moodle/core/globals.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from enum import Enum, StrEnum
|
2
|
+
|
2
3
|
import lxml.etree as ET
|
3
4
|
|
4
5
|
questionTypes = {
|
@@ -7,14 +8,15 @@ questionTypes = {
|
|
7
8
|
"MC": "multichoice",
|
8
9
|
}
|
9
10
|
|
11
|
+
|
10
12
|
class DFIndex(StrEnum):
|
11
|
-
"""
|
12
|
-
|
13
|
+
"""The identifier string for for the spreadsheet and the string for the xml-tag.
|
14
|
+
|
13
15
|
Each enum corresponds to a list of two values.
|
14
16
|
The first Value is the index in the spreadsheet, the second is the name of the xml-tag
|
15
17
|
"""
|
16
18
|
|
17
|
-
TEXT
|
19
|
+
TEXT = "text"
|
18
20
|
BPOINTS = "bulletPoints"
|
19
21
|
TRUE = "true"
|
20
22
|
FALSE = "false"
|
@@ -24,51 +26,63 @@ class DFIndex(StrEnum):
|
|
24
26
|
PICTURE = "picture"
|
25
27
|
NUMBER = "number"
|
26
28
|
ANSTYPE = "answerType"
|
29
|
+
TOLERANCE = "tolerance"
|
30
|
+
|
27
31
|
|
28
32
|
class TextElements(Enum):
|
29
|
-
PLEFT ="p","text-align: left;"
|
33
|
+
PLEFT = "p", "text-align: left;"
|
30
34
|
SPANRED = "span", "color: rgb(239, 69, 64)"
|
31
35
|
SPANGREEN = "span", "color: rgb(152, 202, 62)"
|
32
|
-
SPANORANGE = "span","color: rgb(152, 100, 100)"
|
33
|
-
ULIST =
|
34
|
-
|
36
|
+
SPANORANGE = "span", "color: rgb(152, 100, 100)"
|
37
|
+
ULIST = (
|
38
|
+
"ul",
|
39
|
+
"",
|
40
|
+
)
|
41
|
+
LISTITEM = (
|
42
|
+
"li",
|
43
|
+
"text-align: left;",
|
44
|
+
)
|
35
45
|
|
36
|
-
def create(self, tag:str|None=None):
|
46
|
+
def create(self, tag: str | None = None):
|
37
47
|
if tag is None:
|
38
48
|
tag, style = self.value
|
39
|
-
else:
|
49
|
+
else:
|
50
|
+
style = self.value[1]
|
40
51
|
return ET.Element(tag, dir="ltr", style=style)
|
41
52
|
|
42
53
|
@property
|
43
|
-
def style(
|
54
|
+
def style(
|
55
|
+
self,
|
56
|
+
) -> str:
|
44
57
|
return self.value[1]
|
45
58
|
|
59
|
+
|
46
60
|
class XMLTags(StrEnum):
|
47
|
-
def __new__(cls, value:str, dfkey: DFIndex|None = None):
|
61
|
+
def __new__(cls, value: str, dfkey: DFIndex | None = None):
|
48
62
|
obj = str.__new__(cls, value)
|
49
63
|
obj._value_ = value
|
50
64
|
if dfkey is not None:
|
51
65
|
obj._dfkey_ = dfkey
|
52
66
|
return obj
|
53
67
|
|
54
|
-
def __init__(self,_:str, dfkey:DFIndex|None =None, getEle=None
|
68
|
+
def __init__(self, _: str, dfkey: DFIndex | None = None, getEle=None) -> None:
|
55
69
|
if isinstance(dfkey, DFIndex):
|
56
|
-
self._dfkey_:str=dfkey
|
70
|
+
self._dfkey_: str = dfkey
|
57
71
|
if getEle:
|
58
|
-
self._getEle_:object=getEle
|
72
|
+
self._getEle_: object = getEle
|
59
73
|
|
60
74
|
@property
|
61
|
-
def dfkey(self)->str:
|
75
|
+
def dfkey(self) -> str:
|
62
76
|
return self._dfkey_
|
63
77
|
|
64
|
-
def set(self,getEle)->None:
|
78
|
+
def set(self, getEle) -> None:
|
65
79
|
self._getEle_ = getEle
|
66
80
|
|
67
81
|
def __repr__(self) -> str:
|
68
82
|
msg = []
|
69
|
-
msg.append(f"XML Tag {self.value
|
83
|
+
msg.append(f"XML Tag {self.value=}")
|
70
84
|
if hasattr(self, "_dfkey_"):
|
71
|
-
msg.append(f"Df Key {self.dfkey
|
85
|
+
msg.append(f"Df Key {self.dfkey=}")
|
72
86
|
return "\n".join(msg)
|
73
87
|
|
74
88
|
NAME = "name", DFIndex.NAME
|
@@ -89,31 +103,32 @@ class XMLTags(StrEnum):
|
|
89
103
|
ANSWER = "answer"
|
90
104
|
TOLERANCE = "tolerance"
|
91
105
|
|
106
|
+
|
92
107
|
feedBElements = {
|
93
|
-
XMLTags.CORFEEDB:TextElements.SPANGREEN.create(),
|
108
|
+
XMLTags.CORFEEDB: TextElements.SPANGREEN.create(),
|
94
109
|
XMLTags.PCORFEEDB: TextElements.SPANORANGE.create(),
|
95
110
|
XMLTags.INCORFEEDB: TextElements.SPANRED.create(),
|
96
111
|
XMLTags.ANSFEEDBACK: TextElements.SPANGREEN.create(),
|
97
112
|
XMLTags.GENFEEDB: TextElements.SPANGREEN.create(),
|
98
113
|
}
|
99
114
|
feedbackStr = {
|
100
|
-
XMLTags.CORFEEDB:"Die Frage wurde richtig beantwortet",
|
115
|
+
XMLTags.CORFEEDB: "Die Frage wurde richtig beantwortet",
|
101
116
|
XMLTags.PCORFEEDB: "Die Frage wurde teilweise richtig beantwortet",
|
102
117
|
XMLTags.INCORFEEDB: "Die Frage wurde Falsch beantwortet",
|
103
118
|
XMLTags.GENFEEDB: "Sie haben eine Antwort abgegeben",
|
104
119
|
"right": "richtig",
|
105
120
|
"wrong": "falsch",
|
106
|
-
"right1Percent": "Gratultaion, die Frage wurde im Rahmen der Toleranz richtig beantwortet"
|
121
|
+
"right1Percent": "Gratultaion, die Frage wurde im Rahmen der Toleranz richtig beantwortet",
|
107
122
|
}
|
108
123
|
|
109
124
|
parserSettings = {
|
110
|
-
"Parser"
|
111
|
-
|
112
|
-
|
113
|
-
},
|
125
|
+
"Parser": {
|
126
|
+
"standards": {
|
127
|
+
"hidden": 0,
|
114
128
|
},
|
115
|
-
|
116
|
-
|
129
|
+
},
|
130
|
+
"MCParser": {
|
131
|
+
"standards": {
|
117
132
|
"single": "false",
|
118
133
|
"shuffleanswers": "true",
|
119
134
|
"answernumbering": "abc",
|
@@ -121,8 +136,7 @@ parserSettings = {
|
|
121
136
|
"shownumcorrect": "",
|
122
137
|
},
|
123
138
|
},
|
124
|
-
"NFParser"
|
139
|
+
"NFParser": {
|
125
140
|
"standards": {},
|
126
141
|
},
|
127
142
|
}
|
128
|
-
|
@@ -1,44 +1,45 @@
|
|
1
|
-
"""Numeric Multi Questions Module to calculate results from a formula
|
1
|
+
"""Numeric Multi Questions Module to calculate results from a formula.
|
2
2
|
|
3
3
|
This module calculates a series of results from al matrix of variables.
|
4
4
|
For each column in the matrix there will be one result.
|
5
5
|
As well it returns a bullet points string that shows the numerical values corresponding to the set of variables
|
6
6
|
"""
|
7
7
|
|
8
|
-
import re
|
8
|
+
import re
|
9
|
+
|
9
10
|
import pandas as pd
|
10
11
|
from asteval import Interpreter
|
11
12
|
|
12
13
|
astEval = Interpreter()
|
13
14
|
|
14
15
|
|
15
|
-
def getVariablesDict(df: pd.DataFrame, keyList: list, index:int)-> dict:
|
16
|
-
"""Liest alle Variablen-Listen deren Name in ``keyList`` ist aus dem DataFrame im Column[index]"""
|
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]."""
|
17
18
|
dic = {}
|
18
19
|
for k in keyList:
|
19
20
|
val = df.loc[str(k)][index]
|
20
|
-
if
|
21
|
+
if isinstance(val, str) and val is not None:
|
21
22
|
li = val.split(";")
|
22
23
|
dic[str(k)] = li
|
23
|
-
else:
|
24
|
+
else:
|
24
25
|
dic[str(k)] = [str(val)]
|
25
|
-
print(f"Folgende Variablen wurden gefunden:\n{dic}\n")
|
26
26
|
return dic
|
27
27
|
|
28
|
-
|
28
|
+
|
29
|
+
def setParameters(parameters: dict, index: int) -> None:
|
29
30
|
"""Ubergibt die Parameter mit entsprechenden Variablen-Namen an den asteval-Interpreter.
|
30
31
|
|
31
32
|
Dann kann dieser die equation loesen.
|
32
33
|
"""
|
33
|
-
for k,v in parameters.items():
|
34
|
+
for k, v in parameters.items():
|
34
35
|
comma = re.compile(r",")
|
35
|
-
value = comma.sub(".",v[index])
|
36
|
+
value = comma.sub(".", v[index])
|
36
37
|
astEval.symtable[k] = float(value)
|
37
|
-
return None
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
Für jeden Eintrag im varDict, wird im bulletPoints String der
|
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.
|
42
43
|
"""
|
43
44
|
for k, v in varDict.items():
|
44
45
|
s = r"{" + str(k) + r"}"
|
@@ -46,23 +47,26 @@ def insertVariablesToBPoints(varDict: dict, bulletPoints: str, index: int)-> str
|
|
46
47
|
bulletPoints = matcher.sub(str(v[index]), bulletPoints)
|
47
48
|
return bulletPoints
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
Durchsucht den bulletPoints String nach den Variablen
|
52
|
-
"""
|
50
|
+
|
51
|
+
def getVarsList(bps: str) -> list:
|
52
|
+
"""Durchsucht den bulletPoints String nach den Variablen `{var}`."""
|
53
53
|
vars = re.findall(r"\{\w\}", str(bps))
|
54
|
-
variablen=[]
|
54
|
+
variablen = []
|
55
55
|
for v in vars:
|
56
56
|
variablen.append(v.strip("{}"))
|
57
57
|
return variablen
|
58
58
|
|
59
59
|
|
60
|
-
def parseNumericMultiQuestion(
|
61
|
-
|
62
|
-
|
63
|
-
|
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*.
|
64
67
|
|
65
|
-
Gibt eine Liste mit allen Ergebnissen zurück
|
68
|
+
Gibt eine Liste mit allen Ergebnissen zurück
|
69
|
+
und eine Liste mit den bulletPoints-Strings, die die Numerischen Variablen enthalten
|
66
70
|
"""
|
67
71
|
results = []
|
68
72
|
bps = []
|