diff --git a/XML_Moodle.py b/XML_Moodle.py index 9e54dd6b87b61a2fcced172f87dec36ff7b93d08..d1659b8551f28dfe2d4c671fecff16ac3079ffdc 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 56beef04d21bdafeb8daa6cf3a1273908c207d89..4f9302aeebf1eaffa907b3f2b057be55ff24a919 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 = {' ': '~', '>':'>', '<': '<'} - # """ 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(" ","~")),folder,q))) def mlang_2_multiling(txt, which = "both"): if which == "fr":