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.
@@ -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
- points: float = 0,
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.points = points
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.version}{self.n:02d}"
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._setProjectSettings(settingDf, mainPath=sheetPath.parent)
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 _setProjectSettings(
122
- self, settings: pd.DataFrame, mainPath: Path | None = None
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
- mainPath,
134
+ sheetPath,
137
135
  )
138
- sys.path.append(str(mainPath))
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
- if Tags.PICTURESUBFOLDER not in settings.index:
143
- logger.warning("You didn't specify an image Folder. This may cause errors.")
144
- self.settings.set(Tags.PICTUREFOLDER, imgFolder.resolve())
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
- points = (
233
- self.categoriesMetaData["points"].loc[categoryName]
234
- if "points" in self.categoriesMetaData
235
- and not pd.isna(self.categoriesMetaData["points"]).loc[categoryName]
236
- else self.settings.get(Tags.POINTS)
237
- )
238
- version = (
239
- self.categoriesMetaData["version"].loc[categoryName]
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
- points=points,
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
@@ -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(picKey, svgFolder, self.question.id)
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:
@@ -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
- try:
64
- typed = key.typ()(val)
65
- except ValueError:
66
- return None
67
- return typed
68
- return settings.get(key)
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 < 10:
266
- picID = f"{self.questionId[:3]}{imgID:02d}"
288
+ if imgID < 100:
289
+ picID = f"{self.questionId[:2]}{imgID:02d}"
267
290
  elif imgID < 10000:
268
- picID = f"{self.questionId[:1]}{imgID:04d}"
269
- elif imgID <= 100000:
270
- picID = str(imgID)
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
@@ -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 = "wrongsignpercentage", int, 50
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, type %s", key, value, tag.typ())
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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;")
94
109
  color = self.logLevelColors.get(record.levelname, "black")
95
- prettyMessage = f'<span style="color:{color};">{log_message}</span>'
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
- w = self.settings.get(Tags.ANSPICWIDTH)
107
+ width = self.rawInput.get(Tags.ANSPICWIDTH)
108
108
  self.trueImgs: list[Picture] = [
109
- Picture(t, imgFolder, self.question.id, width=w)
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=w)
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()
@@ -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(self.question.picture.path))
103
+ self.picItem = QGraphicsSvgItem(str(path))
102
104
  else:
103
- pic = QtGui.QPixmap(self.question.picture.path)
104
- aspRat = pic.height() // pic.width()
105
- width = 400
106
- scaleHeight = aspRat * width
107
- self.picItem = QtWidgets.QGraphicsPixmapItem(
108
- pic.scaled(
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
- # else:
114
- # self.ui.graphicsView.setFixedHeight(1)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: excel2moodle
3
- Version: 0.4.4
3
+ Version: 0.5.0
4
4
  Summary: A package for converting questions from a spreadsheet, to valid moodle-xml
5
5
  Author: Jakob Bosse
6
6
  License-Expression: GPL-3.0-or-later
@@ -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=S9tvSwsOx2xta_AhGKHPNgi5FaiycyUDtkzHlIlNAX8,3193
3
+ excel2moodle/logger.py,sha256=fq8ZOkCI1wj38v8IyrZsUlpt16onlSH_phqbVvYUwBQ,3725
4
4
  excel2moodle/core/__init__.py,sha256=87BwhtZse72Tk17Ib-V9X2k9wkhmtVnEj2ZmJ9JBAnI,63
5
- excel2moodle/core/category.py,sha256=TzntDhEURshxWuI6qc_K_t5bSTLXn6s0n_RA56Basg8,1885
6
- excel2moodle/core/dataStructure.py,sha256=lmquDdRyMqKxHjbcMOJ83BRs1otXqG9vhNFVfQsljwE,15578
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=Ec8bQ0CiiZCIdzQovfH2rce406wmyRmvhpDrCPeQfGU,8112
12
- excel2moodle/core/question.py,sha256=nJDs8SPDRYP6xG4rvo8Bbh1734eQ96DZxVXb7yrT8LM,10802
13
- excel2moodle/core/settings.py,sha256=JhcgZcOrgFC7P4h9fSXZAApEl_9dE643r5arFpU1HXQ,5808
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=rHKIKSNeHyto84bI9B3wMKzNJuluplsCmYM4k2HW3wQ,5213
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=NXlBcjxHBeUzpU8rvqwN3wxad8AUgpMjXidwwVdC9GM,10863
29
- excel2moodle/ui/dialogs.py,sha256=JbQ1y55wbzWVTY0nxdGrUm81AdOIY3PXKgET03Hc6Ys,6760
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.4.4.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
34
- excel2moodle-0.4.4.dist-info/METADATA,sha256=NnGjQn0V6gBHcWuIjnleEqTeeFK5zVS0bDpwB6240SE,2951
35
- excel2moodle-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- excel2moodle-0.4.4.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
37
- excel2moodle-0.4.4.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
38
- excel2moodle-0.4.4.dist-info/RECORD,,
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,,