excel2moodle 0.6.3__py3-none-any.whl → 0.6.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.
@@ -5,7 +5,7 @@ import lxml.etree as ET
5
5
 
6
6
  from excel2moodle.core import stringHelpers
7
7
  from excel2moodle.core.globals import TextElements
8
- from excel2moodle.core.question import ParametricQuestion
8
+ from excel2moodle.core.question import ParametricQuestion, Parametrics
9
9
  from excel2moodle.logger import LogAdapterQuestionID
10
10
 
11
11
  loggerObj = logging.getLogger(__name__)
@@ -20,9 +20,8 @@ class BulletList:
20
20
  self.logger = LogAdapterQuestionID(loggerObj, {"qID": self.id})
21
21
  self._setupBullets(rawBullets)
22
22
 
23
- def updateBullets(
24
- self, variables: dict[str, list[float]], variant: int = 1
25
- ) -> None:
23
+ def update(self, parametrics: Parametrics, variant: int = 1) -> None:
24
+ variables: dict[str, list[float]] = parametrics.variables
26
25
  for var, bullet in self.bullets.items():
27
26
  bullet.update(value=variables[var][variant - 1])
28
27
 
@@ -117,14 +117,15 @@ class QuestionDB:
117
117
  f,
118
118
  sheet_name="settings",
119
119
  index_col=0,
120
+ header=None,
120
121
  engine="calamine",
121
122
  )
122
123
  logger.debug("Found the settings: \n\t%s", settingDf)
123
124
  settingDf = self.harmonizeDFIndex(settingDf)
124
- for tag, value in settingDf.iterrows():
125
- val = value.iloc[0]
126
- if pd.notna(val):
127
- self.settings.set(tag, val)
125
+ settingsDict = Validator.dfToDict(settingDf[1])
126
+ Validator.listify(settingsDict)
127
+ for tag, value in settingsDict.items():
128
+ self.settings.set(tag, value)
128
129
 
129
130
  self._validateProjectSettings(sheetPath=sheetPath)
130
131
  with Path(sheetPath).open("rb") as f:
@@ -11,8 +11,9 @@ from excel2moodle.core.globals import (
11
11
  XMLTags,
12
12
  feedBElements,
13
13
  )
14
- from excel2moodle.core.question import Picture, Question
14
+ from excel2moodle.core.question import ParametricQuestion, Picture, Question
15
15
  from excel2moodle.core.settings import Settings, Tags
16
+ from excel2moodle.extra.scriptCaller import MediaCall
16
17
  from excel2moodle.logger import LogAdapterQuestionID
17
18
 
18
19
  loggerObj = logging.getLogger(__name__)
@@ -129,9 +130,13 @@ class QuestionParser:
129
130
  )
130
131
  self.htmlRoot.append(bullets.element)
131
132
  self.question.bulletList = bullets
133
+ if isinstance(self.question, ParametricQuestion):
134
+ self.question.updateQue = [bullets]
132
135
  if self.hasPicture():
133
136
  self.htmlRoot.append(self.question.picture.htmlTag)
134
137
  textRootElem.append(self.question.picture.element)
138
+ if Tags.MEDIACALL in self.rawInput:
139
+ self.insertScriptedMedia()
135
140
  ansList = self._parseAnswers()
136
141
  if ansList is not None:
137
142
  for ele in ansList:
@@ -166,6 +171,19 @@ class QuestionParser:
166
171
  ele.append(eth.getCdatTxtElement(par))
167
172
  return ele
168
173
 
174
+ def insertScriptedMedia(self) -> None:
175
+ """Load the scripts, insert the div and call a Function."""
176
+ for script in self.rawInput.get(Tags.MEDIASCRIPTS):
177
+ ET.SubElement(
178
+ self.htmlRoot, "script", type="text/javascript", src=script
179
+ ).text = ""
180
+ divId = f"scriptedMedia-{self.question.id}"
181
+ ET.SubElement(self.htmlRoot, "div", id=divId).text = ""
182
+ scriptCall = MediaCall(self.rawInput.get(Tags.MEDIACALL), divId=divId)
183
+ self.htmlRoot.append(scriptCall.element)
184
+ if isinstance(self.question, ParametricQuestion):
185
+ self.question.updateQue.append(scriptCall)
186
+
169
187
  def _parseAnswers(self) -> list[ET.Element] | None:
170
188
  """Needs to be implemented in the type-specific subclasses."""
171
189
  return None
