source: pyromaths/trunk/fuentes/src/pyromaths/outils/System.py @ 423

Last change on this file since 423 was 423, checked in by mabarracus, 4 years ago

add sources from pyromaths 15.10

File size: 16.4 KB
Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Pyromaths
5# Un programme en Python qui permet de créer des fiches d'exercices types de
6# mathématiques niveau collège ainsi que leur corrigé en LaTeX.
7# Copyright (C) 2006 -- Jérôme Ortais (jerome.ortais@pyromaths.org)
8#
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22#
23
24import sys, os, codecs
25from lxml import etree
26from lxml import _elementpath as DONTUSE  # Astuce pour inclure lxml dans Py2exe
27from re import findall
28from .TexFiles import mise_en_forme
29from pyromaths.Values import HOME, VERSION, CONFIGDIR
30
31
32#==============================================================
33#        Gestion des extensions de fichiers
34#==============================================================
35def supprime_extension(filename, ext):
36    """supprime l'éventuelle extension ext du nom de fichier filename.
37    ext est de la forme '.tex'"""
38    if os.path.splitext(filename)[1].lower():
39        return os.path.splitext(filename)[0]
40    return filename
41
42def ajoute_extension(filename, ext):
43    """ajoute si nécessaire l'extension ext au nom de fichier filename.
44    ext est de la forme '.tex'"""
45    if os.path.splitext(filename)[1].lower() == ext:
46        return filename
47    return filename + ext
48
49#==============================================================
50#        Gestion du fichier de configuration de Pyromaths
51#==============================================================
52def create_config_file():
53    """Crée le fichier de configuration au format xml"""
54    root = etree.Element("pyromaths")
55
56    child = etree.SubElement(root, "options")
57    etree.SubElement(child, "nom_fichier").text = _("exercices")
58    etree.SubElement(child, "chemin_fichier").text = "%s" % HOME
59    etree.SubElement(child, "titre_fiche").text = _(u"Fiche de révisions")
60    etree.SubElement(child, "corrige").text = "True"
61    etree.SubElement(child, "pdf").text = "True"
62    etree.SubElement(child, "unpdf").text = "False"
63    etree.SubElement(child, "modele").text = "pyromaths.tex"
64
65    child = etree.SubElement(root, "informations")
66    etree.SubElement(child, "version").text = VERSION
67    etree.SubElement(child, "description").text = _(u"Pyromaths est un programme qui permet de générer des fiches d’exercices de mathématiques de collège ainsi que leur corrigé. Il crée des fichiers au format pdf qui peuvent ensuite être imprimés ou lus sur écran.")
68    etree.SubElement(child, "icone").text = "pyromaths.ico"
69
70    subchild = etree.SubElement(child, "auteur")
71    etree.SubElement(subchild, "nom").text = u"Jérôme Ortais"
72    etree.SubElement(subchild, "email").text = u"jerome.ortais@pyromaths.org"
73    etree.SubElement(subchild, "site").text = "http://www.pyromaths.org"
74
75    return etree.tostring(root, pretty_print=True, encoding=unicode)
76
77def indent(elem, level=0):
78    """Indente correctement les fichiers xml.
79    By Filip Salomonsson; published on February 06, 2007.
80    http://infix.se/2007/02/06/gentlemen-indent-your-xml"""
81    i = "\n" + level * "  "
82    if len(elem):
83        if not elem.text or not elem.text.strip():
84            elem.text = i + "  "
85        for e in elem:
86            indent(e, level + 1)
87            if not e.tail or not e.tail.strip():
88                e.tail = i + "  "
89        if not e.tail or not e.tail.strip():
90            e.tail = i
91    else:
92        if level and (not elem.tail or not elem.tail.strip()):
93            elem.tail = i
94    return elem
95
96def modify_config_file(fichier):
97    """Modifie le fichier de configuration si besoin, excepté les options utilisateur déjà configurées"""
98    modifie = False
99    oldtree = etree.parse(fichier)
100    oldroot = oldtree.getroot()
101    newroot = etree.XML(create_config_file())
102    for element in newroot.iter(tag=etree.Element):
103        if not len(element):
104            parents = [element]
105            e = element.getparent()
106            while e is not None:
107                parents.insert(0, e)
108                e = e.getparent()
109            oldtag = oldroot
110            for i in range(1, len(parents)):
111                if oldtag.find(parents[i].tag) is None and i < len(parents) - 1 :
112                    if i > 1:
113                        etree.SubElement(oldroot.find(parents[i - 1].tag), parents[i].tag)
114                    else:
115                        etree.SubElement(oldroot, parents[i].tag)
116                    oldtag = oldtag.find(parents[i].tag)
117                else:
118                    oldtag = oldtag.find(parents[i].tag)
119                if i == len(parents) - 2: oldparent = oldtag
120            if oldtag is None:
121                # Ajoute un nouvel item dans le fichier xml
122                modifie = True
123                etree.SubElement(oldparent, element.tag).text = element.text
124            elif oldtag.text != element.text and parents[1].tag != "options":
125                # Modifie un item existant s'il ne s'agit pas des options
126                modifie = True
127                oldtag.text = element.text
128    if modifie:
129        f = codecs.open(os.path.join(CONFIGDIR, "pyromaths.xml"), encoding='utf-8', mode='w')
130        f.write(etree.tostring(indent(oldroot), pretty_print=True, encoding=unicode))
131        f.close()
132
133#==============================================================
134#        Créer et lance la compilation des fichiers TeX
135#==============================================================
136def _preprocess_pipe(filename, pipe):
137    """Exécute chacune des commandes de `pipe` sur `filename`.
138
139    :param str filename: Nom du fichier LaTeX qui va être compilé.
140    :param list pipe: Liste de commandes à appliquer, sous la forme de chaînes
141        de caractères. Si ces chaînes contiennent `{}`, ceci est remplacé par
142        le nom du fichier ; sinon, il est ajouté à la fin de la commande. Cet
143        élément peut aussi être `None`, auquel cas il correspond à une liste
144        vide.
145    """
146    from subprocess import call
147    if pipe is None:
148        pipe = []
149    for command in pipe:
150        formatted = command.format(filename)
151        if formatted == command:
152            formatted = '{} {}'.format(command, filename)
153        call(formatted, env=os.environ, shell=True)
154
155def creation(parametres):
156    """Création et compilation des fiches d'exercices.
157    parametres = {'fiche_exo': f0,
158                  'fiche_cor': f1,
159                  'liste_exos': self.lesexos,
160                  'creer_pdf': self.checkBox_create_pdf.checkState(),
161                  'creer_unpdf': self.checkBox_unpdf.isChecked() and self.checkBox_unpdf.isEnabled(),
162                  'titre': unicode(self.lineEdit_titre.text()),
163                  'niveau': unicode(self.comboBox_niveau.currentText()),
164                }"""
165    exo = unicode(parametres['fiche_exo'])
166    cor = unicode(parametres['fiche_cor'])
167    f0 = codecs.open(exo, encoding='utf-8', mode='w')
168    f1 = codecs.open(cor, encoding='utf-8', mode='w')
169
170    if parametres['creer_pdf']:
171        copie_tronq_modele(f0, parametres, 'entete')
172        if not parametres['creer_unpdf']:
173            copie_tronq_modele(f1, parametres, 'entete')
174
175    for exercice in parametres['liste_exos']:
176        # write exercise's TeX code (question & answer) to files
177        f0.write("\n")
178        f0.writelines(line + "\n" for line in exercice.tex_statement())
179        f1.write("\n")
180        f1.writelines(line + "\n" for line in exercice.tex_answer())
181
182
183    if parametres['creer_pdf']:
184        if parametres['creer_unpdf']:
185            f0.write("\\label{LastPage}\n")
186            f0.write("\\newpage\n")
187            f0.write(_(u"\\currentpdfbookmark{Le corrigé des exercices}{Corrigé}\n"))
188            f0.write("\\lhead{\\textsl{{\\footnotesize Page \\thepage/ \\pageref{LastCorPage}}}}\n")
189            f0.write("\\setcounter{page}{1} ")
190            f0.write("\\setcounter{exo}{0}\n")
191            f1.write("\\label{LastCorPage}\n")
192            copie_tronq_modele(f1, parametres, 'pied')
193        else:
194            f0.write("\\label{LastPage}\n")
195            f1.write("\\label{LastPage}\n")
196            copie_tronq_modele(f0, parametres, 'pied')
197            copie_tronq_modele(f1, parametres, 'pied')
198
199    f0.close()
200    f1.close()
201
202    if parametres['creer_unpdf']:
203        f0 = codecs.open(exo, encoding='utf-8', mode='a')
204        f1 = codecs.open(cor, encoding='utf-8', mode='r')
205        for line in f1:
206            f0.write(line)
207        f0.close()
208        f1.close()
209
210    # indentation des fichiers teX créés
211    mise_en_forme(exo)
212    if parametres['corrige'] and not parametres['creer_unpdf']:
213        mise_en_forme(cor)
214
215    # Dossiers et fichiers d'enregistrement, définitions qui doivent rester avant le if suivant.
216    dir0 = os.path.dirname(exo)
217    dir1 = os.path.dirname(cor)
218    import socket
219    if socket.gethostname() == "sd-27355.pyromaths.org":
220        # Chemin complet pour Pyromaths en ligne car pas d'accents
221        f0noext = os.path.splitext(exo)[0].encode(sys.getfilesystemencoding())
222        f1noext = os.path.splitext(cor)[0].encode(sys.getfilesystemencoding())
223    else:
224        # Pas le chemin pour les autres, au cas où il y aurait un accent dans
225        # le chemin (latex ne gère pas le 8 bits)
226        f0noext = os.path.splitext(os.path.basename(exo))[0].encode(sys.getfilesystemencoding())
227        f1noext = os.path.splitext(os.path.basename(cor))[0].encode(sys.getfilesystemencoding())
228    if parametres['creer_pdf']:
229        from subprocess import call
230
231        _preprocess_pipe(os.path.join(dir0, '{}.tex'.format(f0noext)), parametres.get('pipe', None))
232        os.chdir(dir0)
233        latexmkrc(f0noext)
234        log = open('%s-pyromaths.log' % f0noext, 'w')
235        if socket.gethostname() == "sd-27355.pyromaths.org":
236            os.environ['PATH'] += os.pathsep + "/usr/local/texlive/2014/bin/x86_64-linux"
237            call(["latexmk", "-shell-escape", "-silent", "-interaction=nonstopmode", "-output-directory=%s" % dir0, "-pdfps", "%s.tex" % f0noext], env=os.environ, stdout=log)
238            call(["latexmk", "-c", "-silent", "-output-directory=%s" % dir0], env=os.environ, stdout=log)
239        elif os.name == 'nt':
240            call(["latexmk", "-pdfps", "-shell-escape", "-silent", "-interaction=nonstopmode", "%s.tex" % f0noext], env={"PATH": os.environ['PATH'], "WINDIR": os.environ['WINDIR'], 'USERPROFILE': os.environ['USERPROFILE']}, stdout=log)
241            call(["latexmk", "-silent", "-c"], env={"PATH": os.environ['PATH'], "WINDIR": os.environ['WINDIR'], 'USERPROFILE': os.environ['USERPROFILE']}, stdout=log)
242        else:
243            call(["latexmk", "-pdfps", "-shell-escape", "-silent", "-interaction=nonstopmode", "%s.tex" % f0noext], stdout=log)
244            call(["latexmk", "-silent", "-c", "-f"], stdout=log)
245        log.close()
246        nettoyage(f0noext)
247        if not "openpdf" in parametres or parametres["openpdf"]:
248            if os.name == "nt":  # Cas de Windows.
249                os.startfile('%s.pdf' % f0noext)
250            elif sys.platform == "darwin":  # Cas de Mac OS X.
251                os.system('open %s.pdf' % f0noext)
252            else:
253                os.system('xdg-open %s.pdf' % f0noext)
254
255        if parametres['corrige'] and not parametres['creer_unpdf']:
256            os.chdir(dir1)
257            latexmkrc(f1noext)
258            log = open('%s-pyromaths.log' % f1noext, 'w')
259            if socket.gethostname() == "sd-27355.pyromaths.org":
260                os.environ['PATH'] += os.pathsep + "/usr/local/texlive/2014/bin/x86_64-linux"
261                call(["latexmk", "-shell-escape", "-silent", "-interaction=nonstopmode", "-output-directory=%s" % dir1, "-pdfps", "%s.tex" % f1noext], env=os.environ, stdout=log)
262                call(["latexmk", "-c", "-silent", "-output-directory=%s" % dir1], env=os.environ, stdout=log)
263            elif os.name == 'nt':
264                call(["latexmk", "%s.tex" % f1noext], env={"PATH": os.environ['PATH'], "WINDIR": os.environ['WINDIR'], 'USERPROFILE': os.environ['USERPROFILE']}, stdout=log)
265                call(["latexmk", "-c"], env={"PATH": os.environ['PATH'], "WINDIR": os.environ['WINDIR'], 'USERPROFILE': os.environ['USERPROFILE']}, stdout=log)
266            else:
267                call(["latexmk", "%s.tex" % f1noext], stdout=log)
268                call(["latexmk", "-c"], stdout=log)
269            log.close()
270            nettoyage(f1noext)
271            if not "openpdf" in parametres or parametres["openpdf"]:
272                if os.name == "nt":  # Cas de Windows.
273                    os.startfile('%s.pdf' % f1noext)
274                elif sys.platform == "darwin":  # Cas de Mac OS X.
275                    os.system('open %s.pdf' % f1noext)
276                else:
277                    os.system('xdg-open %s.pdf' % f1noext)
278        else:
279            os.remove('%s-corrige.tex' % f0noext)
280
281def latexmkrc(basefilename):
282    latexmkrc = open('latexmkrc', 'w')
283    latexmkrc.write('$pdf_mode = 2;\n')
284    latexmkrc.write('$ps2pdf = "ps2pdf %O %S %D";\n')
285    latexmkrc.write('$latex = "latex --shell-escape -silent -interaction=nonstopmode  %O %S";\n')
286    latexmkrc.write('sub asy {return system("asy \'$_[0]\'");}\n')
287    latexmkrc.write('add_cus_dep("asy","eps",0,"asy");\n')
288    latexmkrc.write('add_cus_dep("asy","pdf",0,"asy");\n')
289    latexmkrc.write('add_cus_dep("asy","tex",0,"asy");\n')
290    latexmkrc.write('push @generated_exts, \'pre\', \'dvi\', \'ps\', \'auxlock\', \'fdb_latexmk\', \'fls\', \'out\', \'aux\';\n')
291    latexmkrc.write('$clean_ext .= " %R-?.tex %R-??.tex %R-figure*.dpth %R-figure*.dvi %R-figure*.eps %R-figure*.log %R-figure*.md5 %R-figure*.pre %R-figure*.ps %R-figure*.asy %R-*.asy %R-*_0.eps %R-*.pre";')
292    latexmkrc.close()
293
294def nettoyage(basefilename):
295    """Supprime les fichiers temporaires créés par LaTeX"""
296    #try:
297    #    os.remove('latexmkrc')
298    #except OSError:
299    #        pass
300    if os.path.getsize('%s.pdf' % basefilename) > 1000 :
301        for ext in ('.log', '-pyromaths.log'):
302            try:
303                os.remove(basefilename + ext)
304            except OSError:
305                pass
306
307def copie_tronq_modele(dest, parametres, master):
308    """Copie des morceaux des modèles, suivant le schéma du master."""
309    master_fin = '% fin ' + master
310    master = '% ' + master
311    n = 0
312
313    # # Le fichier source doit être un modèle, donc il se trouve dans le dossier 'modeles' de pyromaths.
314    source = parametres['modele']
315
316    if os.path.isfile(os.path.join(parametres['datadir'], 'templates', source)):
317        source = os.path.join(parametres['datadir'], 'templates', source)
318    elif os.path.isfile(os.path.join(parametres['configdir'], 'templates', source)):
319        source = os.path.join(parametres['configdir'], 'templates', source)
320    else:
321        # TODO: Message d'erreur, le modèle demandé n'existe pas
322        print(u"Template file not found in %s" %
323                os.path.join(parametres['datadir'], 'templates'))
324
325    # # Les variables à remplacer :
326    titre = parametres['titre']
327    niveau = parametres['niveau']
328    if parametres['creer_unpdf']:
329        bookmark = u"\\currentpdfbookmark{Les énoncés des exercices}{Énoncés}"
330    else:
331        bookmark = ""
332    #===========================================================================
333    # if os.name == 'nt':
334    #     os.environ['TEXINPUTS'] = os.path.normpath(os.path.join(parametres['datadir'],
335    #         'packages'))
336    #     tabvar = 'tabvar.tex'
337    # else:
338    #     tabvar = os.path.normpath(os.path.join(parametres['datadir'],
339    #         'packages', 'tabvar.tex'))
340    #===========================================================================
341    # rawstring pour \tabvar -> tab + abvarsous windows
342    modele = codecs.open(source, encoding='utf-8', mode='r')
343    for line in modele:
344        if master_fin in line:
345            break
346        if n > 0:
347            temp = findall('##{{[A-Z]*}}##', line)
348            if temp:
349                occ = temp[0][4:len(temp) - 5].lower()
350                # line = sub('##{{[A-Z]*}}##',eval(occ),line)
351                line = line.replace(temp[0], eval(occ))
352            dest.write(line)
353
354        if master in line:
355            n = 1
356
357    modele.close()
358    return
Note: See TracBrowser for help on using the repository browser.