Documentation de Pyromaths 21.8.2

pyromaths.outils.jinja2utils

Contenu

Code source de pyromaths.outils.jinja2utils

# 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) 2018-2021 -- Louis Paternault (spalax@gresille.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

"""Outils à facilitant l'écriture d'exercices avec Jinja2
"""

import fractions
import math
import numbers

import jinja2


[docs]class LatexEnvironment(jinja2.Environment): """Environnement (au sens jinja2) utilisé pour les templates des exercices. :meta private: """ # pylint: disable=too-many-instance-attributes, too-few-public-methods def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.block_start_string = "(*" self.block_end_string = "*)" self.variable_start_string = "((" self.variable_end_string = "))" self.comment_start_string = "(% " self.comment_end_string = " %)" self.trim_blocks = True self.lstrip_blocks = True
[docs]def facteur( nombre, court="", parentheses=False, zero=False, arrondi=None, signe=False, # pylint: disable=redefined-outer-name operation=False, produit=False, variable="", ): r"""Renvoit le code :math:`LaTeX` correspondant au nombre, dans un ``\\numprint{}``, en gérant de nombreux cas particuliers. Les cas d'utilisation de cette fonction sont détaillés dans le :ref:`tutoriel pour écrire un nouvel exercice <ecrire-facteur>`. En utilisant les arguments, cette fonction permet de prendre en compte les arrondis, et de nombreux cas particuliers (dans chaque cas, c'est le 3 qui est destiné à être rendu par cette fonction. - ajout de parenthèses uniquement si nécessaire (produit `2×3` et `2×(-3)` plutôt que `2×-3` ou `2×(3)`) ; - ajout du signe, même s'il est positif (produit `1+3x` et `1-3x` plutôt que `1 3x` ou `1+-3x`) ; - n'affiche pas le nombre 1, et seulement le signe de -1 (produit `x` et `-x` plutôt que `1x` et `-1x`) ; - n'affiche pas l'opérande nulle d'un produit (produit `2` plutôt que `2+0x`) ; - etc. Le séparateur décimal est le point, mais celui-ci sera converti en une virgule par ``\\numprint{}``. :param numbers.Real nombre: Nombre à formatter (il peut aussi être de type :class:`decimal.Decimal`). :param str court: Version courte des arguments (voir plus loin). :param boolean parentheses: Encadre le nombre avec des parenthèses, s'il est négatif (utile pour produire `2×(-3)` plutôt que `2×3`). :param int arrondi: Arrondi le nombre. L'argument est le nombre de chiffres après la virgule à utiliser. Mettre cet argument à ``None`` pour ne pas arrondir. :param boolean zero: Ajoute des zéros à la fin du nombre pour avoir autant de chiffres que défini par `arrondi` (par exemple, avec cette option et un arrondi au centième, `2` devient `2.00`). :param boolean signe: Produit le signe, même si le nombre est positif (produit alors un +). :param boolean operation: Indique que le signe plus ou moins est une opération (comme `2-3`) et non pas un opérateur unaire (comme `2×(-3)`). Cela a pour effet d'espacer un peu le signe du nombre. :param boolean produit: Le facteur est une opérande d'un produit : le nombre 1 ne doit pas être affiché ; du nombre -1, on ne conserve que le signe ; rien n'est affiché si le nombre est 0 (pour produire `x`, `-x` ou rien plutôt que `1x`, `-1x`, `0x`). :param str variable: Seconde opérande du produit. Doit être du code :math:`LaTeX` valide. :return: Le nombre, formatté comme du code :math:`LaTeX`. :rtype: str :Dépendances entre arguments: - Le comportement n'est pas défini si les arguments ``operation`` et ``parentheses`` sont utilisées en même temps. :Version courte des arguments: Pour que les arguments soient moins verbeux, il est possible d'utiliser la version courte des arguments. Par exemple, ``facteur(2, produit=True, parentheses=True, arrondi=3)`` peut s'écrire comme ``facteur(2, '*p3')``. Cet argument est une chaîne de caractères, chaque caractère « activant » une option longue. Par exemple ``*p3`` active les options ``produit``, ``parentheses`` et ``arrondi=3``. L'ordre des caractères n'a aucune importance. Plusieurs comportement sont non-définis : - présence de caractères ne correspondant à aucun argument ; - utilisation conjointe des arguments courts et longs ; - utilisation de plusieurs chiffres (pour l'argument ``arrondi``). Les arguments sont : - ``p``: ``parentheses`` ; - ``z``: ``zero`` ; - ``s``: ``signe`` ; - ``o``: ``operation`` ; - ``*``: ``produit`` ; - chiffres ``0`` à ``9``: ``arrondi`` à l'entier correspondant ; - ``x``, ``X``, ``y``, ``Y``, ``n``, ``N`` : Raccourcis pour (respectivement) ``variable="x"``, ``variable="x^2"``, ``variable="y"``, ``variable="y^2"``, ``variable="n"``, ``variable="n^2"``. :Exemples: Cas de base >>> from pyromaths.outils.jinja2utils import facteur >>> facteur(2) \numprint{2} >>> facteur(2.0) \numprint{2} >>> facteur(2.3) \numprint{2.3} >>> facteur(-122.0) \numprint{-122} Arrondi >>> facteur(12345.6789, arrondi=None) \numprint{12345.6789} >>> facteur(12345.6789, arrondi=0) \numprint{12346} >>> facteur(12345.6789, arrondi=2) \numprint{12345.68} >>> facteur(.6789, arrondi=0) \numprint{1} >>> facteur(.6789, arrondi=2) \numprint{0.68} Zéro >>> facteur(12345.6789, arrondi=None, zero=True) \numprint{12345.6789} >>> facteur(12345, arrondi=2, zero=True) \numprint{12345.00} >>> facteur(12345, arrondi=2, zero=False) \numprint{12345} >>> facteur(12345.7, arrondi=2, zero=True) \numprint{12345.70} >>> facteur(12345.7, arrondi=2, zero=False) \numprint{12345.7} Parenthèse >>> facteur(-2, parentheses=True) \left(\numprint{-2}\right) >>> facteur(2, parentheses=True) \numprint{2} Signe >>> facteur(-2, signe=True) \numprint{-2} >>> facteur(2, signe=True) \numprint{+2} >>> facteur(2, signe=False) \numprint{2} Opération >>> facteur(-2, operation=False) \numprint{-2} >>> facteur(2, signe=True, operation=False) \numprint{+2} >>> facteur(-2, operation=True) -\numprint{2} >>> facteur(2, signe=True, operation=True) +\numprint{2} Produit >>> facteur(1, produit=True, variable="x") x >>> facteur(-1, produit=True, variable="x") -x >>> facteur(0, produit=True, variable="x") <BLANKLINE> >>> facteur(1, produit=False, variable="x") \numprint{1}\,x Variable >>> facteur(2, variable="x") \numprint{2}\,x >>> facteur(-1, produit=True, variable="x") -x Version courte des arguments >>> facteur(-2, court="2zXo") -\numprint{2.00}\,x^2 >>> facteur(-2, court="2zXp") \left(\numprint{-2.00}\,x^2\right) >>> facteur(-3, court="2zY") \numprint{-3.00}\,y^2 >>> facteur(-1, court="y*") -y >>> facteur(1, court="p*x") x >>> facteur(-1, court="p*x") \left(-x\right) >>> facteur(-2, court="p*x") \left(\numprint{-2}\,x\right) >>> facteur(2, court="p*x") \numprint{2}\,x >>> facteur(1, court="s*x") +x Fractions >>> from fractions import Fraction >>> facteur(Fraction(-2, 5), court="p") \left(-\frac{\numprint{2}}{\numprint{5}}\right) >>> facteur(Fraction(7, 1)) \numprint{7} >>> facteur(Fraction(3, 2)) \frac{\numprint{3}}{\numprint{2}} """ # pylint: disable=too-many-arguments, too-many-return-statements, too-many-branches, too-many-statements # Format court des arguments if "p" in court: parentheses = True if "z" in court: zero = True if "s" in court: signe = True if "o" in court: operation = True if "*" in court: produit = True if "x" in court: variable = "x" if "y" in court: variable = "y" if "n" in court: variable = "n" if "X" in court: variable = "x^2" if "Y" in court: variable = "y^2" if "N" in court: variable = "n^2" for entier in range(10): if str(entier) in court: arrondi = entier # Cas particuliers : produit if produit: if nombre == 1: if operation or signe: return "+{}".format(variable) return variable if nombre == -1: if operation: return "-{}".format(variable) if parentheses: return r"\left(-{}\right)".format(variable) return "-{}".format(variable) if nombre == 0: return "" # Division du nombre en signe et valeur absolue absolu = abs(nombre) if signe and nombre >= 0: plusoumoins = "+" elif nombre < 0: plusoumoins = "-" else: plusoumoins = "" # Conversion du nombre en prenant compte de l'arrondi et des zéros # Je n'ai pas réussi à faire ça uniquement à coup de # str.format(). Je serai ravi si quelqu'un me prouvait que # c'est possible ! numprint = True if arrondi is None: if ( isinstance(absolu, fractions.Fraction) and absolu.denominator != 1 and absolu.numerator != 0 ): numprint = False strabsolu = f"{plusoumoins}{tex(absolu)}" elif round(absolu) == absolu: strabsolu = str(int(absolu)) else: strabsolu = str(absolu) elif arrondi == 0: strabsolu = f"{round(absolu):g}" else: entier = math.trunc(absolu) decimal = str(round(absolu - entier, arrondi))[2:] if zero: strabsolu = f"{entier}.{decimal:0<{arrondi}}" elif entier == absolu: strabsolu = f"{entier}" else: strabsolu = f"{entier}.{decimal}" # Ajout éventuel d'une espace devant la variable if variable: variable = r"\," + variable # Ajout éventuel des parenthèses # pylint: disable=no-else-return if parentheses and nombre < 0: if numprint: return fr"\left(\numprint{{{plusoumoins}{strabsolu}}}{variable}\right)" else: return fr"\left({strabsolu}{variable}\right)" elif numprint: if operation: return fr"{plusoumoins}\numprint{{{strabsolu}}}{variable}" else: return fr"\numprint{{{plusoumoins}{strabsolu}}}{variable}" else: return fr"{strabsolu}{variable}"
[docs]def iter_facteurs(liste, short): """Applique :func:`facteur` à une liste de nombres. - Les éléments peuvent être des nombres, ou pas (auquel cas la fonction :func:`str` est appliquée). - L'argument ``short`` est transmis à :func:`facteur`. """ for element in liste: if isinstance(element, numbers.Number): yield facteur(element, short) else: yield str(element)
[docs]def matrice(listes, short=""): r"""Renvoit le code LaTeX d'une matrice. - La matrice est sous la forme d'une liste de lignes (où chaque ligne est une liste de coefficients). - L'argument ``short`` est transmis à :func:`facteur`. >>> from pyromaths.outils.jinja2utils import matrice >>> matrice([[1, 2], [3, 4]]) \begin{pmatrix}\numprint{1} & \numprint{2}\\\numprint{3} & \numprint{4}\\\end{pmatrix} """ text = "" text += r"\begin{pmatrix}" for ligne in listes: text += " & ".join(iter_facteurs(ligne, short)) text += r"\\" text += r"\end{pmatrix}" return text
[docs]def signe(nombre): """Renvoit le signe du nombre (c'est-à-dire + ou -).""" if nombre >= 0: return "+" return "-"
[docs]def tex(objet): """Convertit un objet mathématique en code LaTeX. * Soit l'objet a une méthode :meth:`tex`, auquel cas `objet.tex()` est renvoyé ; * sinon, si l'objet est de type :class:`numbers.Real`, une représentation adaptée est renvoyée (même si, pour plus de finesse, il pourra être utile d'utiliser :func:`facteur`) ; * sinon, si l'objet est une chaîne de caractères, elle est renvoyée ; * sinon, une exception est levée. """ # pylint: disable=no-else-return if hasattr(objet, "tex") and callable(getattr(objet, "tex")): return getattr(objet, "tex")() if isinstance(objet, str): return objet if isinstance(objet, fractions.Fraction): if objet < 0: return fr"-\frac{{\numprint{{{-int(objet.numerator)}}}}}{{\numprint{{{objet.denominator}}}}}" else: return fr"\frac{{\numprint{{{objet.numerator}}}}}{{\numprint{{{objet.denominator}}}}}" if isinstance(objet, numbers.Real): return fr"\numprint{{{objet}}}" raise TypeError(f"L'objet {objet} n'a pas de méthode 'tex()'.")

Contenu