#!/usr/bin/python # -*- coding: utf-8 -*- # # Pyromaths # Un programme en Python qui permet de créer des fiches d'exercices types de # mathématiques niveau collège ainsi que leur corrigé en LaTeX. # Copyright (C) 2006 -- Jérôme Ortais (jerome.ortais@pyromaths.org) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # from lxml import etree from re import findall from os import chdir, chmod, popen from os.path import join from sys import platform from stat import S_IEXEC class quizs_html: def __init__(self, dir_name, data_dir): self.quizs = [] self.respondes = [] self.n_questions = int(0) chdir(data_dir) self.mimetex_path = join("mimetex", "mimetex.cgi") if platform.find("win")!= -1: self.mimetex_path = join("mimetex", "mimetex.exe") chmod(self.mimetex_path, S_IEXEC) self.dir_name = dir_name def html_question(self, type_moodle, name_quiz, exo, cor): # xml_type -> matching(emparejar), essay(ensayo), numerical, multichoice, # shortanswer, cloze, truefalse, description(sin pregunta) self.n_questions += 1 numero = etree.Element("div", {"class":"numero"}) numero.text = str(self.n_questions) pregunta = etree.Element("div", {"class":"pregunta"}) p = etree.SubElement(pregunta, "p") etree.SubElement(p, "b").text = (u"%s"%name_quiz) form = etree.SubElement(pregunta, "form", name="q%s"%self.n_questions) # Extrae código entre $$, genera con mimeTeX y lo convierte tipo html exo_img = self.create_img(exo) if type_moodle == "cloze": exo_img = self.html_question_cloze(exo_img) #Interpreta mal los "&", así que los convierte y más adelante los desconvierte exo_img = exo_img.replace('&','&') exo_img_etree = etree.XML(exo_img) form.append(exo_img_etree) pregunta_div = etree.SubElement(pregunta, "div", align="right") etree.SubElement(pregunta_div, "span", id="resp%s"%self.n_questions).text = u" " etree.SubElement(pregunta_div, "input", type="button", value=_(u"Vérifier"), id="b%s"%self.n_questions, onclick="Engine_form(%s)"%self.n_questions) elif type_moodle == "numerical" or type_moodle == "shortanswer": exo_img = exo_img.replace("\n", "
\n") exo_img = self.html_question_numerical_shortanswer(exo_img, cor) #Interpreta mal los "&", así que los convierte y más adelante los desconvierte exo_img = exo_img.replace('&','&') exo_img_etree = etree.XML(exo_img) form.append(exo_img_etree) pregunta_div = etree.SubElement(pregunta, "div", align="right") etree.SubElement(pregunta_div, "span", id="resp%s"%self.n_questions).text = u" " etree.SubElement(pregunta_div, "input", type="button", value=_(u"Vérifier"), id="b%s"%self.n_questions, onclick="Engine_form(%s)"%self.n_questions) elif type_moodle == "multichoice": exo_img = exo_img.replace("\n", "
\n") exo_img = self.html_question_multichoice(exo_img, cor) #Interpreta mal los "&", así que los convierte y más adelante los desconvierte exo_img = exo_img.replace('&','&') exo_img_etree = etree.XML(exo_img) form.append(exo_img_etree) pregunta_div = etree.SubElement(pregunta, "div", align="right") etree.SubElement(pregunta_div, "span", id="resp%s"%self.n_questions).text = u" " etree.SubElement(pregunta_div, "input", type="button", value=_(u"Vérifier"), id="b%s"%self.n_questions, onclick="Engine_checkbox(%s)"%self.n_questions) elif type_moodle == "matching": exo_img = exo_img.replace("\n", "
\n") exo_img = self.html_question_matching(exo_img, cor) #Interpreta mal los "&", así que los convierte y más adelante los desconvierte exo_img = exo_img.replace('&','&') exo_img_etree = etree.XML(exo_img) form.append(exo_img_etree) pregunta_div = etree.SubElement(pregunta, "div", align="right") etree.SubElement(pregunta_div, "span", id="resp%s"%self.n_questions).text = u" " etree.SubElement(pregunta_div, "input", type="button", value=_(u"Vérifier"), id="b%s"%self.n_questions, onclick="Engine_form(%s)"%self.n_questions) elif type_moodle == "truefalse": exo_img = exo_img.replace("\n", "
\n") exo_img = self.html_question_multichoice(exo_img, cor, type_check="radio") #Interpreta mal los "&", así que los convierte y más adelante los desconvierte exo_img = exo_img.replace('&','&') exo_img_etree = etree.XML(exo_img) form.append(exo_img_etree) pregunta_div = etree.SubElement(pregunta, "div", align="right") etree.SubElement(pregunta_div, "span", id="resp%s"%self.n_questions).text = u" " etree.SubElement(pregunta_div, "input", type="button", value=_(u"Vérifier"), id="b%s"%self.n_questions, onclick="Engine_checkbox(%s)"%self.n_questions) #tipo type_moodle == "essay" o type_moodle == "description" no hay ninguno: quiz_txt = etree.tounicode(numero, pretty_print=True) quiz_txt += etree.tounicode(pregunta, pretty_print=True) #Corrige tabulación de salto de línea if self.n_questions == 1: quiz_txt = " " + quiz_txt quiz_txt = quiz_txt.replace("\n", "\n ") #Añade separación en las tablas (diferente si tiene borde) if quiz_txt.find("border") > -1: quiz_txt = quiz_txt.replace('cellpadding=\"0\"', 'cellpadding=\"5\"') else: quiz_txt = quiz_txt.replace('cellpadding=\"0\"', 'cellpadding=\"20\"') #Corrige conversión al añadirse como texto & en un etree (hacerlo 2 veces) quiz_txt = quiz_txt.replace('&', '&').replace('&', '&') #Corrige en "multichoice", pues se añade < y > como texto quiz_txt = quiz_txt.replace(u'<img', '') #Corrige en windows usa "\" y para las web es mejor "/" quiz_txt = quiz_txt.replace(u'img\\', u'img/') self.quizs.append(quiz_txt) def create_img(self, text, subnom = ""): # Crea las flechas de avanzar y retroceder self.compileToFile("\Longrightarrow", "next.gif") self.compileToFile("\Longleftarrow", "prev.gif") # Busca los texto a convertir en imagen to_img = findall("\$\$[^\$]*\$\$", text) for i in to_img: img_nom = str(self.n_questions)+"_"+str(to_img.index(i))+subnom+".gif" self.compileToFile(i[2:len(i)-2], img_nom) #img_txt = etree.Element("img", src=join("img", img_nom)) img_txt = etree.Element("img", align="middle", src=join("img", img_nom)) #img_txt = etree.Element("img", align="top", src=join("img", img_nom)) text = text.replace(i, etree.tounicode(img_txt)) if subnom == "": return u"
"+text+u"
" else: return text def compileToFile (self, text, fname, params = "-s 4"): file_name = join(self.dir_name,fname) #Corrección ya que si el texto comineza con "-", mimetex se cree que pide un comando if text[0] == "-": text = " " + text path = '%s "%s" -e "%s" %s' %(self.mimetex_path, text, file_name, params) pipe = popen(path) pipe.read() pipe.close() def html_question_cloze(self, text): to_text = findall("CAL:=[^\}]*\}|%100%[^\}]*\}|OICE:[^\}]*\}", text) try: resp_long_max = len(max(max(to_text).split('~'))) except ValueError: resp_long_max = len(max(to_text)) if resp_long_max > 30: resp_long = 70 elif resp_long_max > 20: resp_long = 20 elif resp_long_max > 10: resp_long = 10 else: resp_long = 5 exo_input_txt = etree.Element("input", type="text", autocomplete="off", size="%s"%resp_long, onkeypress="tabE(this,event,%s);"%self.n_questions) exo_txt = etree.tounicode(exo_input_txt) #except ValueError: # None #to_text.extend(findall("OICE:[^\}]*\}", text)) respondes_txt = u" ans[%s] = new Array(" % self.n_questions for n in to_text: if n[:5] == "%100%": if n.find("~") == -1: text = text.replace(u"{1:SHORTANSWER:%s"%n, exo_txt) respondes_txt += u"\'%s\'," % n[5:len(n)-1] else: text = text.replace(u"{1:SHORTANSWER:~%s"%n, exo_txt) text_list = n[5:-1].split("~%100%") text_array = u"new Array(" for m in text_list: text_array += u"\'%s\'," % m respondes_txt += text_array[:-1] + u")," elif n[:5] == "CAL:=": text = text.replace(u"{1:NUMERI%s"%n, exo_txt) respondes_txt += u"\'%s\'," % n[5:len(n)-1] elif n[:5] == "OICE:": n_list = n[:-1].split("~") n_list.pop(0) n_list.sort(key=self.mi_cmp) exo_input_multi = etree.Element("select", autocomplete="off") etree.SubElement(exo_input_multi, "option", selected="selected").text = " " for x in n_list: if x[0] == "=": n_sol = n_list.index(x) + 1 etree.SubElement(exo_input_multi, "option").text = x[1:] else: etree.SubElement(exo_input_multi, "option").text = x exo_multi = etree.tounicode(exo_input_multi, pretty_print=True) text = text.replace(u"{1:MULTICH%s"%n, exo_multi) respondes_txt += u"\'%s\'," % n_sol respondes_txt = respondes_txt[:-1]+u");\n" #Corrige las soluciones que acaban en .0 (en lugar de 15.0, será 15) respondes_txt = respondes_txt.replace(".0\'", "\'") self.respondes.append(respondes_txt) #Este input en para corregir el bug de cuando hay un solo "text", que al darle Enter, reinicia página exo_extra = etree.Element("input", type="text", style="display:none;") text = text[:-6] + etree.tounicode(exo_extra) + "" return text def mi_cmp(self,a): if a[0] == "=": a = a[1:] return a def html_question_numerical_shortanswer(self, exo_img, cor): if len(unicode(cor)) > 15: resp_long = 20 elif len(unicode(cor)) > 7: resp_long = 15 else: resp_long = 5 exo_input = etree.Element("input", type="text", autocomplete="off", size="%s"%resp_long, onkeypress="tabE(this,event,%s);"%self.n_questions) exo_txt = etree.tounicode(exo_input) exo_img = exo_img.replace("_____", exo_txt) #Este input en para corregir el bug de cuando hay un solo "text", que al darle Enter, reinicia página exo_extra = etree.Element("input", type="text", style="display:none;") exo_img = exo_img[:-6] + etree.tounicode(exo_extra) + "" if isinstance(cor,list): respondes_txt = u" ans[%s] = new Array(new Array(\'" % self.n_questions #for i in cor: # respondes_txt += u"\'%s\'," % i respondes_txt += "\',\'".join(cor) respondes_txt += u"\'));\n" else: respondes_txt = u" ans[%s] = new Array(\'%s\');\n" % (self.n_questions, cor) #Corrige las soluciones que acaban en .0 (en lugar de 15.0, será 15) respondes_txt = respondes_txt.replace(".0\'", "\'") self.respondes.append(respondes_txt) return exo_img def html_question_multichoice(self, exo_img, cor, type_check="checkbox"): exo_img = exo_img[:-6] + "
\n" respondes_txt = u" ans[%s] = new Array(" % self.n_questions for n in cor: if n[1].find("$$") > -1: n[1] = self.create_img(n[1],"_"+str(cor.index(n))) #type_check puede ser "checkbox" (eleccion multiple) o "radio" (eleccion uno solo) exo_input = etree.Element("input", type=type_check, name="q%s"%self.n_questions, autocomplete="off") exo_input.text = n[1] exo_txt = etree.tounicode(exo_input, pretty_print=True) exo_img += exo_txt if cor.index(n) == 5: exo_img += "
\n" if n[0] == 0: respondes_txt += u"\'0\'," else: respondes_txt += u"\'1\'," respondes_txt = respondes_txt[:-1]+u");\n" self.respondes.append(respondes_txt) exo_img += "" return exo_img def html_question_matching(self, exo_img, cor): list_sol = [] for i in range(len(cor)): list_sol.append(cor[i][1]) list_sol.sort() exo_img = exo_img[:-6] + "

\n" exo_input_match = etree.Element("select", autocomplete="off") etree.SubElement(exo_input_match, "option", selected="selected").text=" " for n in cor: if n[0].find("$$") > -1: n[0] = self.create_img(n[0],"_"+str(cor.index(n))) etree.SubElement(exo_input_match, "option").text = list_sol[cor.index(n)] exo_match = etree.tounicode(exo_input_match, pretty_print=True) respondes_txt = u" ans[%s] = new Array(" % self.n_questions for m in cor: exo_img += m[0] + " " + exo_match + "
\n" for j in range(len(list_sol)): if m[1] == list_sol[j]: respondes_txt += u"\'%s\'," % str(j+1) break; respondes_txt = respondes_txt[:-1]+u");\n" self.respondes.append(respondes_txt) #NO QUITAR, aunque en matching no hay el bug, el "index.html" mira hasta el n-1 exo_extra = etree.Element("input", type="text", style="display:none;") exo_img += etree.tounicode(exo_extra) + "\n" return exo_img def html_fin(self): return self.quizs, self.respondes, self.n_questions