271 lines
7.5 KiB
Python
Executable File
271 lines
7.5 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# encoding: utf-8
|
|
|
|
"""
|
|
Producing then compiling templates
|
|
"""
|
|
|
|
import csv
|
|
import os
|
|
import logging
|
|
import optparse
|
|
import sys
|
|
|
|
from path import Path
|
|
import pytex
|
|
from mapytex import Expression, Integer, Decimal
|
|
import bopytex.filters as filters
|
|
|
|
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)
|
|
|
|
|
|
def setup():
|
|
Expression.set_render("tex")
|
|
logger.debug(f"Render for Expression is {Expression.RENDER}")
|
|
mapytex_tools = {
|
|
"Expression": Expression,
|
|
"Integer": Integer,
|
|
"Decimal": Decimal,
|
|
# "Polynom": mapytex.Polynom,
|
|
# "Fraction": mapytex.Fraction,
|
|
# "Equation": mapytex.Equation,
|
|
# "random_str": mapytex.random_str,
|
|
# "random_pythagore": mapytex.random_pythagore,
|
|
# "Dataset": mapytex.Dataset,
|
|
# "WeightedDataset": mapytex.WeightedDataset,
|
|
}
|
|
pytex.update_export_dict(mapytex_tools)
|
|
|
|
pytex.add_filter("calculus", filters.do_calculus)
|
|
|
|
|
|
def get_working_dir(options):
|
|
""" Get the working directory """
|
|
if options.working_dir:
|
|
working_dir = Path(options.working_dir)
|
|
else:
|
|
try:
|
|
template = Path(options.template)
|
|
except TypeError:
|
|
raise ValueError(
|
|
"Need to set the working directory \
|
|
or to give a template"
|
|
)
|
|
else:
|
|
working_dir = template.dirname()
|
|
logger.debug(f"The output directory will be {working_dir}")
|
|
return working_dir
|
|
|
|
|
|
def activate_printanswers(
|
|
texfile, noans=r"solution/print = false", ans=r"solution/print = true"
|
|
):
|
|
""" Activate printanswers mod in texfile """
|
|
output_fname = "corr_" + texfile
|
|
with open(texfile, "r") as input_f:
|
|
with open(output_fname, "w") as output_f:
|
|
for line in input_f.readlines():
|
|
output_f.write(line.replace(noans, ans))
|
|
return output_fname
|
|
|
|
|
|
def deactivate_printanswers(corr_fname):
|
|
""" Activate printanswers mod in texfile """
|
|
Path(corr_fname).remove()
|
|
|
|
|
|
def pdfjoin(pdf_files, destname, working_dir=".", rm_pdfs=1):
|
|
"""TODO: Docstring for pdfjoin.
|
|
|
|
:param pdf_files: list of pdf files to join
|
|
:param destname: name for joined pdf
|
|
:param working_dir: the working directory
|
|
:param rm_pdfs: Remove pdf_files after joining them
|
|
:returns: TODO
|
|
|
|
"""
|
|
joined_pdfs = Path(working_dir) / Path(destname)
|
|
pdf_files_str = " ".join(pdf_files)
|
|
pdfjam = f"pdfjam {pdf_files_str} -o {joined_pdfs}"
|
|
logger.debug(f"Run {pdfjam}")
|
|
logger.info("Joining pdf files")
|
|
os.system(pdfjam)
|
|
if rm_pdfs:
|
|
logger.info(f"Remove {pdf_files_str}")
|
|
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["Élève"] for r in reader]
|
|
|
|
|
|
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}")
|
|
|
|
if options.student_csv:
|
|
list_infos = [
|
|
{"num": f"{i+1:02d}", "name": s}
|
|
for (i, s) in enumerate(extract_student_csv(options.student_csv))
|
|
]
|
|
else:
|
|
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(
|
|
"-s",
|
|
"--students",
|
|
action="store",
|
|
type="string",
|
|
dest="student_csv",
|
|
help="CSV containing list of students names",
|
|
)
|
|
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)
|
|
|
|
produce_and_compile(options)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|
|
|
|
# -----------------------------
|
|
# Reglages pour 'vim'
|
|
# vim:set autoindent expandtab tabstop=4 shiftwidth=4:
|
|
# cursor: 16 del
|