excel2moodle 0.4.3__py3-none-any.whl → 0.5.0__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/core/category.py +14 -6
- excel2moodle/core/dataStructure.py +46 -38
- excel2moodle/core/parser.py +6 -1
- excel2moodle/core/question.py +39 -15
- excel2moodle/core/settings.py +6 -5
- excel2moodle/core/validator.py +3 -3
- excel2moodle/logger.py +16 -1
- excel2moodle/question_types/mc.py +3 -3
- excel2moodle/ui/appUi.py +1 -1
- excel2moodle/ui/dialogs.py +24 -13
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/METADATA +1 -1
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/RECORD +16 -16
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/WHEEL +0 -0
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.4.3.dist-info → excel2moodle-0.5.0.dist-info}/top_level.txt +0 -0
excel2moodle/core/category.py
CHANGED
@@ -5,6 +5,8 @@ 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
|
+
|
8
10
|
if TYPE_CHECKING:
|
9
11
|
from excel2moodle.core.question import Question
|
10
12
|
|
@@ -19,17 +21,15 @@ class Category:
|
|
19
21
|
name: str,
|
20
22
|
description: str,
|
21
23
|
dataframe: pd.DataFrame,
|
22
|
-
|
23
|
-
version: int = 0,
|
24
|
+
settings: dict[str, float | str],
|
24
25
|
) -> None:
|
25
26
|
"""Instantiate a new Category object."""
|
26
27
|
self.NAME = name
|
27
|
-
match = re.search(r"\d+$", self.NAME)
|
28
|
+
match = re.search(r"\d+$", str(self.NAME))
|
28
29
|
self.n: int = int(match.group(0)) if match else 99
|
29
30
|
self.desc = str(description)
|
30
31
|
self.dataframe: pd.DataFrame = dataframe
|
31
|
-
self.
|
32
|
-
self.version = int(version)
|
32
|
+
self.settings: dict[str, float | str] = settings if settings else {}
|
33
33
|
self.questions: dict[int, Question] = {}
|
34
34
|
self.maxVariants: int | None = None
|
35
35
|
loggerObj.info("initializing Category %s", self.NAME)
|
@@ -38,9 +38,17 @@ class Category:
|
|
38
38
|
def name(self) -> str:
|
39
39
|
return self.NAME
|
40
40
|
|
41
|
+
@property
|
42
|
+
def points(self) -> float:
|
43
|
+
return self.settings.get(Tags.POINTS)
|
44
|
+
|
45
|
+
@points.setter
|
46
|
+
def points(self, points: float) -> None:
|
47
|
+
self.settings[Tags.POINTS] = points
|
48
|
+
|
41
49
|
@property
|
42
50
|
def id(self) -> str:
|
43
|
-
return f"{self.
|
51
|
+
return f"{self.n:02d}"
|
44
52
|
|
45
53
|
def __hash__(self) -> int:
|
46
54
|
return hash(self.NAME)
|
@@ -105,7 +105,18 @@ class QuestionDB:
|
|
105
105
|
engine="calamine",
|
106
106
|
)
|
107
107
|
logger.debug("Found the settings: \n\t%s", settingDf)
|
108
|
-
self.
|
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)
|
113
|
+
try:
|
114
|
+
self._validateProjectSettings(sheetPath=sheetPath)
|
115
|
+
except InvalidFieldException:
|
116
|
+
logger.exception(
|
117
|
+
"Can not create the database with invalid project settings."
|
118
|
+
)
|
119
|
+
raise
|
109
120
|
with Path(sheetPath).open("rb") as f:
|
110
121
|
self.categoriesMetaData = pd.read_excel(
|
111
122
|
f,
|
@@ -113,34 +124,33 @@ class QuestionDB:
|
|
113
124
|
index_col=0,
|
114
125
|
engine="calamine",
|
115
126
|
)
|
116
|
-
logger.info(
|
117
|
-
"Sucessfully read categoriesMetaData \n %s", self.categoriesMetaData
|
118
|
-
)
|
127
|
+
logger.info("Sucessfully read categoriesMetaData")
|
119
128
|
return self.categoriesMetaData
|
120
129
|
|
121
|
-
def
|
122
|
-
|
123
|
-
) -> None:
|
124
|
-
settings = self.harmonizeDFIndex(settings)
|
125
|
-
for tag, value in settings.iterrows():
|
126
|
-
value = value.iloc[0]
|
127
|
-
if tag == Tags.TOLERANCE:
|
128
|
-
tol = value if value <= 1 else value / 100
|
129
|
-
self.settings.set(Tags.TOLERANCE, tol)
|
130
|
-
else:
|
131
|
-
self.settings.set(tag, value)
|
132
|
-
if Tags.IMPORTMODULE in settings.index:
|
130
|
+
def _validateProjectSettings(self, sheetPath: Path) -> None:
|
131
|
+
if Tags.IMPORTMODULE in self.settings:
|
133
132
|
logger.warning(
|
134
133
|
"Appending: %s to sys.path. All names defined by it will be usable",
|
135
|
-
|
134
|
+
sheetPath,
|
136
135
|
)
|
137
|
-
sys.path.append(str(
|
136
|
+
sys.path.append(str(sheetPath.parent))
|
137
|
+
if Tags.PICTURESUBFOLDER not in self.settings:
|
138
|
+
logger.warning("You didn't specify an image Folder. This may cause errors.")
|
138
139
|
imgFolder = self.settings.get(Tags.SPREADSHEETPATH).parent / self.settings.get(
|
139
140
|
Tags.PICTURESUBFOLDER
|
140
141
|
)
|
141
|
-
|
142
|
-
|
143
|
-
|
142
|
+
catSheet = self.settings.get(Tags.CATEGORIESSHEET)
|
143
|
+
if catSheet not in pd.ExcelFile(sheetPath, engine="calamine").sheet_names:
|
144
|
+
msg = f"The specified categories sheet: '{catSheet}' doesn't exist."
|
145
|
+
raise InvalidFieldException(msg, "00000", Tags.CATEGORIESSHEET)
|
146
|
+
try:
|
147
|
+
imgFolder.resolve(strict=True)
|
148
|
+
except FileNotFoundError:
|
149
|
+
msg = f"Img Path: {imgFolder} could not be found"
|
150
|
+
raise InvalidFieldException(msg, "00000", Tags.PICTURESUBFOLDER)
|
151
|
+
else:
|
152
|
+
self.settings.set(Tags.PICTUREFOLDER, imgFolder.resolve())
|
153
|
+
logger.info("Set up the project settings")
|
144
154
|
|
145
155
|
def initAllCategories(self, sheetPath: Path | None = None) -> None:
|
146
156
|
"""Read all category sheets and initialize all Categories."""
|
@@ -227,29 +237,26 @@ class QuestionDB:
|
|
227
237
|
|
228
238
|
:emits: categoryReady(self) Signal.
|
229
239
|
"""
|
230
|
-
categoryDf = self.harmonizeDFIndex(categoryDf)
|
231
|
-
|
232
|
-
self.categoriesMetaData
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
if "version" in self.categoriesMetaData
|
240
|
-
and not pd.isna(self.categoriesMetaData["version"].loc[categoryName])
|
241
|
-
else self.settings.get(Tags.VERSION)
|
242
|
-
)
|
240
|
+
categoryDf: pd.DataFrame = self.harmonizeDFIndex(categoryDf)
|
241
|
+
categorySettings: dict[str, float | int | str] = self.harmonizeDFIndex(
|
242
|
+
self.categoriesMetaData.loc[categoryName]
|
243
|
+
).to_dict()
|
244
|
+
nanSettings: list[str] = [
|
245
|
+
k for k, v in categorySettings.items() if pd.isna(v) or k not in Tags
|
246
|
+
]
|
247
|
+
for na in nanSettings:
|
248
|
+
categorySettings.pop(na)
|
243
249
|
category = Category(
|
244
250
|
categoryName,
|
245
251
|
self.categoriesMetaData["description"].loc[categoryName],
|
246
252
|
dataframe=categoryDf,
|
247
|
-
|
248
|
-
version=version,
|
253
|
+
settings=categorySettings,
|
249
254
|
)
|
250
255
|
if hasattr(self, "categories"):
|
251
256
|
self.categories[categoryName] = category
|
252
257
|
self.signals.categoryReady.emit(category)
|
258
|
+
else:
|
259
|
+
logger.warning("Can't append initialized category to the database")
|
253
260
|
logger.debug("Category %s is initialized", categoryName)
|
254
261
|
return category
|
255
262
|
|
@@ -279,7 +286,7 @@ class QuestionDB:
|
|
279
286
|
self.signals.categoryQuestionsReady.emit(category)
|
280
287
|
|
281
288
|
@classmethod
|
282
|
-
def setupAndParseQuestion(cls, category: Category, qNumber: int) -> None:
|
289
|
+
def setupAndParseQuestion(cls, category: Category, qNumber: int) -> Question | None:
|
283
290
|
"""Check if the Question Data is valid. Then parse it.
|
284
291
|
|
285
292
|
The Question data is accessed from `category.dataframe` via its number
|
@@ -315,7 +322,7 @@ class QuestionDB:
|
|
315
322
|
question = QuestionTypeMapping[qtype].create(category, validData)
|
316
323
|
if question.isParsed:
|
317
324
|
locallogger.info("Question already parsed")
|
318
|
-
return
|
325
|
+
return None
|
319
326
|
if isinstance(question, NFQuestion):
|
320
327
|
cls.nfParser.setup(question)
|
321
328
|
locallogger.debug("setup a new NF parser ")
|
@@ -336,6 +343,7 @@ class QuestionDB:
|
|
336
343
|
msg = "couldn't setup Parser"
|
337
344
|
raise QNotParsedException(msg, question.id)
|
338
345
|
category.questions[qNumber] = question
|
346
|
+
return question
|
339
347
|
|
340
348
|
def appendQuestions(
|
341
349
|
self, questions: list[QuestionItem], file: Path | None = None
|
excel2moodle/core/parser.py
CHANGED
@@ -50,7 +50,12 @@ class QuestionParser:
|
|
50
50
|
f = self.settings.get(Tags.PICTUREFOLDER)
|
51
51
|
svgFolder = (f / self.question.katName).resolve()
|
52
52
|
if not hasattr(self.question, "picture"):
|
53
|
-
self.question.picture = Picture(
|
53
|
+
self.question.picture = Picture(
|
54
|
+
picKey,
|
55
|
+
svgFolder,
|
56
|
+
self.question.id,
|
57
|
+
width=self.rawInput.get(Tags.PICTUREWIDTH),
|
58
|
+
)
|
54
59
|
return bool(self.question.picture.ready)
|
55
60
|
|
56
61
|
def setMainText(self) -> None:
|
excel2moodle/core/question.py
CHANGED
@@ -25,6 +25,14 @@ settings = Settings()
|
|
25
25
|
|
26
26
|
|
27
27
|
class QuestionData(dict):
|
28
|
+
@property
|
29
|
+
def categoryFallbacks(self) -> dict[str, float | str]:
|
30
|
+
return self._categoryFallbacks
|
31
|
+
|
32
|
+
@categoryFallbacks.setter
|
33
|
+
def categoryFallbacks(self, fallbacks: dict) -> None:
|
34
|
+
self._categoryFallbacks: dict[str, float | str] = fallbacks
|
35
|
+
|
28
36
|
@overload
|
29
37
|
def get(
|
30
38
|
self,
|
@@ -39,7 +47,6 @@ class QuestionData(dict):
|
|
39
47
|
def get(
|
40
48
|
self,
|
41
49
|
key: Literal[
|
42
|
-
Tags.VERSION,
|
43
50
|
Tags.NUMBER,
|
44
51
|
Tags.PICTUREWIDTH,
|
45
52
|
Tags.ANSPICWIDTH,
|
@@ -58,14 +65,26 @@ class QuestionData(dict):
|
|
58
65
|
def get(self, key: Literal[Tags.RESULT]) -> float | str: ...
|
59
66
|
|
60
67
|
def get(self, key: Tags, default=None):
|
68
|
+
"""Get the value for `key` with correct type.
|
69
|
+
|
70
|
+
If `key == Tags.TOLERANCE` the tolerance is checked to be a perc. fraction
|
71
|
+
"""
|
61
72
|
if key in self:
|
62
73
|
val = self[key]
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
elif key in self.categoryFallbacks:
|
75
|
+
val = self.categoryFallbacks.get(key)
|
76
|
+
else:
|
77
|
+
val = settings.get(key)
|
78
|
+
try:
|
79
|
+
typed = key.typ()(val)
|
80
|
+
except (TypeError, ValueError):
|
81
|
+
return None
|
82
|
+
if key == Tags.TOLERANCE:
|
83
|
+
loggerObj.debug("Verifying Tolerance")
|
84
|
+
if typed <= 0 or typed >= 100:
|
85
|
+
typed = settings.get(Tags.TOLERANCE)
|
86
|
+
return typed if typed < 1 else typed / 100
|
87
|
+
return typed
|
69
88
|
|
70
89
|
|
71
90
|
class Question:
|
@@ -105,13 +124,12 @@ class Question:
|
|
105
124
|
category: Category,
|
106
125
|
rawData: QuestionData,
|
107
126
|
parent=None,
|
108
|
-
points: float = 0,
|
109
127
|
) -> None:
|
110
128
|
self.rawData: QuestionData = rawData
|
129
|
+
self.rawData.categoryFallbacks = category.settings
|
111
130
|
self.category = category
|
112
131
|
self.katName = self.category.name
|
113
132
|
self.moodleType = QUESTION_TYPES[self.qtype]
|
114
|
-
self.points = points if points != 0 else self.category.points
|
115
133
|
self.element: ET.Element = None
|
116
134
|
self.isParsed: bool = False
|
117
135
|
self.picture: Picture
|
@@ -122,6 +140,10 @@ class Question:
|
|
122
140
|
self.logger = LogAdapterQuestionID(loggerObj, {"qID": self.id})
|
123
141
|
self.logger.debug("Sucess initializing")
|
124
142
|
|
143
|
+
@property
|
144
|
+
def points(self) -> float:
|
145
|
+
return self.rawData.get(Tags.POINTS)
|
146
|
+
|
125
147
|
@property
|
126
148
|
def name(self) -> str:
|
127
149
|
return self.rawData.get(Tags.NAME)
|
@@ -242,6 +264,7 @@ class Picture:
|
|
242
264
|
|
243
265
|
def getImgId(self, imgKey: str) -> bool:
|
244
266
|
"""Get the image ID and width based on the given key.
|
267
|
+
|
245
268
|
The key should either be the full ID (as the question) or only the question Num.
|
246
269
|
If only the number is given, the category.id is prepended.
|
247
270
|
The width should be specified by `ID:width:XX`. where xx is the px value.
|
@@ -262,12 +285,13 @@ class Picture:
|
|
262
285
|
if imgKey in ("false", "nan", False) or len(num) == 0:
|
263
286
|
return False
|
264
287
|
imgID: int = int(num[0])
|
265
|
-
if imgID <
|
266
|
-
picID = f"{self.questionId[:
|
288
|
+
if imgID < 100:
|
289
|
+
picID = f"{self.questionId[:2]}{imgID:02d}"
|
267
290
|
elif imgID < 10000:
|
268
|
-
picID = f"{
|
269
|
-
|
270
|
-
|
291
|
+
picID = f"{imgID:04d}"
|
292
|
+
else:
|
293
|
+
msg = f"The imgKey {imgKey} is invalid, it should be a 4 digit question ID with an optional suffix"
|
294
|
+
raise QNotParsedException(msg, self.questionId)
|
271
295
|
if len(app) > 0 and app[0]:
|
272
296
|
self.picID = f"{picID}{app[0]}"
|
273
297
|
else:
|
@@ -280,7 +304,7 @@ class Picture:
|
|
280
304
|
return base64.b64encode(img.read()).decode("utf-8")
|
281
305
|
|
282
306
|
def _getImg(self) -> bool:
|
283
|
-
suffixes = ["png", "svg", "jpeg", "jpg"]
|
307
|
+
suffixes = ["png", "svg", "jpeg", "jpg", "JPG", "jxl"]
|
284
308
|
paths = [
|
285
309
|
path
|
286
310
|
for suf in suffixes
|
excel2moodle/core/settings.py
CHANGED
@@ -22,7 +22,7 @@ class Tags(StrEnum):
|
|
22
22
|
typ: type,
|
23
23
|
default: str | float | Path | bool | None,
|
24
24
|
place: str = "project",
|
25
|
-
):
|
25
|
+
) -> object:
|
26
26
|
"""Define new settings class."""
|
27
27
|
obj = str.__new__(cls, key)
|
28
28
|
obj._value_ = key
|
@@ -84,17 +84,19 @@ class Tags(StrEnum):
|
|
84
84
|
ANSTYPE = "answertype", str, None
|
85
85
|
QUESTIONPART = "part", list, None
|
86
86
|
PARTTYPE = "parttype", str, None
|
87
|
-
VERSION = "version", int, 1
|
88
87
|
POINTS = "points", float, 1.0
|
89
88
|
PICTUREWIDTH = "imgwidth", int, 500
|
90
89
|
ANSPICWIDTH = "answerimgwidth", int, 120
|
91
|
-
WRONGSIGNPERCENT = "
|
90
|
+
WRONGSIGNPERCENT = "wrongsignpercent", int, 50
|
92
91
|
FIRSTRESULT = "firstresult", float, 0
|
93
92
|
|
94
93
|
|
95
94
|
class Settings:
|
96
95
|
values: ClassVar[dict[str, str | float | Path]] = {}
|
97
96
|
|
97
|
+
def __contains__(self, tag: Tags) -> bool:
|
98
|
+
return bool(tag in type(self).values)
|
99
|
+
|
98
100
|
@classmethod
|
99
101
|
def clear(cls) -> None:
|
100
102
|
cls.values.clear()
|
@@ -112,7 +114,6 @@ class Settings:
|
|
112
114
|
key: Literal[
|
113
115
|
Tags.QUESTIONVARIANT,
|
114
116
|
Tags.TOLERANCE,
|
115
|
-
Tags.VERSION,
|
116
117
|
Tags.PICTUREWIDTH,
|
117
118
|
Tags.ANSPICWIDTH,
|
118
119
|
Tags.WRONGSIGNPERCENT,
|
@@ -192,6 +193,6 @@ class Settings:
|
|
192
193
|
type(value),
|
193
194
|
)
|
194
195
|
return
|
195
|
-
logger.info("Saved %s = %s
|
196
|
+
logger.info("Saved %s = %s: %s", key, value, tag.typ().__name__)
|
196
197
|
else:
|
197
198
|
logger.warning("got invalid local Setting %s = %s", key, value)
|
excel2moodle/core/validator.py
CHANGED
@@ -80,9 +80,9 @@ class Validator:
|
|
80
80
|
if key.startswith(tag) and not isinstance(self.qdata[key], list):
|
81
81
|
self.qdata[key] = stringHelpers.getListFromStr(self.qdata[key])
|
82
82
|
tol = float(self.qdata.get(Tags.TOLERANCE, 0))
|
83
|
-
if tol
|
84
|
-
|
85
|
-
|
83
|
+
if tol < 0 or tol > 99:
|
84
|
+
tol = settings.get(Tags.TOLERANCE)
|
85
|
+
elif tol != 0:
|
86
86
|
self.qdata[Tags.TOLERANCE] = tol if tol < 1 else tol / 100
|
87
87
|
|
88
88
|
if self.qdata[Tags.TYPE] == "NFM":
|
excel2moodle/logger.py
CHANGED
@@ -88,11 +88,26 @@ class LogWindowHandler(logging.Handler):
|
|
88
88
|
"CRITICAL": "pink",
|
89
89
|
}
|
90
90
|
|
91
|
+
def handle(self, record) -> bool:
|
92
|
+
info = record.exc_info
|
93
|
+
if record.exc_info:
|
94
|
+
excType, excVal, excTraceB = record.exc_info
|
95
|
+
exc_info_msg = f"[{excType.__name__}]: <b>{excVal}</b>"
|
96
|
+
record.exc_text = exc_info_msg
|
97
|
+
record.exc_info = None
|
98
|
+
try:
|
99
|
+
super().handle(record)
|
100
|
+
finally:
|
101
|
+
record.exc_info = info
|
102
|
+
record.exc_text = None
|
103
|
+
return True
|
104
|
+
|
91
105
|
def emit(self, record: logging.LogRecord) -> None:
|
92
106
|
"""Emit the signal, with a new logging message."""
|
93
107
|
log_message = self.format(record)
|
108
|
+
msg = log_message.replace("\n", "<br> ")
|
94
109
|
color = self.logLevelColors.get(record.levelname, "black")
|
95
|
-
prettyMessage = f'<span style="color:{color};">{
|
110
|
+
prettyMessage = f'<span style="color:{color};">{msg}</span>'
|
96
111
|
self.emitter.signal.emit(prettyMessage)
|
97
112
|
|
98
113
|
|
@@ -104,13 +104,13 @@ class MCQuestionParser(QuestionParser):
|
|
104
104
|
if self.answerType == "picture":
|
105
105
|
f = self.settings.get(Tags.PICTUREFOLDER)
|
106
106
|
imgFolder = (f / self.question.katName).resolve()
|
107
|
-
|
107
|
+
width = self.rawInput.get(Tags.ANSPICWIDTH)
|
108
108
|
self.trueImgs: list[Picture] = [
|
109
|
-
Picture(t, imgFolder, self.question.id, width=
|
109
|
+
Picture(t, imgFolder, self.question.id, width=width)
|
110
110
|
for t in self.rawInput.get(Tags.TRUE)
|
111
111
|
]
|
112
112
|
self.falseImgs: list[Picture] = [
|
113
|
-
Picture(t, imgFolder, self.question.id, width=
|
113
|
+
Picture(t, imgFolder, self.question.id, width=width)
|
114
114
|
for t in self.rawInput.get(Tags.FALSE)
|
115
115
|
]
|
116
116
|
trueAnsList: list[str] = [pic.htmlTag for pic in self.trueImgs if pic.ready]
|
excel2moodle/ui/appUi.py
CHANGED
@@ -108,7 +108,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
108
108
|
If successful it prints out a list of all exported Questions
|
109
109
|
"""
|
110
110
|
self.ui.treeWidget.clear()
|
111
|
-
self.testDB.readCategoriesMetadata()
|
112
111
|
process = ParseAllThread(self.testDB, self)
|
113
112
|
self.threadPool.start(process)
|
114
113
|
|
@@ -259,6 +258,7 @@ class ParseAllThread(QRunnable):
|
|
259
258
|
|
260
259
|
@QtCore.Slot()
|
261
260
|
def run(self) -> None:
|
261
|
+
self.testDB.readCategoriesMetadata()
|
262
262
|
self.testDB.asyncInitAllCategories(self.mainApp.excelPath)
|
263
263
|
self.mainApp.setStatus("[OK] Tabellen wurde eingelesen")
|
264
264
|
self.testDB.parseAllQuestions()
|
excel2moodle/ui/dialogs.py
CHANGED
@@ -90,28 +90,39 @@ class QuestionPreview:
|
|
90
90
|
self.ui.graphicsView.setScene(self.picScene)
|
91
91
|
self.setText()
|
92
92
|
self.setAnswers()
|
93
|
-
if hasattr(self, "picItem"):
|
93
|
+
if hasattr(self, "picItem") and self.picItem.scene() == self.picScene:
|
94
|
+
logger.debug("removing Previous picture")
|
94
95
|
self.picScene.removeItem(self.picItem)
|
96
|
+
del self.picItem
|
95
97
|
self.setPicture()
|
96
98
|
|
97
99
|
def setPicture(self) -> None:
|
98
100
|
if hasattr(self.question, "picture") and self.question.picture.ready:
|
99
101
|
path = self.question.picture.path
|
100
102
|
if path.suffix == ".svg":
|
101
|
-
self.picItem = QGraphicsSvgItem(str(
|
103
|
+
self.picItem = QGraphicsSvgItem(str(path))
|
102
104
|
else:
|
103
|
-
pic = QtGui.QPixmap(
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
width, scaleHeight, QtGui.Qt.AspectRatioMode.KeepAspectRatio
|
110
|
-
)
|
111
|
-
)
|
105
|
+
pic = QtGui.QPixmap(str(path))
|
106
|
+
self.picItem = QtWidgets.QGraphicsPixmapItem(pic)
|
107
|
+
if pic.isNull():
|
108
|
+
logger.warning("Picture null")
|
109
|
+
scale = self._getImgFittingScale()
|
110
|
+
self.picItem.setScale(scale)
|
112
111
|
self.picScene.addItem(self.picItem)
|
113
|
-
|
114
|
-
|
112
|
+
|
113
|
+
def _getImgFittingScale(self) -> float:
|
114
|
+
view_size = self.ui.graphicsView.viewport().size()
|
115
|
+
view_width = view_size.width()
|
116
|
+
view_height = view_size.height()
|
117
|
+
if isinstance(self.picItem, QtWidgets.QGraphicsPixmapItem):
|
118
|
+
original_size = self.picItem.pixmap().size()
|
119
|
+
elif isinstance(self.picItem, QGraphicsSvgItem):
|
120
|
+
original_size = self.picItem.renderer().defaultSize()
|
121
|
+
else:
|
122
|
+
return 1 # Unknown item type
|
123
|
+
scale_x = view_width / original_size.width()
|
124
|
+
scale_y = view_height / original_size.height()
|
125
|
+
return min(scale_x, scale_y)
|
115
126
|
|
116
127
|
def setText(self) -> None:
|
117
128
|
t = []
|
@@ -1,23 +1,23 @@
|
|
1
1
|
excel2moodle/__init__.py,sha256=mnb-RWgmWIPSBk4S65a_jP6rxntAkTeYxN0ObUJalbQ,1801
|
2
2
|
excel2moodle/__main__.py,sha256=sG4ygwfVFskLQorBn-v98SvasNcPmwl_vLYpruT5Hk8,1175
|
3
|
-
excel2moodle/logger.py,sha256=
|
3
|
+
excel2moodle/logger.py,sha256=fq8ZOkCI1wj38v8IyrZsUlpt16onlSH_phqbVvYUwBQ,3725
|
4
4
|
excel2moodle/core/__init__.py,sha256=87BwhtZse72Tk17Ib-V9X2k9wkhmtVnEj2ZmJ9JBAnI,63
|
5
|
-
excel2moodle/core/category.py,sha256=
|
6
|
-
excel2moodle/core/dataStructure.py,sha256=
|
5
|
+
excel2moodle/core/category.py,sha256=wLzpbweQbzaItdbp2NCPI_Zmk94fy1EDOwEEN8zPvkU,2123
|
6
|
+
excel2moodle/core/dataStructure.py,sha256=sDdum2I0EZRuXTgbVSsLX4aBINfzkmNXBCHzUVBhxH8,16022
|
7
7
|
excel2moodle/core/etHelpers.py,sha256=G37qplp8tPJxqHNCBrf2Wo0jJZ0aDbxE9slQavqYqd8,2293
|
8
8
|
excel2moodle/core/exceptions.py,sha256=9xfsaIcm6Yej6QAZga0d3DK3jLQejdfgJARuAaG-uZY,739
|
9
9
|
excel2moodle/core/globals.py,sha256=Zm1wcrzQTRnhjrkwgBvo7VjKCFdPMjh-VLSSI5_QCO8,2837
|
10
10
|
excel2moodle/core/numericMultiQ.py,sha256=vr-gYogu2sf2a_Bhvhnu1ZSZFZXM32MfhJesjTkoOQM,2618
|
11
|
-
excel2moodle/core/parser.py,sha256=
|
12
|
-
excel2moodle/core/question.py,sha256=
|
13
|
-
excel2moodle/core/settings.py,sha256=
|
11
|
+
excel2moodle/core/parser.py,sha256=y0BXXt5j-4gRZO8otmEZ1Rmb0DW7hziesUoZ2kVpo9Y,8235
|
12
|
+
excel2moodle/core/question.py,sha256=BSbLE_EpSOWma1B06ssZ5Y-B2GTNJZ6GXdBB6oSQQPo,11709
|
13
|
+
excel2moodle/core/settings.py,sha256=27D-P44rYk-DMrwI1dNpxHcznpFQf1W3XZrOc8e6rX4,5855
|
14
14
|
excel2moodle/core/stringHelpers.py,sha256=OzFZ6Eu3PeBLKb61K-aeVfUZmVuBerr9KfyOsuNRd7Y,2403
|
15
|
-
excel2moodle/core/validator.py,sha256=
|
15
|
+
excel2moodle/core/validator.py,sha256=ssgkyUwrR-0AGPX1cUqvRwZsGja13J7HQ2W72ltqN-Y,4683
|
16
16
|
excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
|
17
17
|
excel2moodle/extra/equationVerification.py,sha256=GLJl1r90d8AAiNy0H2hooZrg3D6aEwNfifYKAe3aGxM,3921
|
18
18
|
excel2moodle/question_types/__init__.py,sha256=81mss0g7SVtnlb-WkydE28G_dEAAf6oT1uB8lpK2-II,1041
|
19
19
|
excel2moodle/question_types/cloze.py,sha256=hgyv244PChnl7FbxcAMQiC4yzBhV6J3Bg4PHgZQyRmQ,7677
|
20
|
-
excel2moodle/question_types/mc.py,sha256=
|
20
|
+
excel2moodle/question_types/mc.py,sha256=2kn6dPjFVg97H8SlUBFbcPjzDk84vgDGCMOtSABseu0,5225
|
21
21
|
excel2moodle/question_types/nf.py,sha256=bMP4IXrhnXmAI0NmjEc7DtX4xGaUbxzLicE2LjeaUho,1150
|
22
22
|
excel2moodle/question_types/nfm.py,sha256=VYrQmCsEZqfn0x_coBsDw3XvrUA4sy_yj0WS7Nsffqw,4950
|
23
23
|
excel2moodle/ui/UI_equationChecker.py,sha256=evQDlqCHeooJcAnYjhFCyjlPhfknr7ULGKQwMmqQeJ4,8947
|
@@ -25,14 +25,14 @@ excel2moodle/ui/UI_exportSettingsDialog.py,sha256=71xxXEqtewN0ReMfJ5t4gbrX_Bf0VE
|
|
25
25
|
excel2moodle/ui/UI_mainWindow.py,sha256=asWUmKIYqufKUvRuCuA1JoMyv4qfRXyoR70F0331lww,19291
|
26
26
|
excel2moodle/ui/UI_variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
|
27
27
|
excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
|
28
|
-
excel2moodle/ui/appUi.py,sha256=
|
29
|
-
excel2moodle/ui/dialogs.py,sha256=
|
28
|
+
excel2moodle/ui/appUi.py,sha256=88WODtEWqX1oQJebbPhlQChKM5N_9BH0ZuOpKVYrKm0,10863
|
29
|
+
excel2moodle/ui/dialogs.py,sha256=Gt6Bj1iCweC7CPP9VBi961ht799frMuxn0VNWvQARaM,7303
|
30
30
|
excel2moodle/ui/equationChecker.py,sha256=ANpN7S0llkp6pGL1sKHII1Jc8YUvgDR458UnGVnZZOo,2702
|
31
31
|
excel2moodle/ui/treewidget.py,sha256=az64swVj1yQUsioeaZys32AauvQDdC4EKcqdbbWgL6s,2489
|
32
32
|
excel2moodle/ui/windowDoc.py,sha256=WvzHj6F4JvHP82WlTsyFeOXW024Xq3BUqtp--T4twuI,661
|
33
|
-
excel2moodle-0.
|
34
|
-
excel2moodle-0.
|
35
|
-
excel2moodle-0.
|
36
|
-
excel2moodle-0.
|
37
|
-
excel2moodle-0.
|
38
|
-
excel2moodle-0.
|
33
|
+
excel2moodle-0.5.0.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
|
34
|
+
excel2moodle-0.5.0.dist-info/METADATA,sha256=Dc3PVham-yIk0fVJAZBU_2D59TVMNkxwYs23TyUvWJo,2951
|
35
|
+
excel2moodle-0.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
36
|
+
excel2moodle-0.5.0.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
|
37
|
+
excel2moodle-0.5.0.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
|
38
|
+
excel2moodle-0.5.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|