#!/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) if options.crazy: while True: try: pytex.feed(working_dir / template, {"infos": infos}, output=dest, force=1) except: pass else: break else: 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", ) parser.add_option( "-C", "--crazy", action="store_true", dest="crazy", help="Crazy mode. Tries and tries again until template feeding success!", ) (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