From 26be2f42a3050eb341d8ae766f6288d56cbad780 Mon Sep 17 00:00:00 2001
From: Mathieu Loiseau <mathieu.loiseau@univ-grenoble-alpes.fr>
Date: Wed, 26 Jan 2022 08:27:21 +0100
Subject: [PATCH] listings

---
 XML_Moodle.py | 39 ++++++++++++++++++++++++---------------
 utils.py      | 40 +++++++++++++++++++++-------------------
 2 files changed, 45 insertions(+), 34 deletions(-)

diff --git a/XML_Moodle.py b/XML_Moodle.py
index 9e54dd6..d1659b8 100755
--- a/XML_Moodle.py
+++ b/XML_Moodle.py
@@ -10,6 +10,7 @@ class Quizz:
 		self.file_name = file_name
 		self.tree = ET.parse(self.file_name)
 		self.questions = {}
+		self.categories = {}
 		self.folder = target_folder
 		self.parse()
 
@@ -19,19 +20,21 @@ class Quizz:
 	def create_question(self, q, c):
 		if c not in self.questions.keys():
 			self.questions[c] = []
+			self.categories[c] = f"c{len(self.categories)}"
 		if q.attrib['type'] == "multichoice":
-			newQ = MCQ(q,c,len(self.questions[c]),self.folder)
+			newQ = MCQ(q,self.categories[c],len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "shortanswer":
-			newQ = ShortAnswer(q,c,len(self.questions[c]),self.folder)
+			newQ = ShortAnswer(q,self.categories[c],len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "truefalse":
-			newQ = TF(q,c,len(self.questions[c]),self.folder)
+			newQ = TF(q,self.categories[c],len(self.questions[c]),self.folder)
 		elif q.attrib['type'] == "matching":
-			newQ = Question(q,c,len(self.questions[c]),self.folder)
+			newQ = Question(q,self.categories[c],len(self.questions[c]),self.folder)#non traité
 		elif q.attrib['type'] == "ddimageortext":
-			newQ = Question(q,c,len(self.questions[c]),self.folder)
+			newQ = Question(q,self.categories[c],len(self.questions[c]),self.folder)#non traité
 		else:
 			raise f"{q.attrib['type']} is not expected"
-		self.questions[c].append(newQ)
+		if newQ.__class__.__name__ != "Question":
+			self.questions[c].append(newQ)
 
 	def parse(self):
 		questions = self.tree.findall("question")
@@ -43,10 +46,16 @@ class Quizz:
 
 	def __str__(self):
 		res = ""
-		for c_q in self.questions.values():
+		digest ="%%%Exemple d'examen\n\t\t%\\cleargroup{exam}\n"
+		for c,c_q in self.questions.items():
+			tmp = "\t\t%\\shufflegroup{"+self.categories[c]+"}%("+c+"):"
+			count = 0
 			for q in c_q:
 				res += str(q)
-		return res
+				count += 1
+			if count > 0:
+				digest += f"{tmp}{count} questions\n\t\t%\\copygroup[{int(count / 2)}]" + "{" + self.categories[c] + "}" + "{exam}\n"
+		return digest+"\t\t%\shufflegroup{exam}\n\t\t%\insertgroup{exam}\n\n"+res
 
 	def save(self):
 		name = self.file_name[self.file_name.rfind('/')+1:self.file_name.rfind('.')]
@@ -56,9 +65,9 @@ class Quizz:
 class Question:
 	def __init__(self,xmlQ,c,n,f):
 		self.folder = f
-		self.q = strip_tags(mlang_2_multiling(xmlQ.find("questiontext/text").text), self.folder)
+		self.id = mlang_2_multiling(xmlQ.find("name/text").text,"en")+f".{n}"
+		self.q = strip_tags(mlang_2_multiling(xmlQ.find("questiontext/text").text), self.folder,self.id)
 		self.category = c
-		self.id = mlang_2_multiling(xmlQ.find("name/text").text,"en")+f":{n}"
 		self.env = "todo:"+xmlQ.attrib["type"]
 		self.max = float(xmlQ.find("defaultgrade").text)
 		self.parseImages(xmlQ.findall(".//file"))
@@ -114,8 +123,8 @@ class MCQ(Question):
 		for a in xmlQ.findall("answer"):
 			fb = a.find("feedback/text").text
 			if fb != None:
-				fb = strip_tags(mlang_2_multiling(fb), self.folder)
-			self.choices.append(Answer(strip_tags(mlang_2_multiling(a.find("text").text), self.folder), a.attrib['fraction'], self.max, fb))
+				fb = strip_tags(mlang_2_multiling(fb), self.folder, self.id)
+			self.choices.append(Answer(strip_tags(mlang_2_multiling(a.find("text").text), self.folder,self.id), a.attrib['fraction'], self.max, fb))
 
 	def get_choice_env(self):
 		if self.choice_type == MCQ.VERTICAL:
@@ -148,7 +157,7 @@ class TF(MCQ):
 		for a in xmlQ.findall("answer"):
 			fb = a.find("feedback/text").text
 			if fb != None:
-				fb = strip_tags(mlang_2_multiling(fb), self.folder)
+				fb = strip_tags(mlang_2_multiling(fb), self.folder, self.id)
 			if a.find("text").text == "true":
 				self.choices.append(Answer("\\multiling{vrai}{true}", a.attrib['fraction'], self.max, fb))
 			else:
@@ -170,7 +179,7 @@ class ShortAnswer(TF):
 	def __str__(self):
 		res = """\\element{"""+self.category+"""}{
 	\\begin{"""+self.env+"""}{"""+self.id+"""}\\nbpoints{"""+score_2_str(self.max)+"""}
-		"""+self.q+"\n\t\t\\AMCOpen{lineheigh=0.8cm,lines="+str(self.nb_lines)+"}{"
+		"""+self.q+"\n\t\t\\AMCOpen{lineheight=0.66cm,lines="+str(self.nb_lines)+"}{"
 		for c in self.choices:
 			res += "\\wrongchoice{"+score_2_str(c)+"}\\scoring{"+score_2_str(c)+"}"
 		res += "\\correctchoice{"+score_2_str(self.max)+"}\\scoring{"+score_2_str(self.max)+"}"
@@ -178,7 +187,7 @@ class ShortAnswer(TF):
 		return res
 
 if __name__ == "__main__":
-	quizz = Quizz("data/quiz-GI-4-SID-S1-top-20201204-1620_corrigé.xml", "data")
+	quizz = Quizz("data/SID questions.xml", "data")
 	#quizz = Quizz("data/quiz-GI-4.xml" , "data")
 	#print(quizz)
 	quizz.save()
diff --git a/utils.py b/utils.py
index 56beef0..4f9302a 100644
--- a/utils.py
+++ b/utils.py
@@ -1,38 +1,40 @@
 #!/usr/bin/env python3
-from re import compile,sub,escape
+from re import compile,sub,escape,findall,DOTALL
 from html import unescape
 from urllib.parse import unquote
 
 unsafe = True
 
-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())))
-	#
-	# # 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)
+def process_listings(txt, folder="",q="q"):
+	txt = txt.replace("\r\n","\n")
+	listings = findall(r'\<pre\>(.*)\</pre\>', txt, DOTALL)
+	i=0
+	for l in listings:
+		if folder != "":
+			folder+="/"
+		i+=1
+		filename = folder+"Images/"+q+str(i)+".txt"
+		f = open(filename, "w")
+		f.write(unescape(l))
+		txt = txt.replace("<pre>"+l+"</pre>","\t\t\\lstinputlisting[language=Python]{"+filename.replace(folder,"")+"}")
+	return txt
 
-
-def remove_moodle_cdata(txt, folder):
+def remove_moodle_cdata(txt, folder, q):
 	global unsafe
+	txt = process_listings(txt, folder, q)
 	res = sub(r'<img src="[^/]*/([^"]*)" (alt="([^"]*)")?[^>]*>', r"""\\begin{figure}[h!]
 	\\begin{center}
-		\\includegraphics[width=0.8\\linewidth]{$$$$folder$$$$/\1}
+		\\includegraphics[width=0.8\\linewidth]{Images/\1}
 	\\end{center}
 	\\caption{\3}
 \\end{figure}
-""",txt).replace("<![CDATA[","").replace("]]>","").replace("$$$$folder$$$$", folder+"/Images").replace("<strong>","\\emph{").replace("</strong>","}").replace("<pre>","\\begin{lstlisting}[language=Python]\n").replace("</pre>","\\end{lstlisting}\n")
+""",txt).replace("<![CDATA[","").replace("]]>","").replace("<strong>","\\emph{").replace("</strong>","}")
 	if unsafe:
-		res = res.replace('<span style="font:monospace">',"\lstinline[language=python]|").replace('<span style="font-family:monospace">',"\lstinline[language=python]|").replace("</span>","|")
+		res = res.replace('<span style="font:monospace">',"\lstinline[language=python]|").replace('<span style="font-family:monospace">',"\lstinline[language=python]|").replace('<span style="font-family=monospace">',"\lstinline[language=python]|").replace("</span>","|")
 	return res
 
-def strip_tags(txt, folder):
-	return html_2_tex(sub(compile('<.*?>'), '',remove_moodle_cdata(unquote(txt), folder)))
+def strip_tags(txt, folder,q="q"):
+	return unescape(sub(compile('<.*?>'), '',remove_moodle_cdata(unquote(txt.replace("&nbsp;","~")),folder,q)))
 
 def mlang_2_multiling(txt, which = "both"):
 	if which == "fr":
-- 
GitLab