Compare commits

...

13 Commits

20 changed files with 552 additions and 641 deletions

14
.drone.yml Normal file
View File

@ -0,0 +1,14 @@
kind: pipeline
name: default
steps:
- name: Publish
image: plugins/pypi
settings:
username:
from_secret: pypi_username
password:
from_secret: pypi_password
trigger:
event:
- tag

View File

@ -2,7 +2,8 @@
# encoding: utf-8
__version__ = "0.1"
#from .bopytex import subject_metadatas, crazy_feed, pdfjoin
# -----------------------------
# Reglages pour 'vim'

View File

@ -1,13 +0,0 @@
#!/usr/bin/env python
# encoding: utf-8
from .bopytex import main
main()
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -5,12 +5,11 @@
Producing then compiling templates
"""
import csv
import os
import logging
import optparse
import sys
from path import Path
from pathlib import Path
import pytex
from mapytex import Expression, Integer, Decimal
import bopytex.filters as filters
@ -46,18 +45,18 @@ def setup():
def get_working_dir(options):
""" Get the working directory """
if options.working_dir:
working_dir = Path(options.working_dir)
if options["working_dir"]:
working_dir = Path(options["working_dir"])
else:
try:
template = Path(options.template)
template = Path(options["template"])
except TypeError:
raise ValueError(
"Need to set the working directory \
or to give a template"
)
else:
working_dir = template.dirname()
working_dir = template.parent
logger.debug(f"The output directory will be {working_dir}")
return working_dir
@ -100,146 +99,138 @@ def pdfjoin(pdf_files, destname, working_dir=".", rm_pdfs=1):
os.system(f"rm {pdf_files_str}")
def extract_student_csv(csv_filename):
""" Extract student list from csv_filename """
with open(csv_filename, "r") as csvfile:
reader = csv.DictReader(csvfile)
return [r for r in reader]
def subject_metadatas(options):
""" Return metadata on subject to produce
if csv is given it will based on is
otherwise it will be based on quantity
:example:
>>> subject_metadata(10)
"""
if options["students_csv"]:
metadatas = []
for (i, s) in enumerate(extract_student_csv(options["students_csv"])):
d = {"num": f"{i+1:02d}"}
d.update(s)
metadatas.append(d)
elif options["number_subjects"] > 0:
metadatas = [{"num": f"{i+1:02d}"} for i in range(options["number_subjects"])]
else:
raise ValueError("Need metacsv or quantity to build subject metadata")
for meta in metadatas:
meta.update(
{
"template": str(Path(options["template"]).name),
"texfile": str(Path(options["template"]).name).replace(
"tpl", meta["num"]
),
"directory": str(Path(options["template"]).parent),
}
)
return metadatas
def feed(*args, **kwrds):
""" Nice and smooth pytex feed """
pytex.feed(*args, **kwrds)
def crazy_feed(*args, **kwrds):
""" Crazy mod for pytex feed """
while True:
try:
pytex.feed(*args, **kwrds)
except:
logger.debug(f"Crazy feed is working hard...! {args} {kwrds}")
else:
break
def clean(directory):
pytex.clean(directory)
def texcompile(filename):
logger.debug(f"Start compiling {filename}")
pytex.pdflatex(Path(filename))
logger.debug(f"End compiling")
def produce_and_compile(options):
""" Produce and compile subjects
"""
working_dir = get_working_dir(options)
if options.only_corr:
options.corr = True
tex_files = working_dir.files("[0-9]*_*.tex")
else:
template = Path(options.template).name
logger.debug(f"Template will be {template}")
list_infos = [{"num": f"{i+1:02d}"} for i in range(options.num_subj)]
tex_files = []
for infos in list_infos:
dest = working_dir / Path(template.replace("tpl", infos["num"]))
logger.debug(f"Feeding template toward {dest}")
tex_files.append(dest)
pytex.feed(working_dir / template, {"infos": infos}, output=dest, force=1)
logger.debug(f"{dest} fed")
if not options.no_compil:
pdf_files = []
for texfile in tex_files:
logger.debug(f"Start compiling {texfile}")
pytex.pdflatex(texfile)
logger.debug(f"End compiling {texfile}")
pdf_files.append(str(texfile[:-4] + ".pdf"))
logger.debug(f"Compiled files : {pdf_files}")
if not options.no_join and not options.no_compil:
pdfjoin(
pdf_files,
template.replace("tpl", "all").replace(".tex", ".pdf"),
working_dir,
rm_pdfs=1,
)
if options.corr:
pdf_files = []
for texfile in tex_files:
corr_fname = activate_printanswers(texfile)
if not options.no_compil:
logger.debug(f"Start compiling {texfile}")
pytex.pdflatex(corr_fname)
logger.debug(f"End compiling {texfile}")
pdf_files.append(str(corr_fname[:-4] + ".pdf"))
deactivate_printanswers(corr_fname)
if not options.no_join and not options.no_compil:
pdfjoin(
pdf_files,
template.replace("tpl", "corr").replace(".tex", ".pdf"),
working_dir,
rm_pdfs=1,
)
if not options.dirty:
pytex.clean(working_dir)
def main():
setup()
parser = optparse.OptionParser()
parser.add_option(
"-t",
"--template",
action="store",
type="string",
dest="template",
help="File with the template. The name should have the following form tpl_... .",
)
parser.add_option(
"-w",
"--working-dir",
action="store",
type="string",
dest="working_dir",
help="Where fed templates and compiled files will be placed",
)
parser.add_option(
"-N",
"--number_subjects",
action="store",
type="int",
dest="num_subj",
default=1,
help="The number of subjects to make",
)
parser.add_option(
"-d",
"--dirty",
action="store_true",
dest="dirty",
help="Do not clean after compilation",
)
parser.add_option(
"-n",
"--no-compile",
action="store_true",
dest="no_compil",
help="Do not compile source code",
)
parser.add_option(
"-j",
"--no-join",
action="store_true",
dest="no_join",
help="Do not join pdf and clean single pdf",
)
parser.add_option(
"-O",
"--only-corr",
action="store_true",
dest="only_corr",
help="Create and compile only correction from existing subjects",
)
parser.add_option(
"-c",
"--corr",
action="store_true",
dest="corr",
help="Create and compile correction while making subjects",
)
(options, _) = parser.parse_args()
logger.debug(f"CI parser gets {options}")
if not options.template:
print("I need a template!")
sys.exit(0)
template = Path(options["template"]).name
directory = Path(options["template"]).parent
metadatas = subject_metadatas(options)
logger.debug(f"Metadata {metadatas}")
produce_and_compile(options)
for meta in metadatas:
logger.debug(f"Feeding template toward {meta['texfile']}")
if options["crazy"]:
crazy_feed(
template=Path(meta["directory"]) / meta["template"],
data=meta,
output=meta["texfile"],
force=1,
)
else:
feed(
template=Path(meta["directory"]) / meta["template"],
data=meta,
output=meta["texfile"],
force=1,
)
assert(Path(meta["texfile"]).exists())
logger.debug(f"{meta['texfile']} fed")
if options["corr"]:
logger.debug(f"Building correction for {meta['texfile']}")
meta.update({
"corr_texfile": activate_printanswers(meta["texfile"]),
})
if __name__ == "__main__":
main()
if not options["no_compile"]:
for prefix in ["", "corr_"]:
key = prefix + "texfile"
try:
meta[key]
except KeyError:
pass
else:
texcompile(meta[key])
meta.update({
prefix+'pdffile': meta[key].replace('tex', 'pdf')
})
if not options["no_join"]:
for prefix in ["", "corr_"]:
key = prefix + "pdffile"
try:
pdfs = [m[key] for m in metadatas]
except KeyError:
pass
else:
pdfjoin(
pdfs,
template.replace("tpl", prefix+"all").replace(".tex", ".pdf"),
directory,
rm_pdfs=1,
)
if not options["dirty"]:
clean(directory)
# -----------------------------

163
bopytex/script.py Normal file
View File

@ -0,0 +1,163 @@
#!/usr/bin/env python
# encoding: utf-8
import click
import logging
from pathlib import Path
from .bopytex import subject_metadatas, crazy_feed, pdfjoin, feed, clean, texcompile, setup, activate_printanswers
formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s")
steam_handler = logging.StreamHandler()
steam_handler.setLevel(logging.DEBUG)
steam_handler.setFormatter(formatter)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.addHandler(steam_handler)
@click.command()
@click.argument(
"template",
type=click.Path(exists=True),
nargs=1,
# help="File with the template. The name should have the following form tpl_... .",
)
@click.option(
"-w",
"--working-dir",
type=click.Path(exists=True),
help="Where fed templates and compiled files will be placed",
)
@click.option(
"-s",
"--students-csv",
type=str,
default="",
help="CSV containing list of students names",
)
@click.option(
"-d", "--dirty", is_flag=True, default=False, help="Do not clean after compilation",
)
@click.option(
"-n",
"--no-compile",
is_flag=True,
default=False,
help="Do not compile source code",
)
@click.option(
"-N",
"--number_subjects",
type=int,
default=1,
help="The number of subjects to make",
)
@click.option(
"-j",
"--no-join",
is_flag=True,
default=False,
help="Do not join pdfs to a single pdf and remove individuals",
)
@click.option(
"-O",
"--only-corr",
is_flag=True,
default=False,
help="Create and compile only correction from existing subjects",
)
@click.option(
"-c",
"--corr",
is_flag=True,
default=False,
help="Create and compile correction while making subjects",
)
@click.option(
"-C",
"--crazy",
is_flag=True,
default=False,
help="Crazy mode. Tries and tries again until template feeding success!",
)
def new(**options):
""" Bopytex
Feed the template (tpl_...) and then compile it with latex.
"""
setup()
logger.debug(f"CI parser gets {options}")
template = Path(options["template"]).name
directory = Path(options["template"]).parent
metadatas = subject_metadatas(options)
logger.debug(f"Metadata {metadatas}")
for meta in metadatas:
logger.debug(f"Feeding template toward {meta['texfile']}")
if options["crazy"]:
crazy_feed(
template=Path(meta["directory"]) / meta["template"],
data=meta,
output=meta["texfile"],
force=1,
)
else:
feed(
template=Path(meta["directory"]) / meta["template"],
data=meta,
output=meta["texfile"],
force=1,
)
assert(Path(meta["texfile"]).exists())
logger.debug(f"{meta['texfile']} fed")
if options["corr"]:
logger.debug(f"Building correction for {meta['texfile']}")
meta.update({
"corr_texfile": activate_printanswers(meta["texfile"]),
})
if not options["no_compile"]:
for prefix in ["", "corr_"]:
key = prefix + "texfile"
try:
meta[key]
except KeyError:
pass
else:
texcompile(meta[key])
meta.update({
prefix+'pdffile': meta[key].replace('tex', 'pdf')
})
if not options["no_join"]:
for prefix in ["", "corr_"]:
key = prefix + "pdffile"
try:
pdfs = [m[key] for m in metadatas]
except KeyError:
pass
else:
pdfjoin(
pdfs,
template.replace("tpl", prefix+"all").replace(".tex", ".pdf"),
directory,
rm_pdfs=1,
)
if not options["dirty"]:
clean(directory)
# produce_and_compile(options)
if __name__ == "__main__":
new()
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del

View File

@ -1,18 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\begin{document}
\section{Ajouts de fractions}
Adding two fractions
\[
A = \frac{- 2}{4} + \frac{7}{8}
\]
Solution
\[
\frac{- 2}{4} + \frac{7}{8}=\frac{- 2 \times 2}{4 \times 2} + \frac{7}{8}=\frac{- 4}{8} + \frac{7}{8}=\frac{- 4 + 7}{8}=\frac{3}{8}
\]
\end{document}

View File

@ -1,18 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\begin{document}
\section{Ajouts de fractions}
Adding two fractions
\[
A = \frac{8}{9} + \frac{3}{63}
\]
Solution
\[
\frac{8}{9} + \frac{3}{63}=\frac{8 \times 7}{9 \times 7} + \frac{3}{63}=\frac{56}{63} + \frac{3}{63}=\frac{56 + 3}{63}=\frac{59}{63}
\]
\end{document}

Binary file not shown.

View File

@ -11,7 +11,6 @@ location==0.0.7
MarkupSafe==1.0
mpmath==0.19
packaging==16.8
path.py==10.1
pexpect==4.2.1
pickleshare==0.7.4
prompt-toolkit==1.0.14

View File

@ -5,7 +5,7 @@ from setuptools import setup
setup(
name='Bopytex',
version='0.1.1',
version='0.1.2',
description='Command line tool for compiling latex with python command embedded',
author='Benjamin Bertrand',
author_email='programming@opytex.org',
@ -13,9 +13,10 @@ setup(
install_requires=[
'mapytex',
'mypytex',
'click',
],
entry_points={
"console_scripts": ['bopytex= bopytex.bopytex:main']
"console_scripts": ['bopytex=bopytex.script:new']
},
)

View File

@ -1,133 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[francais]{babel}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\title{
Snippets pour Opytex \\
Fractions
}
\author{
Benjamin Bertrand
}
\begin{document}
\maketitle
\section{Simplifications de fractions}
\begin{itemize}
\item Trouver le numérateur quand le dénominateur augmente
%
\begin{align*}
\dfrac{2}{6} = \dfrac{\ldots}{48}
\end{align*}
Solution
\begin{align*}
\dfrac{2}{6} = \dfrac{16}{48}
\end{align*}
\item Trouver le numérateur quand le dénominateur diminue
%
\begin{align*}
\dfrac{12}{9} = \dfrac{\cdots}{3}
\end{align*}
Solution
\begin{align*}
\dfrac{12}{9} = \dfrac{4}{3}
\end{align*}
Explications
\begin{align*}
\frac{ 12 }{ 9 }=\frac{ 4 \times 3 }{ 3 \times 3 }=\frac{ 4 }{ 3 }
\end{align*}
\end{itemize}
\section{Ajouts de fractions}
\begin{itemize}
\item Fraction avec le même dénominateur
\begin{align*}
A = \frac{ 1 }{ 4 } + \frac{ 5 }{ 4 }
\end{align*}
Solution
\begin{align*}
\frac{ 1 }{ 4 } + \frac{ 5 }{ 4 }=\frac{ 1 + 5 }{ 4 }=\frac{ 6 }{ 4 }=\frac{ 3 \times 2 }{ 2 \times 2 }=\frac{ 3 }{ 2 }
\end{align*}
\item Fraction avec un denominateur multiple de l'autre
\begin{align*}
A = \frac{ 10 }{ 7 } + \frac{ 3 }{ 49 }
\end{align*}
Solution
\begin{align*}
\frac{ 10 }{ 7 } + \frac{ 3 }{ 49 }=\frac{ 10 \times 7 }{ 7 \times 7 } + \frac{ 3 \times 1 }{ 49 \times 1 }=\frac{ 70 }{ 49 } + \frac{ 3 }{ 49 }=\frac{ 70 + 3 }{ 49 }=\frac{ 73 }{ 49 }
\end{align*}
\item Fraction avec des dénominateurs premiers entre eux
\begin{align*}
A = \frac{ 10 }{ 3 } + \frac{ 4 }{ 2 }
\end{align*}
Solution
\begin{align*}
\frac{ 10 }{ 3 } + \frac{ 4 }{ 2 }=\frac{ 10 \times 2 }{ 3 \times 2 } + \frac{ 4 \times 3 }{ 2 \times 3 }=\frac{ 20 }{ 6 } + \frac{ 12 }{ 6 }=\frac{ 20 + 12 }{ 6 }=\frac{ 32 }{ 6 }=\frac{ 16 \times 2 }{ 3 \times 2 }=\frac{ 16 }{ 3 }
\end{align*}
\item Une fraction et un entier
\begin{align*}
A = \frac{ 6 }{ 8 } + 9
\end{align*}
Solution
\begin{align*}
\frac{ 6 }{ 8 } + 9=\frac{ 6 \times 1 }{ 8 \times 1 } + \frac{ 9 \times 8 }{ 1 \times 8 }=\frac{ 6 }{ 8 } + \frac{ 72 }{ 8 }=\frac{ 6 + 72 }{ 8 }=\frac{ 78 }{ 8 }=\frac{ 39 \times 2 }{ 4 \times 2 }=\frac{ 39 }{ 4 }
\end{align*}
\item Une fraction et un entier
\begin{align*}
A = 2 + \frac{ 8 }{ 2 }
\end{align*}
Solution
\begin{align*}
2 + \frac{ 8 }{ 2 }=\frac{ 2 \times 2 }{ 1 \times 2 } + \frac{ 8 \times 1 }{ 2 \times 1 }=\frac{ 4 }{ 2 } + \frac{ 8 }{ 2 }=\frac{ 4 + 8 }{ 2 }=6
\end{align*}
\end{itemize}
\section{Multiplications de fractions}
\begin{itemize}
\item Une fraction et un entier
\begin{align*}
A = 5 \times \frac{ 7 }{ 8 }
\end{align*}
Solution
\begin{align*}
5 \times \frac{ 7 }{ 8 }=\frac{ 7 }{ 8 } \times 5=\frac{ 7 \times 5 }{ 8 }=\frac{ 35 }{ 8 }
\end{align*}
\item Fraction avec des dénominateurs quelconques
\begin{align*}
A = \frac{ 5 }{ 10 } \times \frac{ 4 }{ 7 }
\end{align*}
Solution
\begin{align*}
\frac{ 5 }{ 10 } \times \frac{ 4 }{ 7 }=\frac{ 4 }{ 7 } \times \frac{ 5 }{ 10 }=\frac{ 2 \times 2 \times 5 }{ 7 \times 5 \times 2 }=\frac{ 4 \times 5 }{ 7 \times 10 }=\frac{ 20 }{ 70 }=\frac{ 2 \times 10 }{ 7 \times 10 }=\frac{ 2 }{ 7 }
\end{align*}
\end{itemize}
\end{document}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,96 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[francais]{babel}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\title{
Snippets pour Opytex \\
Fonctions
}
\author{
Benjamin Bertrand
}
\begin{document}
\maketitle
\section{Calculer des images}
\begin{enumerate}
%-set f = Expression.random("{a}*x^2 + {b}*x + {c}")
\item $\forall x \in \mathbb{R} \qquad f(x) = \Var{f}$
Solution:
\begin{align*}
f(0) &= \Var{f(0).explain() | join('=')} \\
f(1) &= \Var{f(1).explain() | join('=')} \\
f(2) &= \Var{f(2).explain() | join('=')} \\
f({10}) &= \Var{f(10).explain() | join('=')} \\
f({100}) &= \Var{f(100).explain() | join('=')}
\end{align*}
\end{enumerate}
\section{Résolution d'équation du 2nd degré}
%- macro solveEquation(P)
On commence par calculer le discriminant de $P(x) = \Var{P}$.
\begin{eqnarray*}
\Delta & = & b^2-4ac \\
\Var{P.delta.explain()|calculus(name="\\Delta")}
\end{eqnarray*}
\Block{if P.delta > 0}
comme $\Delta = \Var{P.delta} > 0$ donc $P$ a deux racines
\begin{eqnarray*}
x_1 & = & \frac{-b - \sqrt{\Delta}}{2a} = \frac{\Var{-P.b} - \sqrt{\Var{P.delta}}}{2 \times \Var{P.a}} = \Var{P.roots[0] } \\
x_2 & = & \frac{-b + \sqrt{\Delta}}{2a} = \frac{\Var{-P.b} + \sqrt{\Var{P.delta}}}{2 \times \Var{P.a}} = \Var{P.roots[1] }
\end{eqnarray*}
Les solutions de l'équation $\Var{P} = 0$ sont donc $\mathcal{S} = \left\{ \Var{P.roots[0]}; \Var{P.roots[1]} \right\}$
\Block{elif P.delta == 0}
Comme $\Delta = 0$ donc $P$ a une racine
\begin{eqnarray*}
x_1 = \frac{-b}{2a} = \frac{-\Var{P.b}}{2\times \Var{P.a}} = \Var{P.roots[0]} \\
\end{eqnarray*}
La solution de $\Var{P} = 0$ est donc $\mathcal{S} = \left\{ \Var{P.roots[0]}\right\}$
\Block{else}
Alors $\Delta = \Var{P.delta} < 0$ donc $P$ n'a pas de racine donc l'équation $\Var{P} = 0$ n'a pas de solution.
\Block{endif}
%- endmacro
\begin{enumerate}
%-set P = Expression.random("{a}*x^2 + {b}*x + {c}", ["b**2-4*a*c>0"])
\item Étude du polynôme $P$, $\forall x \in \mathbb{R} \quad P(x) = \Var{P}$
Solution:
\Var{solveEquation(P)}
%-set P = Expression.random("{a}*x^2 + {b}*x + {c}", ["b**2-4*a*c==0"])
\item Étude du polynôme $P$, $\forall x \in \mathbb{R} \quad P(x) = \Var{P}$
Solution:
\Var{solveEquation(P)}
%-set P = Expression.random("{a}*x^2 + {b}*x + {c}", ["b**2-4*a*c<0"])
\item Étude du polynôme $P$, $\forall x \in \mathbb{R} \quad P(x) = \Var{P}$
Solution:
\Var{solveEquation(P)}
\end{enumerate}
\end{document}

View File

@ -1,133 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[francais]{babel}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\title{
Snippets pour Opytex \\
Fractions
}
\author{
Benjamin Bertrand
}
\begin{document}
\maketitle
\section{Simplifications de fractions}
\begin{itemize}
\item Trouver le numérateur quand le dénominateur augmente
\Block{set a,b,ans,c = random_str("{a},{b},{a*c},{b*c}", conditions = ["{a} != {b}"], val_min = 2, val_max = 10).split(',')}%
\begin{align*}
\dfrac{\Var{a}}{\Var{b}} = \dfrac{\ldots}{\Var{c}}
\end{align*}
Solution
\begin{align*}
\dfrac{\Var{a}}{\Var{b}} = \dfrac{\Var{ans}}{\Var{c}}
\end{align*}
\item Trouver le numérateur quand le dénominateur diminue
\Block{set a,b,ans,c = random_str("{a*c},{b*c},{a},{b}", conditions = ["{a} != {b}"], val_min = 2, val_max = 10).split(',')}%
\begin{align*}
\dfrac{\Var{a}}{\Var{b}} = \dfrac{\cdots}{\Var{c}}
\end{align*}
Solution
\begin{align*}
\dfrac{\Var{a}}{\Var{b}} = \dfrac{\Var{ans}}{\Var{c}}
\end{align*}
Explications
\Block{set f = Expression(a + "/" +b)}
\begin{align*}
\Var{f.simplify().explain()|join('=')}
\end{align*}
\end{itemize}
\section{Ajouts de fractions}
\begin{itemize}
\item Fraction avec le même dénominateur
\Block{set e = Expression.random("{a} / {b} + {c} / {b}", ["{b} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\item Fraction avec un denominateur multiple de l'autre
\Block{set e = Expression.random("{a} / {b} + {c} / {b*d}", ["{b} > 1","{d} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\item Fraction avec des dénominateurs premiers entre eux
\Block{set e = Expression.random("{a} / {b} + {c} / {d}", ["{b} > 1","{d} > 1", "gcd({b},{d}) == 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\item Une fraction et un entier
\Block{set e = Expression.random("{a} / {b} + {c}", ["{b} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\item Une fraction et un entier
\Block{set e = Expression.random("{c} + {a} / {b}", ["{b} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\end{itemize}
\section{Multiplications de fractions}
\begin{itemize}
\item Une fraction et un entier
\Block{set e = Expression.random("{c} * {a} / {b}", ["{b} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\item Fraction avec des dénominateurs quelconques
\Block{set e = Expression.random("{a} / {b} * {c} / {d}", ["{b} > 1","{d} > 1"], val_min = 1)}
\begin{align*}
A = \Var{e}
\end{align*}
Solution
\begin{align*}
\Var{e.simplify().explain() | join('=')}
\end{align*}
\end{itemize}
\end{document}

View File

@ -1,87 +0,0 @@
% vim:ft=tex:
%
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[francais]{babel}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\title{
Snippets pour Opytex \\
Suites
}
\author{
Benjamin Bertrand
}
\begin{document}
\maketitle
\section{Calculs de termes}
\begin{enumerate}
\item Calculer les termes $u_0$, $u_1$, $u_2$, $u_{10}$ et $u_{100}$ pour les suites suivantes
\begin{enumerate}
%-set u = Expression.random("{a}*n+{b}")
\item $\forall n \in \mathbb{N} \qquad u_n = \Var{u}$
Solution:
\begin{align*}
u_0 &= \Var{u(0).explain() | join('=')} \\
u_1 &= \Var{u(1).explain() | join('=')} \\
u_2 &= \Var{u(2).explain() | join('=')} \\
u_{10} &= \Var{u(10).explain() | join('=')} \\
u_{100} &= \Var{u(100).explain() | join('=')}
\end{align*}
%-set v = Expression.random("({a}*n+{b})/{c}", ["c>1"])
\item $\forall n \in \mathbb{N} \qquad v_n = \Var{v|replace("frac","dfrac")}$
Solution:
\begin{align*}
v_0 &= \Var{v(0).explain() | join('=')} \\
v_1 &= \Var{v(1).explain() | join('=')} \\
v_2 &= \Var{v(2).explain() | join('=')} \\
v_{10} &= \Var{v(10).explain() | join('=')} \\
v_{100} &= \Var{v(100).explain() | join('=')}
\end{align*}
%-set v = Expression.random("({a}*n+{b})/{c}", ["c>1"])
\item $\forall n \in \mathbb{N} \qquad v_n = \Var{v}$
Solution:
\begin{align*}
%- for j in [0, 1, 2, 10, 100]
v_{\Var{j}} &= \Var{v(j).explain() | join('=')} \\
%- endfor
\end{align*}
%-set f = Expression.random("{a}*x")
%-set v0 = randint(0, 10)
\item $\forall n \in \mathbb{N} \qquad v_{n+1} = \Var{f("v_n")} \mbox{ et } v_0 = \Var{v0}$
Solution:
\begin{align*}
v_0 &= \Var{v0} \\
%-set v = f(v0)
v_1 &= \Var{v.explain() | join('=')} \\
%-set v = f(v)
v_2 &= \Var{v.explain() | join('=')} \\
\end{align*}
Pour le terme 10, il faut calculer tous les autres avant!
\begin{align*}
%#- Trick to move around scoping rules
%#- https://stackoverflow.com/a/49699589
%- set v = namespace(val = v)
%- for i in range(8)
%- set v.val = f(v.val)
v_{\Var{i+3}} &= \Var{v.val.explain() | join('=')} \\
%- endfor
\end{align*}
\end{enumerate}
\end{enumerate}
\end{document}

6
test/students.csv Normal file
View File

@ -0,0 +1,6 @@
nom,classe,elo
Bob,1ST,1000
Pipo,1ST,1300
Popi,1ST,100
Boule,1ST,4000
Bill,1ST,1300
1 nom classe elo
2 Bob 1ST 1000
3 Pipo 1ST 1300
4 Popi 1ST 100
5 Boule 1ST 4000
6 Bill 1ST 1300

View File

@ -0,0 +1,22 @@
\documentclass[12pt]{article}
\usepackage[utf8x]{inputenc}
\usepackage[francais]{babel}
\usepackage[T1]{fontenc}
\usepackage{amssymb}
\usepackage{amsmath}
\usepackage{amsfonts}
\title{
Tests
}
\author{
Benjamin Bertrand
}
\begin{document}
\maketitle
\end{document}

212
test/test_bopytex.py Normal file
View File

@ -0,0 +1,212 @@
#!/usr/bin/env python
# encoding: utf-8
import pytest
import os
from pathlib import Path
from shutil import copyfile
from bopytex.bopytex import produce_and_compile, subject_metadatas
SNIPPETS_PATH = Path("snippets/")
TEST_PATH = Path("test")
TEST_TEMPLATE_PATH = TEST_PATH / "templates/"
@pytest.fixture
def prepare_test_template(tmp_path):
""" Create a tmp directory, copy snippets inside
return tmp directory name
"""
tmp = tmp_path
snippets = TEST_TEMPLATE_PATH.glob("tpl_*.tex")
for s in snippets:
copyfile(s, tmp / s.name)
csvs = TEST_PATH.glob("*.csv")
for s in csvs:
copyfile(s, tmp / s.name)
prev_dir = Path.cwd()
os.chdir(tmp)
yield tmp
os.chdir(prev_dir)
@pytest.fixture
def prepare_snippets(tmp_path):
""" Create a tmp directory, copy snippets inside
return tmp directory name
"""
tmp = tmp_path
snippets = SNIPPETS_PATH.glob("tpl_*.tex")
for s in snippets:
copyfile(s, tmp / s.name)
prev_dir = Path.cwd()
os.chdir(tmp)
yield tmp
os.chdir(prev_dir)
def test_produce_and_compile_base(prepare_test_template):
test_tpl = list(Path(".").glob("tpl_*.tex"))
assert [tpl.name for tpl in test_tpl] == ["tpl_test.tex"]
for tpl in test_tpl:
produce_and_compile(
{
"template": tpl,
"working_dir": None,
"only_corr": False,
"students_csv": None,
"number_subjects": 1,
"dirty": False,
"no_compile": False,
"no_join": False,
"corr": False,
"crazy": False,
}
)
def test_produce_and_compile_csv(prepare_test_template):
test_tpl = Path(".").glob("tpl_*.tex")
for tpl in test_tpl:
options = {
"template": tpl,
"working_dir": None,
"only_corr": False,
"students_csv": "students.csv",
"number_subjects": 1,
"dirty": False,
"no_compile": False,
"no_join": False,
"corr": False,
"crazy": False,
}
# produce_and_compile(options)
def test_metadatas(prepare_test_template):
test_tpl = Path(".").glob("tpl_*.tex")
for tpl in test_tpl:
options = {
"template": tpl,
"working_dir": None,
"only_corr": False,
"students_csv": "students.csv",
"number_subjects": 1,
"dirty": False,
"no_compile": False,
"no_join": False,
"corr": False,
"crazy": False,
}
metadatas = subject_metadatas(options)
meta = [
{
"num": "01",
"nom": "Bob",
"classe": "1ST",
"elo": "1000",
"texfile": "01_test.tex",
"template": "tpl_test.tex",
"directory": ".",
},
{
"num": "02",
"nom": "Pipo",
"classe": "1ST",
"elo": "1300",
"texfile": "02_test.tex",
"template": "tpl_test.tex",
"directory": ".",
},
{
"num": "03",
"nom": "Popi",
"classe": "1ST",
"elo": "100",
"texfile": "03_test.tex",
"template": "tpl_test.tex",
"directory": ".",
},
{
"num": "04",
"nom": "Boule",
"classe": "1ST",
"elo": "4000",
"texfile": "04_test.tex",
"template": "tpl_test.tex",
"directory": ".",
},
{
"num": "05",
"nom": "Bill",
"classe": "1ST",
"elo": "1300",
"texfile": "05_test.tex",
"template": "tpl_test.tex",
"directory": ".",
},
]
assert metadatas == meta
def test_pdfjoin_current_directory(prepare_test_template):
wdir = prepare_test_template
pass
def test_pdfjoin_deep_directory():
pass
def test_pdfjoin_dont_remove():
pass
def test_subject_names():
pass
def test_feed_texfiles():
pass
def test_tex2pdf_current_directory():
pass
def test_tex2pdf_deep_directory():
pass
def test_activate_solution():
pass
# def test_snippets(prepare_snippets):
# snippets = list(Path(".").glob("tpl_*.tex"))
# for tpl in snippets:
# produce_and_compile(
# {
# "template": tpl,
# "working_dir": None,
# "only_corr": False,
# "students_csv": None,
# "number_subjects": 1,
# "dirty": False,
# "no_compile": False,
# "no_join": False,
# "corr": False,
# "crazy": False,
# }
# )
# -----------------------------
# Reglages pour 'vim'
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
# cursor: 16 del