@@ -53,7 +53,14 @@ class QuestionData(dict):
53
53
  @overload
54
54
  def get(
55
55
  self,
56
- key: Literal[Tags.BPOINTS, Tags.TRUE, Tags.FALSE, Tags.QUESTIONPART, Tags.TEXT],
56
+ key: Literal[
57
+ Tags.BPOINTS,
58
+ Tags.TRUE,
59
+ Tags.FALSE,
60
+ Tags.QUESTIONPART,
61
+ Tags.TEXT,
62
+ Tags.MEDIASCRIPTS,
63
+ ],
57
64
  default: object = None,
58
65
  ) -> list: ...
59
66
  @overload
@@ -115,6 +122,7 @@ class Question:
115
122
  }
116
123
  optionalTags: ClassVar[dict[Tags, type | UnionType]] = {
117
124
  Tags.PICTURE: int | str,
125
+ Tags.MEDIACALL: list,
118
126
  }
119
127
 
120
128
  def __init_subclass__(cls, **kwargs) -> None:
@@ -202,6 +210,7 @@ class ParametricQuestion(Question):
202
210
  self.rules: list[str] = []
203
211
  self.parametrics: Parametrics
204
212
  self._variant: int
213
+ self.updateQue: list
205
214
 
206
215
  @property
207
216
  def currentVariant(self) -> int:
@@ -214,13 +223,12 @@ class ParametricQuestion(Question):
214
223
  `Question` returns the Element.
215
224
 
