excel2moodle 0.4.4__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 -39
- excel2moodle/core/parser.py +6 -1
- excel2moodle/core/question.py +39 -15
- excel2moodle/core/settings.py +6 -5
- 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.4.dist-info → excel2moodle-0.5.0.dist-info}/METADATA +1 -1
- {excel2moodle-0.4.4.dist-info → excel2moodle-0.5.0.dist-info}/RECORD +15 -15
- {excel2moodle-0.4.4.dist-info → excel2moodle-0.5.0.dist-info}/WHEEL +0 -0
- {excel2moodle-0.4.4.dist-info → excel2moodle-0.5.0.dist-info}/entry_points.txt +0 -0
- {excel2moodle-0.4.4.dist-info → excel2moodle-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {excel2moodle-0.4.4.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,35 +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
|
-
if tol > 0 and tol < 1:
|
130
|
-
self.settings.set(Tags.TOLERANCE, tol)
|
131
|
-
else:
|
132
|
-
self.settings.set(tag, value)
|
133
|
-
if Tags.IMPORTMODULE in settings.index:
|
130
|
+
def _validateProjectSettings(self, sheetPath: Path) -> None:
|
131
|
+
if Tags.IMPORTMODULE in self.settings:
|
134
132
|
logger.warning(
|
135
133
|
"Appending: %s to sys.path. All names defined by it will be usable",
|
136
|
-
|
134
|
+
sheetPath,
|
137
135
|
)
|
138
|
-
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.")
|
139
139
|
imgFolder = self.settings.get(Tags.SPREADSHEETPATH).parent / self.settings.get(
|
140
140
|
Tags.PICTURESUBFOLDER
|
141
141
|
)
|
142
|
-
|
143
|
-
|
144
|
-
|
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")
|
145
154
|
|
146
155
|
def initAllCategories(self, sheetPath: Path | None = None) -> None:
|
147
156
|
"""Read all category sheets and initialize all Categories."""
|
@@ -228,29 +237,26 @@ class QuestionDB:
|
|
228
237
|
|
229
238
|
:emits: categoryReady(self) Signal.
|
230
239
|
"""
|
231
|
-
categoryDf = self.harmonizeDFIndex(categoryDf)
|
232
|
-
|
233
|
-
self.categoriesMetaData
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
if "version" in self.categoriesMetaData
|
241
|
-
and not pd.isna(self.categoriesMetaData["version"].loc[categoryName])
|
242
|
-
else self.settings.get(Tags.VERSION)
|
243
|
-
)
|
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)
|
244
249
|
category = Category(
|
245
250
|
categoryName,
|
246
251
|
self.categoriesMetaData["description"].loc[categoryName],
|
247
252
|
dataframe=categoryDf,
|
248
|
-
|
249
|
-
version=version,
|
253
|
+
settings=categorySettings,
|
250
254
|
)
|
251
255
|
if hasattr(self, "categories"):
|
252
256
|
self.categories[categoryName] = category
|
253
257
|
self.signals.categoryReady.emit(category)
|
258
|
+
else:
|
259
|
+
logger.warning("Can't append initialized category to the database")
|
254
260
|
logger.debug("Category %s is initialized", categoryName)
|
255
261
|
return category
|
256
262
|
|
@@ -280,7 +286,7 @@ class QuestionDB:
|
|
280
286
|
self.signals.categoryQuestionsReady.emit(category)
|
281
287
|
|
282
288
|
@classmethod
|
283
|
-
def setupAndParseQuestion(cls, category: Category, qNumber: int) -> None:
|
289
|
+
def setupAndParseQuestion(cls, category: Category, qNumber: int) -> Question | None:
|
284
290
|
"""Check if the Question Data is valid. Then parse it.
|
285
291
|
|
286
292
|
The Question data is accessed from `category.dataframe` via its number
|
@@ -316,7 +322,7 @@ class QuestionDB:
|
|
316
322
|
question = QuestionTypeMapping[qtype].create(category, validData)
|
317
323
|
if question.isParsed:
|
318
324
|
locallogger.info("Question already parsed")
|
319
|
-
return
|
325
|
+
return None
|
320
326
|
if isinstance(question, NFQuestion):
|
321
327
|
cls.nfParser.setup(question)
|
322
328
|
locallogger.debug("setup a new NF parser ")
|
@@ -337,6 +343,7 @@ class QuestionDB:
|
|
337
343
|
msg = "couldn't setup Parser"
|
338
344
|
raise QNotParsedException(msg, question.id)
|
339
345
|
category.questions[qNumber] = question
|
346
|
+
return question
|
340
347
|
|
341
348
|
def appendQuestions(
|
342
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/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
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
|