From 63f3e96be905d13846ad1a78df1ba3d10409dc0e Mon Sep 17 00:00:00 2001
From: Mathieu Loiseau <mathieu.loiseau@univ-grenoble-alpes.fr>
Date: Tue, 25 Jan 2022 21:41:21 +0100
Subject: [PATCH] images

---
 .gitignore    |  1 +
 XML_Moodle.py | 68 ++++++++++++++++++++++++++++++++++-----------------
 utils.py      | 32 +++++++++++++++---------
 3 files changed, 67 insertions(+), 34 deletions(-)

diff --git a/.gitignore b/.gitignore
index ecc788e..6df43f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 quiz-*.xml
 __pycache__
+data
diff --git a/XML_Moodle.py b/XML_Moodle.py
index f9ad0d4..da3c8ba 100755
--- a/XML_Moodle.py
+++ b/XML_Moodle.py
@@ -1,11 +1,16 @@
 #!/usr/bin/env python3
 import xml.etree.ElementTree as ET
 from utils import strip_tags, mlang_2_multiling, score_2_str
+import base64
+from PIL import Image
+from io import BytesIO
+
 class Quizz:
-	def __init__(self, file_name):
+	def __init__(self, file_name, target_folder):
 		self.file_name = file_name
 		self.tree = ET.parse(self.file_name)
 		self.questions = {}
+		self.folder = target_folder
 		self.parse()
 
 	def get_tree(self):
@@ -15,15 +20,15 @@ class Quizz:
 		if c not in self.questions.keys():
 			self.questions[c] = []
 		if q.attrib['type'] == "multichoice":
