excel2moodle 0.3.3__py3-none-any.whl → 0.3.4__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 +64 -55
- excel2moodle/__main__.py +5 -5
- excel2moodle/core/category.py +53 -33
- excel2moodle/core/dataStructure.py +82 -50
- excel2moodle/core/etHelpers.py +21 -16
- excel2moodle/core/exceptions.py +11 -4
- excel2moodle/core/globals.py +41 -28
- excel2moodle/core/numericMultiQ.py +18 -13
- excel2moodle/core/parser.py +171 -109
- excel2moodle/core/question.py +86 -57
- excel2moodle/core/questionValidator.py +34 -41
- excel2moodle/core/stringHelpers.py +35 -24
- excel2moodle/extra/__init__.py +1 -3
- excel2moodle/extra/equationVerification.py +40 -22
- excel2moodle/ui/appUi.py +91 -68
- excel2moodle/ui/dialogs.py +35 -18
- excel2moodle/ui/settings.py +44 -7
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.4.dist-info}/METADATA +4 -3
- excel2moodle-0.3.4.dist-info/RECORD +32 -0
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.4.dist-info}/WHEEL +1 -1
- excel2moodle-0.3.4.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.4.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.3.3.dist-info → excel2moodle-0.3.4.dist-info}/top_level.txt +0 -0
excel2moodle/core/question.py
CHANGED
@@ -1,19 +1,34 @@
|
|
1
|
-
import logging
|
2
|
-
import lxml.etree as ET
|
3
|
-
from pathlib import Path
|
4
1
|
import base64 as base64
|
5
|
-
|
6
|
-
from excel2moodle.core import category, etHelpers
|
7
|
-
from excel2moodle.core.globals import XMLTags, TextElements, DFIndex, questionTypes, parserSettings
|
8
|
-
from excel2moodle.core.exceptions import QNotParsedException
|
9
|
-
from typing import Match
|
2
|
+
import logging
|
10
3
|
import re as re
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Match
|
11
6
|
|
7
|
+
import lxml.etree as ET
|
8
|
+
|
9
|
+
from excel2moodle.core import etHelpers
|
10
|
+
from excel2moodle.core.exceptions import QNotParsedException
|
11
|
+
from excel2moodle.core.globals import (
|
12
|
+
DFIndex,
|
13
|
+
TextElements,
|
14
|
+
XMLTags,
|
15
|
+
parserSettings,
|
16
|
+
questionTypes,
|
17
|
+
)
|
12
18
|
|
13
19
|
logger = logging.getLogger(__name__)
|
14
20
|
|
15
|
-
|
16
|
-
|
21
|
+
|
22
|
+
class Question:
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
category,
|
26
|
+
name: str,
|
27
|
+
number: int,
|
28
|
+
parent=None,
|
29
|
+
qtype: str = "type",
|
30
|
+
points: float = 0,
|
31
|
+
):
|
17
32
|
self.category = category
|
18
33
|
self.katName = self.category.name
|
19
34
|
self.name = name
|
@@ -21,43 +36,42 @@ class Question():
|
|
21
36
|
self.parent = parent
|
22
37
|
self.qtype: str = qtype
|
23
38
|
self.moodleType = questionTypes[qtype]
|
24
|
-
self.points =
|
25
|
-
self.element: ET.Element|None=None
|
26
|
-
self.picture:Picture
|
27
|
-
self.id:str
|
39
|
+
self.points = points if points != 0 else self.category.points
|
40
|
+
self.element: ET.Element | None = None
|
41
|
+
self.picture: Picture
|
42
|
+
self.id: str
|
28
43
|
self.qtextParagraphs: list[ET.Element] = []
|
29
|
-
self.bulletList: ET.Element|None = None
|
44
|
+
self.bulletList: ET.Element | None = None
|
30
45
|
self.answerVariants: list[ET.Element] = []
|
31
|
-
self.variants:int|None = None
|
32
|
-
self.variables: dict[str, list[float|int]] = {}
|
46
|
+
self.variants: int | None = None
|
47
|
+
self.variables: dict[str, list[float | int]] = {}
|
33
48
|
self.setID()
|
34
|
-
self.standardTags = {
|
35
|
-
"hidden":"false"
|
36
|
-
}
|
49
|
+
self.standardTags = {"hidden": "false"}
|
37
50
|
logger.debug(f"Question {self.id} is initialized")
|
38
51
|
|
39
|
-
def __repr__(self)->str:
|
40
|
-
li:list[str] = []
|
52
|
+
def __repr__(self) -> str:
|
53
|
+
li: list[str] = []
|
41
54
|
li.append(f"Question v{self.category.version}")
|
42
|
-
li.append(f
|
43
|
-
li.append(f
|
55
|
+
li.append(f"{self.id=}")
|
56
|
+
li.append(f"{self.parent=}")
|
44
57
|
return "\n".join(li)
|
45
58
|
|
46
|
-
def assemble(self, variant:int=1)->None:
|
47
|
-
textElements:list[ET.Element] = []
|
59
|
+
def assemble(self, variant: int = 1) -> None:
|
60
|
+
textElements: list[ET.Element] = []
|
48
61
|
textElements.extend(self.qtextParagraphs)
|
49
|
-
logger.debug(f"Starting assembly of {
|
62
|
+
logger.debug(f"Starting assembly of {self.id}")
|
50
63
|
if self.element is not None:
|
51
64
|
mainText = self.element.find(XMLTags.QTEXT)
|
52
|
-
logger.debug(f"found existing Text in element {mainText
|
65
|
+
logger.debug(f"found existing Text in element {mainText=}")
|
53
66
|
txtele = mainText.find("text")
|
54
67
|
if txtele is not None:
|
55
68
|
mainText.remove(txtele)
|
56
69
|
logger.debug(f"removed prevously existing questiontext")
|
57
|
-
else: raise QNotParsedException("Cant assamble, if element is none", self.id)
|
58
|
-
if self.variants is not None:
|
59
|
-
textElements.append(self.getBPointVariant(variant-1))
|
60
70
|
else:
|
71
|
+
raise QNotParsedException("Cant assamble, if element is none", self.id)
|
72
|
+
if self.variants is not None:
|
73
|
+
textElements.append(self.getBPointVariant(variant - 1))
|
74
|
+
elif self.bulletList is not None:
|
61
75
|
textElements.append(self.bulletList)
|
62
76
|
if hasattr(self, "picture") and self.picture.ready:
|
63
77
|
textElements.append(self.picture.htmlTag)
|
@@ -65,29 +79,31 @@ class Question():
|
|
65
79
|
mainText.append(etHelpers.getCdatTxtElement(textElements))
|
66
80
|
# self.element.insert(3, mainText)
|
67
81
|
logger.debug(f"inserted MainText to question element")
|
68
|
-
if len(
|
82
|
+
if len(self.answerVariants) > 0:
|
69
83
|
ans = self.element.find(XMLTags.ANSWER)
|
70
84
|
if ans is not None:
|
71
85
|
self.element.remove(ans)
|
72
86
|
logger.debug("removed previous answer element")
|
73
|
-
self.element.insert(5, self.answerVariants[variant-1])
|
87
|
+
self.element.insert(5, self.answerVariants[variant - 1])
|
74
88
|
return None
|
75
89
|
|
76
|
-
def setID(self, id
|
90
|
+
def setID(self, id=0) -> None:
|
77
91
|
if id == 0:
|
78
92
|
self.id: str = f"{self.category.id}{self.number:02d}"
|
79
|
-
else:
|
93
|
+
else:
|
94
|
+
self.id: str = str(id)
|
80
95
|
|
81
|
-
def getBPointVariant(self, variant:int)->ET.Element:
|
96
|
+
def getBPointVariant(self, variant: int) -> ET.Element:
|
82
97
|
if self.bulletList is None:
|
83
98
|
return None
|
84
|
-
|
99
|
+
# matches {a}, {some_var}, etc.
|
100
|
+
varPlaceholder = re.compile(r"{(\w+)}")
|
85
101
|
|
86
|
-
def replaceMatch(match: Match[str])->str|int|float:
|
102
|
+
def replaceMatch(match: Match[str]) -> str | int | float:
|
87
103
|
key = match.group(1)
|
88
104
|
if key in self.variables:
|
89
105
|
value = self.variables[key][variant]
|
90
|
-
return f"{value}".replace(".",",\\!")
|
106
|
+
return f"{value}".replace(".", ",\\!")
|
91
107
|
return match.group(0) # keep original if no match
|
92
108
|
|
93
109
|
unorderedList = TextElements.ULIST.create()
|
@@ -100,16 +116,16 @@ class Question():
|
|
100
116
|
return unorderedList
|
101
117
|
|
102
118
|
|
103
|
-
class Picture
|
104
|
-
def __init__(self, picKey:str, imgFolder:Path, question:Question):
|
119
|
+
class Picture:
|
120
|
+
def __init__(self, picKey: str, imgFolder: Path, question: Question):
|
105
121
|
self.pic = picKey
|
106
|
-
self.ready:bool = False
|
122
|
+
self.ready: bool = False
|
107
123
|
self.question = question
|
108
124
|
self.imgFolder = (imgFolder / question.katName).resolve()
|
109
|
-
self.htmlTag:ET.Element
|
110
|
-
self.path:Path
|
125
|
+
self.htmlTag: ET.Element
|
126
|
+
self.path: Path
|
111
127
|
self._setPath()
|
112
|
-
if hasattr(self,
|
128
|
+
if hasattr(self, "picID"):
|
113
129
|
self.ready = self.__getImg()
|
114
130
|
|
115
131
|
def _setPath(self):
|
@@ -117,30 +133,43 @@ class Picture():
|
|
117
133
|
self.picID = self.question.id
|
118
134
|
else:
|
119
135
|
selectedPic = self.pic[2:]
|
120
|
-
logger.debug(f"got a picture key {selectedPic
|
136
|
+
logger.debug(f"got a picture key {selectedPic=}")
|
121
137
|
try:
|
122
|
-
self.picID = f"{self.question.category.id}{
|
138
|
+
self.picID = f"{self.question.category.id}{
|
139
|
+
int(selectedPic):02d}"
|
123
140
|
except ValueError as e:
|
124
|
-
logger.warning(
|
141
|
+
logger.warning(
|
142
|
+
msg=f"Bild-ID konnte aus dem Key: {
|
143
|
+
self.pic=}nicht festgestellt werden",
|
144
|
+
exc_info=e,
|
145
|
+
)
|
125
146
|
|
126
147
|
def __getBase64Img(self, imgPath):
|
127
|
-
with open(imgPath,
|
128
|
-
img64 = base64.b64encode(img.read()).decode(
|
148
|
+
with open(imgPath, "rb") as img:
|
149
|
+
img64 = base64.b64encode(img.read()).decode("utf-8")
|
129
150
|
return img64
|
130
151
|
|
131
|
-
def __setImgElement(self, dir:Path, picID:int)->None:
|
152
|
+
def __setImgElement(self, dir: Path, picID: int) -> None:
|
132
153
|
"""gibt das Bild im dirPath mit dir qID als base64 encodiert mit den entsprechenden XML-Tags zurück"""
|
133
|
-
self.path:Path = (
|
134
|
-
self.element:ET.Element = ET.Element(
|
154
|
+
self.path: Path = (dir / str(picID)).with_suffix(".svg")
|
155
|
+
self.element: ET.Element = ET.Element(
|
156
|
+
"file", name=f"{self.path.name}", path="/", encoding="base64"
|
157
|
+
)
|
135
158
|
self.element.text = self.__getBase64Img(self.path)
|
136
159
|
|
137
|
-
|
138
|
-
def __getImg(self)->bool:
|
160
|
+
def __getImg(self) -> bool:
|
139
161
|
try:
|
140
162
|
self.__setImgElement(self.imgFolder, int(self.picID))
|
141
|
-
self.htmlTag = ET.Element(
|
163
|
+
self.htmlTag = ET.Element(
|
164
|
+
"img",
|
165
|
+
src=f"@@PLUGINFILE@@/{self.path.name}",
|
166
|
+
alt=f"Bild {self.path.name}",
|
167
|
+
width="500",
|
168
|
+
)
|
142
169
|
return True
|
143
170
|
except FileNotFoundError as e:
|
144
|
-
logger.warning(
|
171
|
+
logger.warning(
|
172
|
+
msg=f"Bild {self.picID} konnte nicht gefunden werden ", exc_info=e
|
173
|
+
)
|
145
174
|
self.element = None
|
146
175
|
return False
|
@@ -8,44 +8,40 @@ Those things are considered:
|
|
8
8
|
If Those checks pass, a question is created, which can be accessed via ``Validator.question``
|
9
9
|
"""
|
10
10
|
|
11
|
+
import logging
|
11
12
|
from types import UnionType
|
12
13
|
|
13
|
-
from pandas.core.series import notna
|
14
|
-
from excel2moodle.core.question import Question
|
15
|
-
from excel2moodle.core.globals import DFIndex
|
16
|
-
from excel2moodle.core.exceptions import InvalidFieldException, NanException
|
17
14
|
import pandas as pd
|
18
|
-
|
15
|
+
|
16
|
+
from excel2moodle.core.exceptions import InvalidFieldException
|
17
|
+
from excel2moodle.core.globals import DFIndex
|
18
|
+
from excel2moodle.core.question import Question
|
19
19
|
|
20
20
|
logger = logging.getLogger(__name__)
|
21
21
|
|
22
22
|
|
23
|
-
class Validator
|
23
|
+
class Validator:
|
24
24
|
def __init__(self, category) -> None:
|
25
|
-
self.question:Question
|
25
|
+
self.question: Question
|
26
26
|
self.category = category
|
27
|
-
self.mandatory: dict[DFIndex, type|UnionType] = {
|
27
|
+
self.mandatory: dict[DFIndex, type | UnionType] = {
|
28
28
|
DFIndex.TEXT: str,
|
29
29
|
DFIndex.NAME: str,
|
30
30
|
DFIndex.TYPE: str,
|
31
31
|
}
|
32
|
-
self.optional: dict[DFIndex, type|UnionType] = {
|
33
|
-
DFIndex.BPOINTS
|
32
|
+
self.optional: dict[DFIndex, type | UnionType] = {
|
33
|
+
DFIndex.BPOINTS: str,
|
34
34
|
DFIndex.NAME: str,
|
35
|
-
DFIndex.PICTURE: int|str,
|
36
|
-
}
|
37
|
-
self.nfOpt: dict[DFIndex, type|UnionType] = {
|
38
|
-
DFIndex.RESULT: float|int,
|
35
|
+
DFIndex.PICTURE: int | str,
|
39
36
|
}
|
40
|
-
self.
|
37
|
+
self.nfOpt: dict[DFIndex, type | UnionType] = {
|
38
|
+
DFIndex.RESULT: float | int,
|
41
39
|
}
|
42
|
-
self.
|
43
|
-
}
|
44
|
-
self.nfmMand: dict[DFIndex, type|UnionType] = {
|
45
|
-
}
|
46
|
-
self.
|
47
|
-
}
|
48
|
-
self.mcMand: dict[DFIndex, type|UnionType] = {
|
40
|
+
self.nfMand: dict[DFIndex, type | UnionType] = {}
|
41
|
+
self.nfmOpt: dict[DFIndex, type | UnionType] = {}
|
42
|
+
self.nfmMand: dict[DFIndex, type | UnionType] = {}
|
43
|
+
self.mcOpt: dict[DFIndex, type | UnionType] = {}
|
44
|
+
self.mcMand: dict[DFIndex, type | UnionType] = {
|
49
45
|
DFIndex.TRUE: str,
|
50
46
|
DFIndex.FALSE: str,
|
51
47
|
DFIndex.ANSTYPE: str,
|
@@ -57,15 +53,15 @@ class Validator():
|
|
57
53
|
"NFM": (self.nfmOpt, self.nfmMand),
|
58
54
|
}
|
59
55
|
|
60
|
-
def setup(self, df:pd.Series, index:int)->bool:
|
56
|
+
def setup(self, df: pd.Series, index: int) -> bool:
|
61
57
|
self.df = df
|
62
58
|
self.index = index
|
63
59
|
typ = self.df.loc[DFIndex.TYPE]
|
64
60
|
self.mandatory.update(self.mapper[typ][1])
|
65
61
|
self.optional.update(self.mapper[typ][0])
|
66
62
|
return True
|
67
|
-
|
68
|
-
def validate(self
|
63
|
+
|
64
|
+
def validate(self) -> bool:
|
69
65
|
id = f"{self.category.id}{self.index:02d}"
|
70
66
|
checker, missing = self._mandatory()
|
71
67
|
if not checker:
|
@@ -81,22 +77,22 @@ class Validator():
|
|
81
77
|
self._getData()
|
82
78
|
return True
|
83
79
|
|
84
|
-
def _getData(self)->None:
|
85
|
-
self.qdata:dict[str, str|float|int|list]={}
|
80
|
+
def _getData(self) -> None:
|
81
|
+
self.qdata: dict[str, str | float | int | list] = {}
|
86
82
|
for idx, val in self.df.items():
|
87
83
|
if not isinstance(idx, str):
|
88
84
|
logger.debug(f"Got a non String key in the spreadsheet, skipping it")
|
89
85
|
continue
|
90
86
|
if idx in self.qdata:
|
91
87
|
if isinstance(self.qdata[idx], list):
|
92
|
-
self.qdata[idx].append(val)
|
88
|
+
self.qdata[idx].append(val)
|
93
89
|
else:
|
94
90
|
existing = self.qdata[idx]
|
95
91
|
self.qdata[idx] = [existing, val]
|
96
92
|
else:
|
97
|
-
self.qdata[idx]=val
|
93
|
+
self.qdata[idx] = val
|
98
94
|
|
99
|
-
def _mandatory(self)->tuple[bool,DFIndex|None]:
|
95
|
+
def _mandatory(self) -> tuple[bool, DFIndex | None]:
|
100
96
|
"""detects if all keys of mandatory are filled with values"""
|
101
97
|
checker = pd.Series.notna(self.df)
|
102
98
|
for k in self.mandatory.keys():
|
@@ -108,11 +104,11 @@ class Validator():
|
|
108
104
|
if not c.any():
|
109
105
|
return False, k
|
110
106
|
elif not c:
|
111
|
-
return False, k
|
107
|
+
return False, k
|
112
108
|
return True, None
|
113
109
|
|
114
|
-
def _typeCheck(self)->tuple[bool, list[DFIndex]|None]:
|
115
|
-
invalid:list[DFIndex] = []
|
110
|
+
def _typeCheck(self) -> tuple[bool, list[DFIndex] | None]:
|
111
|
+
invalid: list[DFIndex] = []
|
116
112
|
for field, typ in self.mandatory.items():
|
117
113
|
if isinstance(self.df[field], pd.Series):
|
118
114
|
for f in self.df[field]:
|
@@ -127,16 +123,13 @@ class Validator():
|
|
127
123
|
invalid.append(field)
|
128
124
|
if len(invalid) == 0:
|
129
125
|
return True, None
|
130
|
-
else:
|
126
|
+
else:
|
131
127
|
return False, invalid
|
132
128
|
|
133
|
-
|
134
|
-
def _getQuestion(self)->None:
|
129
|
+
def _getQuestion(self) -> None:
|
135
130
|
name = self.df[DFIndex.NAME]
|
136
131
|
qtype = self.df[DFIndex.TYPE]
|
137
|
-
self.question=Question(
|
138
|
-
|
139
|
-
|
140
|
-
qtype = str(qtype))
|
132
|
+
self.question = Question(
|
133
|
+
self.category, name=str(name), number=self.index, qtype=str(qtype)
|
134
|
+
)
|
141
135
|
return None
|
142
|
-
|
@@ -1,9 +1,10 @@
|
|
1
|
-
"""This Module holds small Helperfunctions related to string manipulation
|
2
|
-
"""
|
1
|
+
"""This Module holds small Helperfunctions related to string manipulation"""
|
3
2
|
|
3
|
+
import base64 as base64
|
4
4
|
from pathlib import Path
|
5
|
+
|
5
6
|
import lxml.etree as ET
|
6
|
-
|
7
|
+
|
7
8
|
|
8
9
|
def stripWhitespace(stringList):
|
9
10
|
stripped = []
|
@@ -13,14 +14,15 @@ def stripWhitespace(stringList):
|
|
13
14
|
stripped.append(s)
|
14
15
|
return stripped
|
15
16
|
|
16
|
-
|
17
|
-
|
17
|
+
|
18
|
+
def stringToFloat(string: str) -> float:
|
19
|
+
string.replace(",", ".")
|
18
20
|
return float(string)
|
19
21
|
|
20
22
|
|
21
23
|
def get_bullet_string(s):
|
22
24
|
"""Formatiert die Angaben zum Statischen System hübsch"""
|
23
|
-
split = s.split(
|
25
|
+
split = s.split(";")
|
24
26
|
s_spl = stripWhitespace(split)
|
25
27
|
name = []
|
26
28
|
var = []
|
@@ -34,20 +36,26 @@ def get_bullet_string(s):
|
|
34
36
|
unit.append(sc_split[4])
|
35
37
|
bulletString = ['</p><ul dir="ltr">']
|
36
38
|
for i in range(0, len(s_spl)):
|
37
|
-
num = quant[i].split(
|
38
|
-
if len(num)==2:
|
39
|
+
num = quant[i].split(",")
|
40
|
+
if len(num) == 2:
|
39
41
|
num_s = f"{str(num[0])},\\!{str(num[1])}~"
|
40
|
-
else:
|
42
|
+
else:
|
43
|
+
num_s = f"{str(num[0])},\\!0~"
|
41
44
|
bulletString.append('<li style="text-align: left;">')
|
42
|
-
bulletString.append(
|
43
|
-
|
45
|
+
bulletString.append(
|
46
|
+
f"{name[i]}: \\( {var[i]} = {
|
47
|
+
num_s} \\mathrm{{ {unit[i]} }}\\) </li>\n"
|
48
|
+
)
|
49
|
+
bulletString.append("<br></ul>")
|
44
50
|
return "\n".join(bulletString)
|
45
51
|
|
52
|
+
|
46
53
|
def getBase64Img(imgPath):
|
47
|
-
with open(imgPath,
|
48
|
-
img64 = base64.b64encode(img.read()).decode(
|
54
|
+
with open(imgPath, "rb") as img:
|
55
|
+
img64 = base64.b64encode(img.read()).decode("utf-8")
|
49
56
|
return img64
|
50
57
|
|
58
|
+
|
51
59
|
def getUnitsElementAsString(unit):
|
52
60
|
|
53
61
|
def __getUnitEle__(name, multipl):
|
@@ -58,34 +66,37 @@ def getUnitsElementAsString(unit):
|
|
58
66
|
|
59
67
|
unitsEle = ET.Element("units")
|
60
68
|
|
61
|
-
|
69
|
+
|
70
|
+
def printDom(xmlElement: ET.Element, file: Path | None = None) -> None:
|
62
71
|
"""Prints the document tree of ``xmlTree`` to the ``file``, if specified, else dumps to stdout"""
|
63
72
|
documentTree = ET.ElementTree(xmlElement)
|
64
73
|
if file is not None:
|
65
74
|
if file.parent.exists():
|
66
|
-
|
75
|
+
documentTree.write(
|
76
|
+
file, xml_declaration=True, encoding="utf-8", pretty_print=True
|
77
|
+
)
|
67
78
|
else:
|
68
|
-
msg =
|
69
|
-
print(f
|
70
|
-
print(
|
79
|
+
msg = "No output File specified, here is the Element:"
|
80
|
+
print(f"\n{msg:=^80}")
|
81
|
+
print(ET.tostring(xmlElement, encoding="utf-8", pretty_print=True))
|
71
82
|
print(f'{" End of Element ":=^80}')
|
72
83
|
|
73
84
|
|
74
|
-
def texWrapper(text:str|list[str], style:str)->list[str]:
|
85
|
+
def texWrapper(text: str | list[str], style: str) -> list[str]:
|
75
86
|
"""Puts the strings inside ``text`` into a LaTex environment
|
76
87
|
|
77
88
|
if ``style == unit``: inside ``\\mathrm{}``
|
78
|
-
if ``style == math``: inside ``\\( \\)``
|
89
|
+
if ``style == math``: inside ``\\( \\)``
|
79
90
|
"""
|
80
91
|
|
81
|
-
answers:list[str]=[]
|
82
|
-
begin =""
|
92
|
+
answers: list[str] = []
|
93
|
+
begin = ""
|
83
94
|
end = ""
|
84
95
|
if style == "math":
|
85
|
-
begin ="\\("
|
96
|
+
begin = "\\("
|
86
97
|
end = "\\)"
|
87
98
|
elif style == "unit":
|
88
|
-
begin ="\\(\\mathrm{"
|
99
|
+
begin = "\\(\\mathrm{"
|
89
100
|
end = "}\\)"
|
90
101
|
if isinstance(text, str):
|
91
102
|
li = [begin]
|
excel2moodle/extra/__init__.py
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
The modules inside *extra* can be run standalone, but are planned to be available from the main Window as well
|
4
4
|
|
5
|
-
To run a script execute the following: ``python -m excel2moodle.extra.SCRIPT``
|
5
|
+
To run a script execute the following: ``python -m excel2moodle.extra.SCRIPT``
|
6
6
|
Note that there is no ``.py`` at the end!!
|
7
7
|
"""
|
8
|
-
|
9
|
-
from excel2moodle.core import numericMultiQ
|
@@ -28,15 +28,18 @@ As Script
|
|
28
28
|
#. Rinse and repeat
|
29
29
|
"""
|
30
30
|
|
31
|
-
import re as re
|
32
|
-
import pandas as pd
|
33
31
|
from pathlib import Path
|
32
|
+
|
33
|
+
import pandas as pd
|
34
|
+
|
34
35
|
from excel2moodle.core import numericMultiQ as nmq
|
35
36
|
|
36
37
|
# Hier Bitte die Frage angeben, die getestet Werden soll:
|
37
38
|
|
38
|
-
|
39
|
-
|
39
|
+
# ===========================================================
|
40
|
+
|
41
|
+
|
42
|
+
def checkResult(checkerValue: float, calculation: float, tolerance=0.01) -> bool:
|
40
43
|
"""Checks if the two Arguments are within the tolerance the same value
|
41
44
|
|
42
45
|
:param checkerValue: the value the calculation is compared against
|
@@ -46,20 +49,23 @@ def checkResult(checkerValue:float, calculation:float, tolerance = 0.01)-> bool:
|
|
46
49
|
:param tolerance: the standart tolerance is 0.01 -> 1%
|
47
50
|
:type tolerance: float, optional
|
48
51
|
|
49
|
-
:returns:
|
52
|
+
:returns:
|
50
53
|
True if checkerValue == calculation
|
51
54
|
False if checkerValue != calculation
|
52
55
|
:rtype: bool
|
53
56
|
"""
|
54
57
|
|
55
|
-
upper = abs(checkerValue + checkerValue*tolerance)
|
56
|
-
lower = abs(checkerValue - checkerValue*tolerance)
|
58
|
+
upper = abs(checkerValue + checkerValue * tolerance)
|
59
|
+
lower = abs(checkerValue - checkerValue * tolerance)
|
57
60
|
if abs(calculation) > lower and abs(calculation) < upper:
|
58
61
|
return True
|
59
|
-
else
|
62
|
+
else:
|
60
63
|
return False
|
61
64
|
|
62
|
-
|
65
|
+
|
66
|
+
def equationChecker(
|
67
|
+
categoryName: str, qNumber: int, spreadsheetFile
|
68
|
+
) -> tuple[list[str], list[float], float]:
|
63
69
|
"""This Function calculates all Results an invokes the checkResult function
|
64
70
|
|
65
71
|
Parameters
|
@@ -91,34 +97,46 @@ def equationChecker(categoryName: str, qNumber:int, spreadsheetFile)-> tuple[lis
|
|
91
97
|
except Exception:
|
92
98
|
print(f"Es ist kein 'firstResult' gegeben, kann nichts überprüfen")
|
93
99
|
res = 0
|
94
|
-
bps, calcs = nmq.parseNumericMultiQuestion(df,bps,eq, qNumber)
|
100
|
+
bps, calcs = nmq.parseNumericMultiQuestion(df, bps, eq, qNumber)
|
95
101
|
return bps, calcs, res
|
96
102
|
|
97
103
|
|
98
|
-
def main(
|
99
|
-
""
|
100
|
-
|
101
|
-
|
104
|
+
def main(
|
105
|
+
spreadsheetFile=Path("../Fragensammlung/Main_question_all.xlsx"),
|
106
|
+
catN=None,
|
107
|
+
qNumber=None,
|
108
|
+
) -> None:
|
109
|
+
"""Takes the Spreadsheet, and asks for a category and a question number"""
|
102
110
|
if catN == None:
|
103
111
|
catN = input("Geben Sie die Kategorie an: KAT_")
|
104
112
|
categoryName = f"KAT_{catN}"
|
105
113
|
if qNumber == None:
|
106
114
|
qNumber = int(input("Geben Sie die Fragennummer an: "))
|
107
|
-
bullets, results, firstResult = equationChecker(
|
115
|
+
bullets, results, firstResult = equationChecker(
|
116
|
+
categoryName, qNumber, spreadsheetFile=spreadsheetFile
|
117
|
+
)
|
108
118
|
check = False
|
109
119
|
|
110
120
|
for i, calculation in enumerate(results):
|
111
|
-
if i == 0 and firstResult !=0:
|
112
|
-
|
113
|
-
print(
|
121
|
+
if i == 0 and firstResult != 0:
|
122
|
+
check = checkResult(firstResult, calculation)
|
123
|
+
print(
|
124
|
+
f"Ergebnis {
|
125
|
+
i+1}: \t{calculation}\n\tMit den Werten: \n{bullets[i]}\n"
|
126
|
+
)
|
114
127
|
|
115
128
|
if check == True:
|
116
|
-
print(
|
129
|
+
print(
|
130
|
+
f"Das erste berechnete Ergebnis stimmt mit dem Wert in 'firstResult' überein\n"
|
131
|
+
)
|
117
132
|
else:
|
118
|
-
print(
|
133
|
+
print(
|
134
|
+
f"WARNUNG: Das erste berechnete Ergebnis weicht von dem Wert {
|
135
|
+
firstResult=} ab.\n"
|
136
|
+
)
|
119
137
|
|
120
138
|
|
121
|
-
if __name__ =="__main__":
|
122
|
-
spreadsheet =input(f"Geben Sie den Pfad zu dem spreadsheet an:")
|
139
|
+
if __name__ == "__main__":
|
140
|
+
spreadsheet = input(f"Geben Sie den Pfad zu dem spreadsheet an:")
|
123
141
|
while True:
|
124
142
|
main(Path(spreadsheet))
|