216
225
  """
217
- if not hasattr(self, "bulletList"):
218
- msg = "Can't assemble a parametric question, without the bulletPoints variables"
226
+ if not hasattr(self, "updateQue"):
227
+ msg = "Can't assemble a parametric question, without the updateQue"
219
228
  raise QNotParsedException(msg, self.id)
220
229
 
221
- self.bulletList.updateBullets(
222
- variables=self.parametrics.variables, variant=variant
223
- )
230
+ for obj in self.updateQue:
231
+ obj.update(parametrics=self.parametrics, variant=variant)
224
232
  self._variant = variant
225
233
  return super().getUpdatedElement(variant)
226
234
 
@@ -96,9 +96,12 @@ class Tags(StrEnum):
96
96
  PCORRECFB = "partialcorrectfeedback", str, "Your answer is partially right."
97
97
  GENERALFB = "feedback", str, "You answered this question."
98
98
 
99
+ MEDIASCRIPTS = "mediascripts", list, None
100
+ MEDIACALL = "parametricmedia", str, None
101
+
99
102
 
100
103
  class Settings:
101
- values: ClassVar[dict[str, str | float | Path]] = {}
104
+ values: ClassVar[dict[str, str | float | Path | list]] = {}
102
105
 
103
106
  def __contains__(self, tag: Tags) -> bool:
104
107
  return bool(tag in type(self).values)
@@ -54,31 +54,29 @@ class Validator:
54
54
  if missing is not None:
55
55
  raise InvalidFieldException(msg, qid, missing)
56
56
 
57
- def getQuestionData(self) -> QuestionData:
58
- """Get the data from the spreadsheet as a dictionary."""
59
- self.qdata: dict[str, int | float | list[str] | str] = {}
60
- for idx, val in self.df.items():
61
- if not isinstance(idx, str):
57
+ @staticmethod
58
+ def dfToDict(df: pd.Series) -> dict[str, int | float | list[str] | str]:
59
+ """Convert a dataframe to dictionary, preserving lists."""
60
+ dic: dict[str, int | float | list[str] | str] = {}
61
+ for var, val in df.items():
62
+ if not isinstance(var, str):
62
63
  continue
63
64
  if pd.isna(val):
64
65
  continue
65
- if idx in self.qdata:
66
- if isinstance(self.qdata[idx], list):
67
- self.qdata[idx].append(val)
66
+ if var in dic:
67
+ if isinstance(dic[var], list):
68
+ dic[var].append(val)
68
69
  else:
69
- existing = self.qdata[idx]
70
- self.qdata[idx] = [existing, val]
70
+ existing = dic[var]
71
+ dic[var] = [existing, val]
71
72
  else:
72
- self.qdata[idx] = val
73
- return self.formatQData()
74
-
75
- def formatQData(self) -> QuestionData:
76
- """Format the dictionary to The types for QuestionData."""
77
- listTags = (Tags.BPOINTS, Tags.TRUE, Tags.FALSE, Tags.TEXT, Tags.QUESTIONPART)
78
- for tag in listTags:
79
- for key in self.qdata:
80
- if key.startswith(tag) and not isinstance(self.qdata[key], list):
81
- self.qdata[key] = stringHelpers.getListFromStr(self.qdata[key])
73
+ dic[var] = val
74
+ return dic
75
+
76
+ def getQuestionData(self) -> QuestionData:
77
+ """Get the data from the spreadsheet as a dictionary."""
78
+ self.qdata = self.dfToDict(self.df)
79
+ self.listify(self.qdata)
82
80
  tol = float(self.qdata.get(Tags.TOLERANCE, 0))
83
81
  if tol < 0 or tol > 99:
84
82
  tol = settings.get(Tags.TOLERANCE)
@@ -89,6 +87,18 @@ class Validator:
89
87
  self.qdata[Tags.EQUATION] = str(self.qdata[Tags.RESULT])
90
88
  return QuestionData(self.qdata)
91
89
 
90
+ @staticmethod
91
+ def listify(dictionary: dict) -> None:
92
+ """Converts to list all tag values, which are supposed to be a list."""
93
+ for key in dictionary:
94
+ k: str = key.split(":")[0]
95
+ if k in Tags:
96
+ tag = Tags(k)
97
+ logger.info("Got the Tag %s from key: %s", tag, key)
98
+ if tag.typ() is list and not isinstance(dictionary[key], list):
99
+ dictionary[key] = stringHelpers.getListFromStr(dictionary[key])
100
+ logger.info("Converted Input to list for %s", key)
101
+
92
102
  def _mandatory(self) -> tuple[bool, Tags | None]:
93
103
  """Detects if all keys of mandatory are filled with values."""
94
104
  checker = pd.Series.notna(self.df)
@@ -3,7 +3,7 @@
3
3
  ################################################################################
4
4
  ## Form generated from reading UI file 'UI_updateDlg.ui'
5
5
  ##
6
- ## Created by: Qt User Interface Compiler version 6.9.1
6
+ ## Created by: Qt User Interface Compiler version 6.9.0
7
7
  ##
8
8
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
9
9
  ################################################################################
@@ -24,7 +24,7 @@ class Ui_UpdateDialog(object):
24
24
  if not UpdateDialog.objectName():
25
25
  UpdateDialog.setObjectName(u"UpdateDialog")
26
26
  UpdateDialog.setWindowModality(Qt.WindowModality.NonModal)
27
- UpdateDialog.resize(534, 512)
27
+ UpdateDialog.resize(540, 512)
28
28
  UpdateDialog.setModal(True)
29
29
  self.verticalLayout = QVBoxLayout(UpdateDialog)
30
30
  self.verticalLayout.setObjectName(u"verticalLayout")
@@ -78,6 +78,10 @@ class Ui_UpdateDialog(object):
78
78
  self.label.setObjectName(u"label")
79
79
  sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
80
80
  self.label.setSizePolicy(sizePolicy)
81
+ font = QFont()
82
+ font.setPointSize(12)
83
+ font.setBold(True)
84
+ self.label.setFont(font)
81
85
 
82
86
  self.verticalLayout.addWidget(self.label)
83
87
 
@@ -101,6 +105,6 @@ class Ui_UpdateDialog(object):
101
105
  self.titleLabel.setText(QCoreApplication.translate("UpdateDialog", u"<h2>A new <i>excel2moodle</i> version is available!</h2>", None))
102
106
  self.fundingLabel.setText(QCoreApplication.translate("UpdateDialog", u"If you find this project useful, please consider supporting its development.", None))
103
107
  self.changelogLabel.setText(QCoreApplication.translate("UpdateDialog", u"<h3>Changelog:</h3>", None))
104
- self.label.setText(QCoreApplication.translate("UpdateDialog", u"To install the update run: 'uv tool upgrade excel2moodle'", None))
108
+ self.label.setText(QCoreApplication.translate("UpdateDialog", u"To install the update execute: 'uv tool upgrade excel2moodle'", None))
105
109
  # retranslateUi
106
110
 
excel2moodle/ui/appUi.py CHANGED
@@ -337,7 +337,7 @@ class MainWindow(QMainWindow):
337
337
  def openSpreadsheetExternally(self) -> None:
338
338
  if self.excelPath is None:
339
339
  return
340
- spreadsheetPath = QUrl(f"file://{self.excelPath.absolute()}")
340
+ spreadsheetPath = QUrl(f"file://{self.excelPath.resolve()}")
341
341
  logger.info("Opening: %s", spreadsheetPath)
342
342
  QDesktopServices.openUrl(spreadsheetPath)
343
343
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: excel2moodle
3
- Version: 0.6.3
3
+ Version: 0.6.4
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
@@ -90,6 +90,13 @@ If You want to support my work as well, you can by me a [coffee](https://ko-fi.c
90
90
 
91
91
  # Changelogs
92
92
 
93
+ ## 0.6.4 (2025-09-02)
94
+ Added Scripted Media Support
95
+
96
+ ### feature (1 change)
97
+
98
+ - [Added support for scripted Media content.](https://gitlab.com/jbosse3/excel2moodle/-/commit/2021942392147d0e9740af5286f469dd6226ffa5)
99
+
93
100
  ## 0.6.3 (2025-08-03)
94
101
  Lots of small improvements made
95
102
 
@@ -2,17 +2,17 @@ excel2moodle/__init__.py,sha256=W05Gsm3IOcxJnp4C-TPvxRiO3NR2L9g8PSIHDoRJua0,1893
2
2
  excel2moodle/__main__.py,sha256=B55ZK25z-HzIIox2xLYkJXMUwYzITPKGCi9fELMFGaM,1877
3
3
  excel2moodle/logger.py,sha256=fq8ZOkCI1wj38v8IyrZsUlpt16onlSH_phqbVvYUwBQ,3725
4
4
  excel2moodle/core/__init__.py,sha256=87BwhtZse72Tk17Ib-V9X2k9wkhmtVnEj2ZmJ9JBAnI,63
5
- excel2moodle/core/bullets.py,sha256=KwlxGOWbWexMkfXkY4Zg-gmxpzQCNZ09kSo8kPffGQU,3501
5
+ excel2moodle/core/bullets.py,sha256=TiRf2EfhsryE-KBfvo43dMtsMWuZi9L9H7TuwXAZ1rg,3550
6
6
  excel2moodle/core/category.py,sha256=fOMj2ynoAy6tXwmFhJ9uST9BQHiRJeU2BrkK1r57ek4,2897
7
- excel2moodle/core/dataStructure.py,sha256=BzBKg_fCT1FHuJum2aGLK_4RqmA2qluODvg1GwiEA7E,19797
7
+ excel2moodle/core/dataStructure.py,sha256=f3aqSPSIQxspYf1FmhFnlr4H1tc1gpVG_DQBdD0bZQk,19858
8
8
  excel2moodle/core/etHelpers.py,sha256=LzimWGuX6RH2TbfEnWUoAXT2Tr0z6P7bCANjxuANSX0,1667
9
9
  excel2moodle/core/exceptions.py,sha256=9xfsaIcm6Yej6QAZga0d3DK3jLQejdfgJARuAaG-uZY,739
10
10
  excel2moodle/core/globals.py,sha256=gvkl8Obq4XBW40B1L68Ewg06sonK27l-KIiodwFv8ic,2393
11
- excel2moodle/core/parser.py,sha256=wth3SPjlbWnS1zUVPap6PsJeVGCRatLuJZe6U7KgXBg,7540
12
- excel2moodle/core/question.py,sha256=eylkSKYQYo0uRxzfTZqoQNFAXq94mzzxbRWyknstupg,14187
13
- excel2moodle/core/settings.py,sha256=zqtA1fcQeO3bELLvS8bzP9TSZT7RksFxMaqnYYBXQCQ,6432
11
+ excel2moodle/core/parser.py,sha256=hjbA0i7N1oHoJHhOmvEtVl4Ryaqd0eqUS26bXS47CBo,8467
12
+ excel2moodle/core/question.py,sha256=H_4C2hO4Hrb5fMXnIsuxoeAJVWrHn9nSnNR2x4QnaqY,14324
13
+ excel2moodle/core/settings.py,sha256=_H3TJ67-4Q0hFi8g3JWFILJ6q3mfcEsBRaV74jhbko8,6531
14
14
  excel2moodle/core/stringHelpers.py,sha256=OzFZ6Eu3PeBLKb61K-aeVfUZmVuBerr9KfyOsuNRd7Y,2403
15
- excel2moodle/core/validator.py,sha256=ssgkyUwrR-0AGPX1cUqvRwZsGja13J7HQ2W72ltqN-Y,4683
15
+ excel2moodle/core/validator.py,sha256=6nZIyTwcXPT2jgi31QrBGur3Cq7A3Q9btLp5ALFEsKw,4998
16
16
  excel2moodle/extra/__init__.py,sha256=PM-id60HD21A3IcGC_fCYFihS8osBGZMIJCcN-ZRsIM,293
17
17
  excel2moodle/extra/equationVerification.py,sha256=oQpk-4cM0x_vKGEexC0Z1UyVoGG7w3V3RydtkM0MV_Y,3869
18
18
  excel2moodle/extra/updateQuery.py,sha256=kD_L23Qea9Cx4zUwfQVNJXXFbybd9cwE8sSbZrz7VF8,1554
@@ -25,17 +25,17 @@ excel2moodle/question_types/nfm.py,sha256=D5-aE4C7TAuwHFidXR15DLWNZ4JT-HVbPXI0Cz
25
25
  excel2moodle/ui/UI_equationChecker.py,sha256=evQDlqCHeooJcAnYjhFCyjlPhfknr7ULGKQwMmqQeJ4,8947
26
26
  excel2moodle/ui/UI_exportSettingsDialog.py,sha256=I0Vqw2TCWoUhDKxTgLoGaAo4_L77vfN8G7_zi7b_5lY,8254
27
27
  excel2moodle/ui/UI_mainWindow.py,sha256=9w8bRgOrVEX7BRGQvMuVhPCiSOsXYkMb4rxLDeRErII,21544
28
- excel2moodle/ui/UI_updateDlg.py,sha256=uabFUsK0O6cRSzNcM84mPntuwdXZCNkqsRDJxAOxG4Q,5144
28
+ excel2moodle/ui/UI_updateDlg.py,sha256=kPv6XyyTqKs2Avsji9peVUrsp0di4B41mXI0Qz_uLfA,5261
29
29
  excel2moodle/ui/UI_variableGenerator.py,sha256=DjpZnBELSqyOjJdwjXNP2V71rCPm3tr6_XkNT9F3e34,11205
30
30
  excel2moodle/ui/UI_variantDialog.py,sha256=snVaF3_YAc7NWjMRg7NzbjL_PzNbOpt4eiqElkE46io,5414
31
31
  excel2moodle/ui/__init__.py,sha256=4EdGtpzwH3rgw4xW9E5x9kdPQYwKbo9rehHRZTNxCrQ,44
32
- excel2moodle/ui/appUi.py,sha256=y5SWTNo45tIZ-HekdZkR7fPOD0QSP66uB_wlrk-b4YM,15046
32
+ excel2moodle/ui/appUi.py,sha256=woISWvPFegL9e0ntORNaMM1IxQSlP7qAHhYmNsYfI6U,15045
33
33
  excel2moodle/ui/dialogs.py,sha256=du6v17lh6LhgDDK0QltPzD-z8wUn3aD4QzaAQBmiTBQ,7314
34
34
  excel2moodle/ui/equationChecker.py,sha256=RII9DlZAlHqe5udBWzeUaozhtyi3ZkCZs8h3-oO6pEw,2700
35
35
  excel2moodle/ui/treewidget.py,sha256=3hZRLlrhp4FMXFyNY0LGDy7k1RSuKH87QyqB1N4qOqg,2335
36
- excel2moodle-0.6.3.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
37
- excel2moodle-0.6.3.dist-info/METADATA,sha256=gspbMXBm2lHiIkV-_-8XL45Q7ItMAEZV-7YFaDGJhZc,11891
38
- excel2moodle-0.6.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
- excel2moodle-0.6.3.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
40
- excel2moodle-0.6.3.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
41
- excel2moodle-0.6.3.dist-info/RECORD,,
36
+ excel2moodle-0.6.4.dist-info/licenses/LICENSE,sha256=ywQqe6Sitymkf2lV2NRcx_aGsaC-KbSl_EfEsRXmNRw,35135
37
+ excel2moodle-0.6.4.dist-info/METADATA,sha256=CBlHZ_zdBMe05oXoz-EKawkWz75QFS3la1-AZAyxvJw,12105
38
+ excel2moodle-0.6.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ excel2moodle-0.6.4.dist-info/entry_points.txt,sha256=myfMLDThuGgWHMJDPPfILiZqo_7D3fhmDdJGqWOAjPw,60
40
+ excel2moodle-0.6.4.dist-info/top_level.txt,sha256=5V1xRUQ9o7UmOCmNoWCZPAuy5nXp3Qbzyqch8fUGT_c,13
41
+ excel2moodle-0.6.4.dist-info/RECORD,,