-			newQ = MCQ(q,c,len(self.questions[c]))
+			newQ = MCQ(q,c,len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "shortanswer":
-			newQ = Question(q,c,len(self.questions[c]))
+			newQ = Question(q,c,len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "truefalse":
-			newQ = TF(q,c,len(self.questions[c]))
+			newQ = TF(q,c,len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "matching":
-			newQ = Question(q,c,len(self.questions[c]))
+			newQ = Question(q,c,len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "ddimageortext":
-			newQ = Question(q,c,len(self.questions[c]))
+			newQ = Question(q,c,len(self.questions[c]),self.folder)
 		else:
 			raise f"{q.attrib['type']} is not expected"
 		self.questions[c].append(newQ)
@@ -43,17 +48,33 @@ class Quizz:
 				res += str(q)
 		return res
 
+	def save(self):
+		name = self.file_name[self.file_name.rfind('/')+1:self.file_name.rfind('.')]
+		output = open(self.folder+"/"+name+".tex", "w")
+		output.write(self.__str__())
+
 class Question:
-	def __init__(self,xmlQ,c,n):
-		self.q = mlang_2_multiling(strip_tags(xmlQ.find("questiontext/text").text))
+	def __init__(self,xmlQ,c,n,f):
+		self.folder = f
+		self.q = mlang_2_multiling(strip_tags(xmlQ.find("questiontext/text").text, self.folder))
 		self.category = c
 		self.id = f"{c[c.rfind(':')+1:]}_{n}"
-		self.env = "questionmult"
+		self.env = "todo:"+xmlQ.attrib["type"]
 		self.max = float(xmlQ.find("defaultgrade").text)
+		self.parseImages(xmlQ.findall(".//file"))
+
+	def parseImages(self, file_elements):
+		for i in file_elements:
+			name = i.attrib["name"]
+			print(name)
+			im = Image.open(BytesIO(base64.b64decode(i.text)))
+			ext = name[name.rfind('.')+1:].upper()
+			im.save(self.folder+"/"+name, ext)
+
 	def __str__(self):
 		return """\\element{"""+self.category+"""}{
 	\\begin{"""+self.env+"""}{"""+self.id+"""}\\nbpoints{"""+score_2_str(self.max)+"""}
-		"""+self.q+"\\end{"+self.env+"}"
+		"""+self.q+"\\end{"+self.env+"}\n"
 
 class Answer:
 	def __init__(self,t,b,max,fb):
@@ -80,9 +101,10 @@ class Answer:
 
 
 class MCQ(Question):
-	def __init__(self, xmlQ,c,n):
-		super().__init__(xmlQ,c,n)
+	def __init__(self, xmlQ,c,n,f):
+		super().__init__(xmlQ,c,n,f)
 		self.choices = []
+		self.env = "questionmult"
 		self.__parseAnswers(xmlQ)
 
 	def __parseAnswers(self, xmlQ):
@@ -90,8 +112,8 @@ class MCQ(Question):
 		for a in xmlQ.findall("answer"):
 			fb = a.find("feedback/text").text
 			if fb != None:
-				fb = mlang_2_multiling(strip_tags(fb))
-			self.choices.append(Answer(mlang_2_multiling(strip_tags(a.find("text").text)), a.attrib['fraction'], self.max, fb))
+				fb = mlang_2_multiling(strip_tags(fb, self.folder))
+			self.choices.append(Answer(mlang_2_multiling(strip_tags(a.find("text").text, self.folder)), a.attrib['fraction'], self.max, fb))
 
 	def __str__(self):
 		res = """\\element{"""+self.category+"""}{
@@ -106,25 +128,25 @@ class MCQ(Question):
 		return res
 
 class TF(MCQ):
-	def __init__(self, xmlQ,c,n):
-		super(MCQ, self).__init__(xmlQ,c,n)
+	def __init__(self, xmlQ,c,n,f):
+		super(MCQ, self).__init__(xmlQ,c,n,f)
 		self.choices = []
 		self.env = "question"
 		self.shuffle = False
 		self.__parseAnswers(xmlQ)
 
 	def __parseAnswers(self, xmlQ):
-		print("ok")
 		for a in xmlQ.findall("answer"):
 			fb = a.find("feedback/text").text
 			if fb != None:
-				fb = mlang_2_multiling(strip_tags(fb))
+				fb = mlang_2_multiling(strip_tags(fb, self.folder))
 			if a.find("text").text == "true":
-				self.choices.append(Answer("\\multi{vrai}{true}", a.attrib['fraction'], self.max, fb))
+				self.choices.append(Answer("\\multiling{vrai}{true}", a.attrib['fraction'], self.max, fb))
 			else:
-				self.choices.append(Answer("\\multi{faux}{false}", a.attrib['fraction'], self.max, fb))
+				self.choices.append(Answer("\\multiling{faux}{false}", a.attrib['fraction'], self.max, fb))
 
 if __name__ == "__main__":
-	quizz = Quizz("quiz-GI-4-SID-S1-top-20201204-1620.xml")
-	#quizz = Quizz("quiz-GI-4.xml")
-	print(quizz)
+	quizz = Quizz("data/quiz-GI-4-SID-S1-top-20201204-1620.xml", "data")
+	#quizz = Quizz("data/quiz-GI-4.xml" , "data")
+	#print(quizz)
+	quizz.save()
diff --git a/utils.py b/utils.py
index 0ca1236..22b2e54 100644
--- a/utils.py
+++ b/utils.py
@@ -1,22 +1,32 @@
 #!/usr/bin/env python3
 from re import compile,sub,escape
+from html import unescape
+from urllib.parse import unquote
 
 def html_2_tex(text):
 	#https://code.activestate.com/recipes/81330-single-pass-multiple-replace/
-	dict = {'&nbsp;': '~', '&gt;':'>', '&lt;': '<'}
-	""" Replace in 'text' all occurences of any key in the given
-	dictionary by its corresponding value.  Returns the new tring."""
-	# Create a regular expression  from the dictionary keys
-	regex = compile("(%s)" % "|".join(map(escape, dict.keys())))
+	# dict = {'&nbsp;': '~', '&gt;':'>', '&lt;': '<'}
+	# """ Replace in 'text' all occurences of any key in the given
+	# dictionary by its corresponding value.  Returns the new tring."""
+	# # Create a regular expression  from the dictionary keys
+	# regex = compile("(%s)" % "|".join(map(escape, dict.keys())))
+	#
+	# # For each match, look-up corresponding value in dictionary
+	# return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
+	return unescape(text)
 
-	# For each match, look-up corresponding value in dictionary
-	return regex.sub(lambda mo: dict[mo.string[mo.start():mo.end()]], text)
 
-def remove_moodle_cdata(txt):
-	return txt.replace("<![CDATA[","").replace("]]>","")
+def remove_moodle_cdata(txt, folder):
+	return sub(r'<img src="[^/]*/([^"]*)" (alt="([^"]*)")?[^>]*>', r"""\\begin{figure}[h!]
+	\\begin{center}
+		\\includegraphics[width=0.8\\linewidth]{$$$$folder$$$$/\1}
+	\\end{center}
+	\\caption{\3}
+\\end{figure}
+""",txt).replace("<![CDATA[","").replace("]]>","").replace("$$$$folder$$$$", folder)
 
-def strip_tags(txt):
-	return html_2_tex(sub(compile('<.*?>'), '',remove_moodle_cdata(txt)))
+def strip_tags(txt, folder):
+	return html_2_tex(sub(compile('<.*?>'), '',remove_moodle_cdata(unquote(txt), folder)))
 
 def mlang_2_multiling(txt):
 	return sub(r"\{mlang en\}(.*?)\{mlang\}((\{mlang other\})|(\{mlang fr\}))(.*?)\{mlang\}", r"\\multiling{\5}{\1}",txt)
-- 
GitLab