diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index 6235e71..c863828 100755 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -5,15 +5,8 @@ Producing then compiling templates """ -import csv -import os import logging - -from pathlib import Path -import pytex -#from mapytex import Expression, Integer, Decimal, random_list -import mapytex -import bopytex.filters as filters +import csv formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -24,222 +17,42 @@ logger.setLevel(logging.DEBUG) logger.addHandler(steam_handler) -def setup(): - mapytex.Expression.set_render("tex") - logger.debug(f"Render for Expression is {mapytex.Expression.RENDER}") - mapytex_tools = { - "Expression": mapytex.Expression, - "Integer": mapytex.Integer, - "Decimal": mapytex.Decimal, - "random_list": mapytex.random_list, - "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 build_subject_list_from_infos(infos: list[dict]) -> list[dict]: + subjects = [] + digit = len(str(len(infos))) + for i, infos in enumerate(infos): + subjects.append({"number": str(i + 1).zfill(digit), **infos}) + return subjects -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.parent - logger.debug(f"The output directory will be {working_dir}") - return working_dir +def build_subject_list_from_qty(qty: int) -> list[dict]: + subjects = [] + digit = len(str(qty)) + for i in range(qty): + subjects.append({"number": str(i + 1).zfill(digit)}) + return subjects + +def build_subjects(students_csv, quantity_subjects): + if students_csv: + with open(students_csv, "r") as csv_file: + infos = csv.DictReader(csv_file) + return build_subject_list_from_infos(infos) + + return build_subject_list_from_qty(quantity_subjects) -def activate_printanswers( - texfile, - noans=r"%\printsolutionstype{exercise}", - ans=r"\printsolutionstype{exercise}", - corr_prefix="corr_" +def bopytex( + template: str, + working_dir: str, + students_csv: str, + quantity_subjects: int, + corr: bool, + dirty: bool, + only_corr: bool, + crazy: bool, + no_join: bool, ): - """ Activate printanswers mod in texfile - - :param texfile: path to the latex file - :param noans: string that prevent printing solution - :param ans: string that activate printing solution - :param corr_prefix: correction file prefix - """ - output_fname = corr_prefix + 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 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 - """ - 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) + pass # ----------------------------- diff --git a/bopytex/script.py b/bopytex/script.py index 73e60da..d4747e6 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -5,7 +5,8 @@ import click import logging from pathlib import Path -from .bopytex import subject_metadatas, crazy_feed, pdfjoin, feed, clean, texcompile, setup, activate_printanswers + +from bopytex.bopytex import bopytex formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -26,6 +27,7 @@ logger.addHandler(steam_handler) @click.option( "-w", "--working-dir", + default=".", type=click.Path(exists=True), ) @click.option( @@ -46,11 +48,11 @@ logger.addHandler(steam_handler) help="Do not compile source code", ) @click.option( - "-N", - "--number_subjects", + "-q", + "--quantity_subjects", type=int, default=1, - help="The number of subjects to make", + help="The quantity of subjects to make", ) @click.option( "-j", @@ -81,82 +83,7 @@ logger.addHandler(steam_handler) 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: - if not options["only_corr"]: - 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"] or options["only_corr"]: - logger.debug(f"Building correction for {meta['texfile']}") - meta.update({ - "corr_texfile": activate_printanswers(meta["texfile"]), - }) - - if not options["no_compile"]: - if options["only_corr"]: - to_compile = ["corr_"] - else: - to_compile = ["", "corr_"] - - for prefix in to_compile: - 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) + bopytex(**options) if __name__ == "__main__": diff --git a/test/test_bopytex.py b/test/test_bopytex.py new file mode 100644 index 0000000..c8b69c5 --- /dev/null +++ b/test/test_bopytex.py @@ -0,0 +1,29 @@ +from bopytex.bopytex import build_subject_list_from_infos, build_subject_list_from_qty + + +def test_build_subject_list_from_qty(): + subjects = build_subject_list_from_qty(10) + assert subjects == [ + {"number": "01"}, + {"number": "02"}, + {"number": "03"}, + {"number": "04"}, + {"number": "05"}, + {"number": "06"}, + {"number": "07"}, + {"number": "08"}, + {"number": "09"}, + {"number": "10"}, + ] + + +def test_build_subject_list_from_infos(): + infos = [ + {"name": "test1", "date": "today"}, + {"name": "test2", "date": "tomorow"}, + ] + subjects = build_subject_list_from_infos(infos) + assert subjects == [ + {"name": "test1", "date": "today", "number": "1"}, + {"name": "test2", "date": "tomorow", "number": "2"}, + ]