From f0be577119aac5c4c51d30a45972be7746a45b6f Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 15:06:12 +0200 Subject: [PATCH 01/76] Fix: imports --- bopytex/script.py | 79 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/bopytex/script.py b/bopytex/script.py index bcec04c..5dfc097 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -3,7 +3,17 @@ import click -from .bopytex import produce_and_compile +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( @@ -76,7 +86,72 @@ def new(**options): Feed the template (tpl_...) and then compile it with latex. """ - produce_and_compile(options) + 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__": From 16af6c15fcd23c9383b33520ef85427cd228d34e Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:10:41 +0200 Subject: [PATCH 02/76] Feat: remove snippets --- examples/01_add_fraction.tex | 18 --- examples/02_add_fraction.tex | 18 --- examples/all_add_fraction.pdf | Bin 39639 -> 0 bytes snippets/1_fraction.tex | 133 -------------------- snippets/Geometrie/fig/parcours.pdf | Bin 6652 -> 0 bytes snippets/Geometrie/tpl_Pythagore_thales.tex | 121 ------------------ snippets/all_fonctions.pdf | Bin 95798 -> 0 bytes snippets/all_fraction.pdf | Bin 83025 -> 0 bytes snippets/all_suite.pdf | Bin 106335 -> 0 bytes snippets/tpl_fonctions.tex | 96 -------------- snippets/tpl_fraction.tex | 133 -------------------- snippets/tpl_suite.tex | 87 ------------- 12 files changed, 606 deletions(-) delete mode 100644 examples/01_add_fraction.tex delete mode 100644 examples/02_add_fraction.tex delete mode 100644 examples/all_add_fraction.pdf delete mode 100644 snippets/1_fraction.tex delete mode 100644 snippets/Geometrie/fig/parcours.pdf delete mode 100644 snippets/Geometrie/tpl_Pythagore_thales.tex delete mode 100644 snippets/all_fonctions.pdf delete mode 100644 snippets/all_fraction.pdf delete mode 100644 snippets/all_suite.pdf delete mode 100644 snippets/tpl_fonctions.tex delete mode 100644 snippets/tpl_fraction.tex delete mode 100644 snippets/tpl_suite.tex diff --git a/examples/01_add_fraction.tex b/examples/01_add_fraction.tex deleted file mode 100644 index e3e4e46..0000000 --- a/examples/01_add_fraction.tex +++ /dev/null @@ -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} \ No newline at end of file diff --git a/examples/02_add_fraction.tex b/examples/02_add_fraction.tex deleted file mode 100644 index f865e87..0000000 --- a/examples/02_add_fraction.tex +++ /dev/null @@ -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} \ No newline at end of file diff --git a/examples/all_add_fraction.pdf b/examples/all_add_fraction.pdf deleted file mode 100644 index 491367a180273487094674f8290459497defd114..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39639 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 diff --git a/snippets/1_fraction.tex b/snippets/1_fraction.tex deleted file mode 100644 index 3e211ac..0000000 --- a/snippets/1_fraction.tex +++ /dev/null @@ -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} \ No newline at end of file diff --git a/snippets/Geometrie/fig/parcours.pdf b/snippets/Geometrie/fig/parcours.pdf deleted file mode 100644 index 51edb4688a67bf104240baa772f8fe7d09b4e6e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6652 zcmds+cT`hLx4>x%(xnOl9;HYpAtV85(xpiUQCbKALP?ylU*7-%yP+e03ED5C*yuVtxOA7%& z0M_1xRzU%f(nDdKyqp0UqR5yQ001dXw3`WB-Q~%tE5Eh>4irj+-d!C za!oohY+(9_3u1vt4z!hg!_4djbgU^s4S^|+GJ7ClYE7^f-3iJWXQz`;y{4Hd5IlG_ zZqP7&pAvMOn=$Fcv;Fvk6(7I`e02B(B#y^h#$hcaBa>F;_I#w*vwWI$Q9E=B3y# z+0ljC2|60J9OWUL3;TEa@3tObEjGukA2s^o6pC{e^!4~p%a6Sm<`PScepG)^tC9aE zo68G#ZX1a^yXuQ>&7#$IVj2T>E!tfxadKH{ZWH5o8-$dQXL(nSIN#*ed~BRO#3;X- zq3NT8Ij*5Ge{a(9Lpc(0&p1N-^69d%Kbz|~AsI$^$3A9D`zJLS+P($1uBe}=KQgLO z>$8QI{R;Hvr@zsm@dM&dV80Fb;+dJ_FEBO^m< zC)K2Xg~wq)X!v<)nRpR{0Z>pN35b^z0z|9;Qfevy7?46Dh<9R$5F5VVr8Kb^ufO$n zlmJP90O-#UBX%NweoO}YH5njf>W@PKQicd8)ZxU22t0~74)~{@ls?J#~_QT;U#s z;}p88G%CU6G&HvZj-L@~=sYqVW14;5wBrQp2y-yn@acwHgtLgVIyaK`7!@tI3iqOa zl3&2x#Y4DdIF(HK}o(@fSzpo#!jz!qW^K8-v zogy2WyzDbJZM^v4K7GeNXjn*+U)8j%CE~TjNSot$(3h&bH+I1(uh)#O&Ob!ya#BuM zQ8DLUSe5%CVPidb%E-d-LCWjAja1)MTkJRfAWdaPAT5;QG}rv;n)G^T=O5?U+9{UV z?+{$BcQwUT=B*d_e)jsjgXFP^dbaW@Qg+M2PW8Y^+O0NZb4~{u?zebUYFOWv!%q=M zDSHCf$sWa=BPZ@m9z_w&k*tOr1+sXZhi}AjUAt33KbCdf5>m>;e$)|Bvuw@l;Xi|z zNTIV<98?I|4?B}A*#;V3b17yBT2_14Q#z0*+^a(Yxn)_;;vVzzRv`^v1pXDhBkKz` z_5p>o9x*!0L}O-e_?0{5j!dFVGdgWg>MxfeS5mX?u0CO;-)v$L21BHtI-(bQWQwhA zj00;oAcAYTpDBGS*EqIA{S;oov&^2}r{WKf=`_%2X`){TfkDr=TmLLTm4z!ZQGb2e zRKk<3l{M}D-gk-r^M0N6JDxQQr}=`LX`PSyt(HZgCReTeHx|?@l7&*s2Hc9Lj?=^mK_eJ;`el5 zDt*7HxlG@iw}g@t*9aDupQBHD(f7jG zleVRNp_Y?#A9_O#PPWhGYwiss9LF(kE>t;(X=Gf9%wWnyRmU|1r+dHVYDE@UiJWl$ zcE8j6;>`KV&P{zc2UIufX}BFCo>h&Rb|D}nIJpkgE^_Bnl!ki&eO`vCo4DXq4@JW@ zVP+L0$%g2og)ClZM)Q}#Ez~Wk!(`#%)%KrEY$_siiaL%KWII>6#gsog-kh*DmpXDW z0v#HW=x*nG|5(LnTPAPUwm{U2023Z+{mfj4jHJ<%9(i<}0rCM`ak~501pdog|Tixp3j4D>SGh{g_xG!~m9fERS{s)8g zt9v#f%UfILjRCm?mQPfzp(A0p!)C)$C=5ayf&0Kig@>?G$!5voL6e0F{kKVz3BhuT zUzbP9k}EIL2vM7ay^oTAefdgBW0mrd9PYZvZsQRq_DS!(IAv?|TD4SN(MfFIPG`sK z-l6GaDIWQ$@<|yVH)KFS`xIP@49AC|xefHwF+SdE9Q6zXG!^)7leZlw2yem6%+(dN#12ooQ@1pArk z-P!Ud>GtsMj@##3t(}K=Sb8RWDhBOG``MbHZ_)Xy&v#3_hNN8md<{;sJ6rAzwBxk4 zn`)S>YQ`Xqq15Ebt9E6VipvM8WPQqaueY45xzc8%JXAfEmV0&5DD1P_>jYc%y-YW6 z6^azcvaCFS>g58Z|}yuX8GlvRPRtV&-20wjP?#bZ$gsGS&tN?^_S`o!Xkz zJxU*XV@Z2Xn?W0U*7_pL3&gqXNnDEJktcn4w1pP9z!k1EiJS{=Jx#E<8Y);Pf~Dtfn{ykO9fIattC zB`WW|-SY<<+2YsPY{@8+V>Yv`*l-hx*C;hRxXv+Ot=$jc)*S38^sJUmJ(8DGByJ&^ z>VnEOE>bA|U?!uMkjbb7uNc0-)2G!boEFM8Q8ag+G5bbxvu!p*-MCfGQ>R!tm-AO> z=q$k;n-mgMr+t_vyVf|KjudQ@O*|8=Nm2&*j9m-U+&dTo!!MOYp6+iDk@t{)C)rRc z74xS{vXAQSg_9$9hQZv)?y$JwQf#$`+wjQ^tssS0w$ap;GAES!MRx>qsv>DN*U9Ui zQ&f56^fgUFc`Q%2_vz8RIu?rTpf+H>mxH%U;`cSK=`!Aa|<-}L#^yiGY7xhe7lThn+ZD-d29l05cB8`R3$hgtXSzoWZ=b3a+RdQfCR zIg_VjVI(>n!g+A8;_yw21|>H{o10>w%KyQB_b&UH4cc2W!UKkMOw4l(FZ*D^nd)z- z)29?}mWN+-o9&oaff+Is)A?*2sInX1 z3A-e`ZPeEVXFvPa+qfV#h>9IYrTo~TkLcVQP!KGeOc%GW8-abH2d&kYM zMbP5gFplKfUosy?ZVR|-&nh2-X!r`)-mx}6cD>rd-OT!qOp-m&5dW|h=0<3H87m>qT&`i3=6>*!hfu z(XB-x16DSf@t1y=sj2B={KAAWECl_<7gZEAtPQS>)LwW_ryzF zBa1E+Yvw``ZXvMU{HZ-?N1r2@x3}uUjuYV_%fW1y)auS-kj?hgDcB^i53C!XXs1CM zoZ4?=I?$b7NM2WVdapq^NW7kJeF;#an>oVmhaeCB)+f7;J{2s@sGPPVPdJ5|YJc#l z5wT}DO?`2%Q8e&Cxb5t^HTOP--N*N>KucNmijcy=#V}c^^<1hwP6AeU&xz2U=qGu@ zNj|y9l91cCvm&m*ziwKgmZQ@RN7krKpJcI3_cNY8Mrcp+vzi7IR$1b86s9{Gyp@K9 zYCeB58WvsuRLJ77Bg|Jh6&1Y18l^)K?kC(4ved;vsJdQbIbFt*EwHaT-Dw=y6Gv^c zO`t5^!55jde(2IX+orD5J`yFRGa==X-<>b-QPrLQ(2i62=_Ae$JAopwp@c}#$Hb=H zX5p68)1F36E8~7OWo-FxOJkj8JWAr^#ha|B8>;o)l20^DnP=5QZzu=#N(Ki=C?Ox0 z?0zbh(CRT^X&gz9meBIhWvB{yes)>6TB##{Yl@Wxj^!4E=}yRSz>_jhVmlaCD(u7Q z#$JqO(Rdd(#U)%`=(}zwT?o zFR(62C}v65u{z_i?m)jibxT-0aYv2ddqG^XHhNk~y!QQS&HD7VxRvlaEGh5}y|~v5 z#hutA{J1tcY%V81CiRkCPe_vA(}9*Ijp>1N`{(VIKF{jrz%4j8?QxhS{r*vpI0kHz zto)6-N3tf|8*02Fr;Xy({jrmiBqw z+Rc*SQYO!{%V)-}vwD`>--?qn&q=s34(sQ}vRkq|zA~&lD7pvZaq6kbbOf6})`P_L zJsKYS+P!GpD%mAcxrvBmtGA?78c_Wx38xK(m+>4eR;@Zra^Pd2<=`e3%2N#YIV?e`8B=u{&3Hnt&6%eQ$ul&k-Qzv^ke`Z4FG zVQZAemlKHkK`N_s*C86toO0GwDx`O7?ndQ8XKYx~uKP_{UyN0Rv0-3>=S@WT+hj}0 z9e}LBkM3x=H|vMCK}Flo7Hc6mP0m6#ceUUHl0R~Bf00|i|6ad3yK>!#GgZ-d1+Fl4ZG*SiQxq0rRI#l|1^Le;zwN? z0y!MvpXMem|5qw#V2F?vjd22`EYKJgj3@f1;84#3?cn9?Nkm={FliTm6=^sL-~G1* zCQH(S$;kq6IPnP~0wy@g8vlKPfk~W;RA5Bce*Y#7|K}QE5YlWihnt1Sk@`dBNE`qE zUr07NM3F?Kh&98YNgN#@9f^Mr!{3vV1V6v$nl9Rb zjY#N;nG6Uc5okOX084^pBw>KCvzHg{oD`AL5`|891kM?a^pwQnokYH;tsx%k;Ef~_ z|NmU=FM139`(Fv)j}O8lw$mkdOTy?@D|5Te!oDT6}fh)eya3(II|X;~PpprD3 100 - %- set unit = "m" -%- else - %- set unit = "km" -%- endif - - -Une commune souhaite aménager des parcours de santé sur son territoire. On fait deux propositions au conseil municipale, schématisés ci-dessous: -\begin{itemize} - \item Le parcours ACDA - \item Le parcours AEFA -\end{itemize} -Ils souhaitent faire un parcours dont la longueur s'approche le plus possible de \Var{objectif}\Var{unit}. - -Peux-tu les aider à choisir le parcours? Justifie - -\textbf{Attention: La figure proposée au conseil municipale n'est pas à l'échelle, mais les codages et les dimension données sont correctes.} - -\begin{minipage}{0.6\textwidth} - \includegraphics[scale = 0.4]{./fig/parcours} -\end{minipage} -\begin{minipage}{0.4\textwidth} - \begin{itemize} - \item $AC = \Var{AC}\Var{unit}$ - \item $CD = \Var{DC}\Var{unit}$ - \item $AE' = \Var{AE1}\Var{unit}$ - \item $AE = \Var{AE}\Var{unit}$ - \item $AF = \Var{AF}\Var{unit}$ - \item $E'F' = \Var{EF1}\Var{unit}$ - \item $(E'F') // (EF)$ - \item L'angle $\widehat{EAF}$ vaut $30^o$ - \end{itemize} -\end{minipage} -\begin{solution} - \begin{itemize} - \item Parcours ACDA: - - D'après la figure, on voit que le triangle $ACD$ est rectangle en $C$ donc d'après le théorème de Pythagore, on a - \begin{align*} - AD^2 &= AC^2 + DC^2 \\ - AD^2 &= \Var{AC}^2 + \Var{DC}^2 \\ - AD^2 &= \Var{AC**2} + \Var{DC**2} \\ - AD^2 &= \Var{AC**2 + DC**2} \\ - AD &= \sqrt{\Var{AC**2 + DC**2}} = \Var{AD}\Var{unit} - \end{align*} - Donc le parcours ACDA mesure - \begin{align*} - AD + AC + CD = \Var{AD} + \Var{AC} + \Var{DC} = \Var{tourACDA}\Var{unit} - \end{align*} - - \item Parcours AEFA: - - D'après les données, on sait que $(EF) // (E'F')$. On voit aussi que $A$, $E'$ et $E$ sont alignés. Il en est de même pour les points $A$, $F'$ et $F$. Donc d'après le théorème de Thalès - - \begin{tabular}{|c|c|c|c|} - \hline - Triangle AEF & AE = \Var{AE} & AF = \Var{AF} & EF \\ - \hline - Triangle AE'F' & AE' = \Var{AE1} & AF' & E'F' = \Var{EF1} \\ - \hline - \end{tabular} - est un tableau de proportionnalité. Donc on peut faire un produit en croix pour calcul $EF$. - \begin{align*} - EF = \frac{E'F' \times AE}{AE'} = \frac{\Var{EF1} \times \Var{AE}}{\Var{AE1}} = \Var{EF} \Var{unit} - \end{align*} - - Donc le parcours AEFA mesure - \begin{align*} - AF + AE + EF = \Var{AF} + \Var{AE} + \Var{EF} = \Var{tourAEFA}\Var{unit} - \end{align*} - - \item Choix du parcours: - - %- if abs(tourACDA - objectif) < abs(tourAEFA - objectif) - Il faudra choisir le tour $ACDA$ car sa longueur est plus proche de \Var{objectif}\Var{unit}. - %- else - Il faudra choisir le tour $AFEA$ car sa longueur est plus proche de \Var{objectif}\Var{unit}. - %- endif - \end{itemize} -\end{solution} - -\end{document} diff --git a/snippets/all_fonctions.pdf b/snippets/all_fonctions.pdf deleted file mode 100644 index 86299c84563faf3ebaa3ad98552ce1cadafe2fd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95798 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 diff --git a/snippets/all_fraction.pdf b/snippets/all_fraction.pdf deleted file mode 100644 index 95a35fe20b7487f0fa474bec050f8f107e77ee25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83025 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�Y?F9^wi@Wl4Xb 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 z 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 Xo!uROHiLutC**-9BNLGmh5r8lSm_MR diff --git a/snippets/all_suite.pdf b/snippets/all_suite.pdf deleted file mode 100644 index f15b1ac85a613161164e079c142d2f17179da821..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106335 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 V_swMqL&RZFNx*{#s`_ex{{aaOio*Z^ diff --git a/snippets/tpl_fonctions.tex b/snippets/tpl_fonctions.tex deleted file mode 100644 index ea3fac8..0000000 --- a/snippets/tpl_fonctions.tex +++ /dev/null @@ -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} diff --git a/snippets/tpl_fraction.tex b/snippets/tpl_fraction.tex deleted file mode 100644 index da4d9d1..0000000 --- a/snippets/tpl_fraction.tex +++ /dev/null @@ -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} diff --git a/snippets/tpl_suite.tex b/snippets/tpl_suite.tex deleted file mode 100644 index 24ed5e1..0000000 --- a/snippets/tpl_suite.tex +++ /dev/null @@ -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} From 1ce68931d67a6d43b7cc1bf7fe04872e3ac904c7 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:11:26 +0200 Subject: [PATCH 03/76] Feat: add click in dependencie --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b8bc0fc..5c2f353 100644 --- a/setup.py +++ b/setup.py @@ -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,6 +13,7 @@ setup( install_requires=[ 'mapytex', 'mypytex', + 'click', ], entry_points={ "console_scripts": ['bopytex=bopytex.script:new'] From 84f0a3a52123536d7406489f317daad4e8d571d0 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:18:47 +0200 Subject: [PATCH 04/76] Feat: add .drone for publishing in pypi --- .drone.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .drone.yml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..ec8afa8 --- /dev/null +++ b/.drone.yml @@ -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 From 49c2e505d04af1d6e03f7b2834b79277faf82667 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:20:43 +0200 Subject: [PATCH 05/76] Feat: add badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 33f233d..4a5a481 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Bopytex +[![Build Status](https://drone.opytex.org/api/badges/lafrite/Bopytex/status.svg)](https://drone.opytex.org/lafrite/Bopytex) + Bopytex is a command line tool for producing random math exercises with their correction. It embeds [mapytex](https://git.opytex.org/lafrite/Mapytex) and [python](python.org) into [latex](latex-project.org) through [jinja](jinja.pocoo.org). ## Installing From d8c758677d50203347a8e12955ef115941284379 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:21:32 +0200 Subject: [PATCH 06/76] Fix: change trigger --- .drone.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.yml b/.drone.yml index ec8afa8..5d904cc 100644 --- a/.drone.yml +++ b/.drone.yml @@ -10,5 +10,5 @@ steps: password: from_secret: pypi_password trigger: - event: - - tag + ref: + - refs/tags/* From 4db2b1ce73fe0ebe3bb67603358eaab78f692e07 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 20 Aug 2020 16:27:17 +0200 Subject: [PATCH 07/76] Fix: only on tagging? --- .drone.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.drone.yml b/.drone.yml index 5d904cc..80ec6ad 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,6 +9,5 @@ steps: from_secret: pypi_username password: from_secret: pypi_password - trigger: - ref: - - refs/tags/* + when: + event: tag From 7782330ed28e47f722916c4c8471bea7e5307887 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 21 Aug 2020 19:29:32 +0200 Subject: [PATCH 08/76] Feat: activate all mapytex --- bopytex/bopytex.py | 24 +++++++++++------------- setup.py | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index d324c59..24fefa4 100755 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -11,7 +11,8 @@ import logging from pathlib import Path import pytex -from mapytex import Expression, Integer, Decimal +#from mapytex import Expression, Integer, Decimal, random_list +import mapytex import bopytex.filters as filters formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") @@ -24,19 +25,16 @@ logger.addHandler(steam_handler) def setup(): - Expression.set_render("tex") - logger.debug(f"Render for Expression is {Expression.RENDER}") + mapytex.Expression.set_render("tex") + logger.debug(f"Render for Expression is {mapytex.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, + "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) diff --git a/setup.py b/setup.py index 5c2f353..b1cddb8 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup setup( name='Bopytex', - version='0.1.2', + version='0.2', description='Command line tool for compiling latex with python command embedded', author='Benjamin Bertrand', author_email='programming@opytex.org', From 5f0fc8bc4248f2e2011b290388bc6c504249fc23 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 3 Nov 2020 10:23:18 +0100 Subject: [PATCH 09/76] Feat: only_corr works --- bopytex/bopytex.py | 21 +++++++++++++----- bopytex/script.py | 53 +++++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index 24fefa4..6235e71 100755 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -60,10 +60,19 @@ def get_working_dir(options): def activate_printanswers( - texfile, noans=r"solution/print = false", ans=r"solution/print = true" + texfile, + noans=r"%\printsolutionstype{exercise}", + ans=r"\printsolutionstype{exercise}", + corr_prefix="corr_" ): - """ Activate printanswers mod in texfile """ - output_fname = "corr_" + texfile + """ 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(): @@ -120,7 +129,8 @@ def subject_metadatas(options): d.update(s) metadatas.append(d) elif options["number_subjects"] > 0: - metadatas = [{"num": f"{i+1:02d}"} for i in range(options["number_subjects"])] + metadatas = [{"num": f"{i+1:02d}"} + for i in range(options["number_subjects"])] else: raise ValueError("Need metacsv or quantity to build subject metadata") @@ -222,7 +232,8 @@ def produce_and_compile(options): else: pdfjoin( pdfs, - template.replace("tpl", prefix+"all").replace(".tex", ".pdf"), + template.replace( + "tpl", prefix+"all").replace(".tex", ".pdf"), directory, rm_pdfs=1, ) diff --git a/bopytex/script.py b/bopytex/script.py index 5dfc097..73e60da 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -15,6 +15,7 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) logger.addHandler(steam_handler) + @click.command() @click.argument( "template", @@ -26,7 +27,6 @@ logger.addHandler(steam_handler) "-w", "--working-dir", type=click.Path(exists=True), - help="Where fed templates and compiled files will be placed", ) @click.option( "-s", @@ -64,7 +64,7 @@ logger.addHandler(steam_handler) "--only-corr", is_flag=True, default=False, - help="Create and compile only correction from existing subjects", + help="Activate correction and compile only from existing subjects", ) @click.option( "-c", @@ -96,32 +96,38 @@ def new(**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 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"]: + 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"]: - for prefix in ["", "corr_"]: + if options["only_corr"]: + to_compile = ["corr_"] + else: + to_compile = ["", "corr_"] + + for prefix in to_compile: key = prefix + "texfile" try: meta[key] @@ -143,7 +149,8 @@ def new(**options): else: pdfjoin( pdfs, - template.replace("tpl", prefix+"all").replace(".tex", ".pdf"), + template.replace( + "tpl", prefix+"all").replace(".tex", ".pdf"), directory, rm_pdfs=1, ) @@ -151,8 +158,6 @@ def new(**options): if not options["dirty"]: clean(directory) - # produce_and_compile(options) - if __name__ == "__main__": new() From ce98f46bca7e5f26268d3d83d0c407d3b50fe17a Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 19:34:22 +0200 Subject: [PATCH 10/76] Feat: clean repository --- bopytex/__init__.py | 4 - bopytex/filters.py | 33 ------ bopytex/lib/pythagore.py | 38 ------ bopytex/macros/poly2Deg.tex | 33 ------ examples/tpl_add_fraction.tex | 19 --- test/students.csv | 6 - test/templates/tpl_test.tex | 22 ---- test/test_bopytex.py | 212 ---------------------------------- 8 files changed, 367 deletions(-) delete mode 100644 bopytex/filters.py delete mode 100644 bopytex/lib/pythagore.py delete mode 100644 bopytex/macros/poly2Deg.tex delete mode 100644 examples/tpl_add_fraction.tex delete mode 100644 test/students.csv delete mode 100644 test/templates/tpl_test.tex delete mode 100644 test/test_bopytex.py diff --git a/bopytex/__init__.py b/bopytex/__init__.py index 1306988..acfc5bd 100644 --- a/bopytex/__init__.py +++ b/bopytex/__init__.py @@ -1,10 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 - -#from .bopytex import subject_metadatas, crazy_feed, pdfjoin - - # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: diff --git a/bopytex/filters.py b/bopytex/filters.py deleted file mode 100644 index 395794e..0000000 --- a/bopytex/filters.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -""" -Custom filter for Bopytex -""" - -__all__ = ["do_calculus"] - -def do_calculus(steps, name="A", sep="=", end="", joining=" \\\\ \n"): - """Display properly the calculus - - Generate this form string: - "name & sep & a_step end joining" - - :param steps: list of steps - :returns: latex string ready to be endbeded - - - """ - - ans = joining.join([ - name + " & " - + sep + " & " - + str(s) + end for s in steps - ]) - return ans - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/bopytex/lib/pythagore.py b/bopytex/lib/pythagore.py deleted file mode 100644 index 9eede1b..0000000 --- a/bopytex/lib/pythagore.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -from random import randint - -def pythagore_triplet(v_min = 1, v_max = 10): - """Random pythagore triplet generator - - :param v_min: minimum in randint - :param v_max: max in randint - :returns: (a,b,c) such that a^2 + b^2 = c^2 - - """ - u = randint(v_min,v_max) - v = randint(v_min,v_max) - while v == u: - v = randint(v_min,v_max) - - u, v = max(u,v), min(u,v) - - return (u**2-v**2 , 2*u*v, u**2 + v**2) - -if __name__ == '__main__': - print(pythagore_triplet()) - - for j in range(1,10): - for i in range(j,10): - print((i**2-j**2 , 2*i*j, i**2 + j**2)) - - - - - - -# ----------------------------- -# Reglages pour 'vim' -# vim:set autoindent expandtab tabstop=4 shiftwidth=4: -# cursor: 16 del diff --git a/bopytex/macros/poly2Deg.tex b/bopytex/macros/poly2Deg.tex deleted file mode 100644 index 5c667e6..0000000 --- a/bopytex/macros/poly2Deg.tex +++ /dev/null @@ -1,33 +0,0 @@ -\Block{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} - -\Block{endmacro} diff --git a/examples/tpl_add_fraction.tex b/examples/tpl_add_fraction.tex deleted file mode 100644 index 5285bd4..0000000 --- a/examples/tpl_add_fraction.tex +++ /dev/null @@ -1,19 +0,0 @@ -% vim:ft=tex: -% -\documentclass[12pt]{article} - -\begin{document} - -\section{Ajouts de fractions} - -Adding two fractions -%- set e = Expression.random("{a} / {b} + {c} / {k*b}", ["b > 1", "k>1"]) -\[ - A = \Var{e} -\] -Solution -\[ - \Var{e.simplify().explain() | join('=')} -\] - -\end{document} diff --git a/test/students.csv b/test/students.csv deleted file mode 100644 index 5ddf91d..0000000 --- a/test/students.csv +++ /dev/null @@ -1,6 +0,0 @@ -nom,classe,elo -Bob,1ST,1000 -Pipo,1ST,1300 -Popi,1ST,100 -Boule,1ST,4000 -Bill,1ST,1300 diff --git a/test/templates/tpl_test.tex b/test/templates/tpl_test.tex deleted file mode 100644 index dc3183b..0000000 --- a/test/templates/tpl_test.tex +++ /dev/null @@ -1,22 +0,0 @@ -\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} diff --git a/test/test_bopytex.py b/test/test_bopytex.py deleted file mode 100644 index 078777a..0000000 --- a/test/test_bopytex.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/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 From 6e718b987aa8addfb6e2baf69a8848ef7ceab6b9 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 19:34:38 +0200 Subject: [PATCH 11/76] Feat: code planners and tests --- bopytex/actions.py | 27 +++++++++++++++++++ bopytex/planner.py | 63 ++++++++++++++++++++++++++++++++++++++++++++ test/__init__.py | 0 test/test_planner.py | 46 ++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 bopytex/actions.py create mode 100644 bopytex/planner.py create mode 100644 test/__init__.py create mode 100644 test/test_planner.py diff --git a/bopytex/actions.py b/bopytex/actions.py new file mode 100644 index 0000000..89805a4 --- /dev/null +++ b/bopytex/actions.py @@ -0,0 +1,27 @@ +def generate(): + pass + + +def compile(): + pass + + +def activate_corr(): + pass + + +def join_pdf(): + pass + + +def clean(): + pass + + +ACTIONS = { + "GENERATE": generate, + "COMPILE": compile, + "ACTIVATE_CORR": activate_corr, + "JOIN_PDF": join_pdf, + "clean": clean, +} diff --git a/bopytex/planner.py b/bopytex/planner.py new file mode 100644 index 0000000..0818129 --- /dev/null +++ b/bopytex/planner.py @@ -0,0 +1,63 @@ +""" Produce tasks to do + +It essentially place things at the right place and define the way that files are named. + +""" +from dataclasses import dataclass + + +@dataclass +class Task: + action: str + args: dict + deps: list + output: str + + +def generate(template: str, meta: dict): + """Create a task to generate a subject""" + return Task( + action="GENERATE", + args=meta, + deps=[template], + output=meta["subject"] + template[3:], + ) + + +def activate_corr_on(src: str): + """Create a task to activate correction for src""" + return Task( + action="ACTIVATE_CORR", + args={}, + deps=[src], + output="corr_" + src, + ) + + +def compile_pdf(src: str): + """Create a task to compile src""" + return Task( + action="COMPILE", + args={}, + deps=[src], + output=src[:-3] + "pdf" + ) + +def join_pdfs(pdfs: list): + """ Create task to join pdf together """ + return Task( + action="JOIN", + args={}, + deps=pdfs, + output="joined.pdf" + ) + + +def clean(files: list): + """ Create task to clean files""" + return Task( + action="CLEAN", + args={}, + deps=files, + output=None + ) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_planner.py b/test/test_planner.py new file mode 100644 index 0000000..77b491c --- /dev/null +++ b/test/test_planner.py @@ -0,0 +1,46 @@ +from bopytex.planner import activate_corr_on, clean, compile_pdf, generate, join_pdfs + + +def test_build_task_generate(): + template = "tpl_source.tex" + task = generate(template, {"subject": "01"}) + assert task.action == "GENERATE" + assert task.args == {"subject": "01"} + assert task.deps == [template] + assert task.output == "01_source.tex" + + +def test_build_task_activate_corr_on(): + src = "source.tex" + task = activate_corr_on(src) + assert task.action == "ACTIVATE_CORR" + assert task.args == {} + assert task.deps == [src] + assert task.output == "corr_source.tex" + + +def test_build_task_compile(): + src = "source.tex" + task = compile_pdf(src) + assert task.action == "COMPILE" + assert task.args == {} + assert task.deps == [src] + assert task.output == "source.pdf" + + +def test_build_task_join(): + pdfs = [f"{i}_source.pdf" for i in range(3)] + task = join_pdfs(pdfs) + assert task.action == "JOIN" + assert task.args == {} + assert task.deps == pdfs + assert task.output == "joined.pdf" + + +def test_build_task_compile(): + files = ["source.aux", "source.log"] + task = clean(files) + assert task.action == "CLEAN" + assert task.args == {} + assert task.deps == files + assert task.output is None From 7b64dcf8d62184cf9203740a14914f368e200375 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 21:24:43 +0200 Subject: [PATCH 12/76] Feat: append, dispatch and __next__ (basic) scheduler --- bopytex/scheduler.py | 29 +++++++++++++++++++++++++++++ test/test_scheduler.py | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 bopytex/scheduler.py create mode 100644 test/test_scheduler.py diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py new file mode 100644 index 0000000..fe651c3 --- /dev/null +++ b/bopytex/scheduler.py @@ -0,0 +1,29 @@ +""" Scheduler for action to make """ + + +class Scheduler: + def __init__(self, actions: list): + self.actions = actions + self._tasks = [] + self._done = [] + + @property + def tasks(self): + return self._tasks + + @property + def done(self): + return self._done + + def append(self, tasks): + self._tasks += tasks + + def dispatch(self, task): + """ Do a task """ + ans = self.actions[task.action](task.deps) + return ans + + def __next__(self): + task = self._tasks.pop() + self._done.append(task.output) + return self.dispatch(task) diff --git a/test/test_scheduler.py b/test/test_scheduler.py new file mode 100644 index 0000000..c9be4f6 --- /dev/null +++ b/test/test_scheduler.py @@ -0,0 +1,27 @@ +from bopytex.planner import Task +from bopytex.scheduler import Scheduler + + +def test_schedule_append(): + actions = {"DO": lambda _: "done"} + scheduler = Scheduler(actions) + tasks = [Task(action="DO", args={}, deps=[], output="")] + scheduler.append(tasks) + assert scheduler.tasks == tasks + +def test_schedule_dispatch(): + actions = {"DO": lambda _: "done"} + scheduler = Scheduler(actions) + task = Task(action="DO", args={}, deps=[], output="Nothing") + result = scheduler.dispatch(task) + assert result == "done" + + +def test_schedule_one_task(): + actions = {"DO": lambda _: "done"} + scheduler = Scheduler(actions) + scheduler.append([Task(action="DO", args={}, deps=[], output="Nothing")]) + result = scheduler.__next__() + assert result == "done" + assert scheduler.tasks == [] + assert scheduler.done == ["Nothing"] From 480ced3259e26c7ac8ec1a4a20263054a1a3882b Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 21:29:35 +0200 Subject: [PATCH 13/76] refact: remove output to tasks --- bopytex/planner.py | 8 +------- bopytex/scheduler.py | 5 +++-- test/test_planner.py | 5 ----- test/test_scheduler.py | 8 ++++---- 4 files changed, 8 insertions(+), 18 deletions(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index 0818129..f564fd3 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -1,6 +1,6 @@ """ Produce tasks to do -It essentially place things at the right place and define the way that files are named. +It essentially place things at the right place. """ from dataclasses import dataclass @@ -11,7 +11,6 @@ class Task: action: str args: dict deps: list - output: str def generate(template: str, meta: dict): @@ -20,7 +19,6 @@ def generate(template: str, meta: dict): action="GENERATE", args=meta, deps=[template], - output=meta["subject"] + template[3:], ) @@ -30,7 +28,6 @@ def activate_corr_on(src: str): action="ACTIVATE_CORR", args={}, deps=[src], - output="corr_" + src, ) @@ -40,7 +37,6 @@ def compile_pdf(src: str): action="COMPILE", args={}, deps=[src], - output=src[:-3] + "pdf" ) def join_pdfs(pdfs: list): @@ -49,7 +45,6 @@ def join_pdfs(pdfs: list): action="JOIN", args={}, deps=pdfs, - output="joined.pdf" ) @@ -59,5 +54,4 @@ def clean(files: list): action="CLEAN", args={}, deps=files, - output=None ) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index fe651c3..1fadec7 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -25,5 +25,6 @@ class Scheduler: def __next__(self): task = self._tasks.pop() - self._done.append(task.output) - return self.dispatch(task) + ans = self.dispatch(task) + self._done.append(ans) + return ans diff --git a/test/test_planner.py b/test/test_planner.py index 77b491c..f45ae96 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -7,7 +7,6 @@ def test_build_task_generate(): assert task.action == "GENERATE" assert task.args == {"subject": "01"} assert task.deps == [template] - assert task.output == "01_source.tex" def test_build_task_activate_corr_on(): @@ -16,7 +15,6 @@ def test_build_task_activate_corr_on(): assert task.action == "ACTIVATE_CORR" assert task.args == {} assert task.deps == [src] - assert task.output == "corr_source.tex" def test_build_task_compile(): @@ -25,7 +23,6 @@ def test_build_task_compile(): assert task.action == "COMPILE" assert task.args == {} assert task.deps == [src] - assert task.output == "source.pdf" def test_build_task_join(): @@ -34,7 +31,6 @@ def test_build_task_join(): assert task.action == "JOIN" assert task.args == {} assert task.deps == pdfs - assert task.output == "joined.pdf" def test_build_task_compile(): @@ -43,4 +39,3 @@ def test_build_task_compile(): assert task.action == "CLEAN" assert task.args == {} assert task.deps == files - assert task.output is None diff --git a/test/test_scheduler.py b/test/test_scheduler.py index c9be4f6..e994d50 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -5,14 +5,14 @@ from bopytex.scheduler import Scheduler def test_schedule_append(): actions = {"DO": lambda _: "done"} scheduler = Scheduler(actions) - tasks = [Task(action="DO", args={}, deps=[], output="")] + tasks = [Task(action="DO", args={}, deps=[])] scheduler.append(tasks) assert scheduler.tasks == tasks def test_schedule_dispatch(): actions = {"DO": lambda _: "done"} scheduler = Scheduler(actions) - task = Task(action="DO", args={}, deps=[], output="Nothing") + task = Task(action="DO", args={}, deps=[]) result = scheduler.dispatch(task) assert result == "done" @@ -20,8 +20,8 @@ def test_schedule_dispatch(): def test_schedule_one_task(): actions = {"DO": lambda _: "done"} scheduler = Scheduler(actions) - scheduler.append([Task(action="DO", args={}, deps=[], output="Nothing")]) + scheduler.append([Task(action="DO", args={}, deps=[])]) result = scheduler.__next__() assert result == "done" assert scheduler.tasks == [] - assert scheduler.done == ["Nothing"] + assert scheduler.done == ["done"] From 4dbfe7a82cfe5c3d84e5e37989afdb3558f23e54 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 21:35:55 +0200 Subject: [PATCH 14/76] feat: dispatch transmit args to actions --- bopytex/scheduler.py | 2 +- test/test_scheduler.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 1fadec7..3b39bd3 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -20,7 +20,7 @@ class Scheduler: def dispatch(self, task): """ Do a task """ - ans = self.actions[task.action](task.deps) + ans = self.actions[task.action](task.deps, task.args) return ans def __next__(self): diff --git a/test/test_scheduler.py b/test/test_scheduler.py index e994d50..ce7b1c6 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -3,14 +3,15 @@ from bopytex.scheduler import Scheduler def test_schedule_append(): - actions = {"DO": lambda _: "done"} + actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) tasks = [Task(action="DO", args={}, deps=[])] scheduler.append(tasks) assert scheduler.tasks == tasks + def test_schedule_dispatch(): - actions = {"DO": lambda _: "done"} + actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) task = Task(action="DO", args={}, deps=[]) result = scheduler.dispatch(task) @@ -18,10 +19,20 @@ def test_schedule_dispatch(): def test_schedule_one_task(): - actions = {"DO": lambda _: "done"} + actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) scheduler.append([Task(action="DO", args={}, deps=[])]) result = scheduler.__next__() assert result == "done" assert scheduler.tasks == [] assert scheduler.done == ["done"] + + +def test_schedule_one_task_with_args(): + actions = {"DO": lambda deps, args: f"{args['task']} done"} + scheduler = Scheduler(actions) + scheduler.append([Task(action="DO", args={"task": "one"}, deps=[])]) + result = scheduler.__next__() + assert result == "one done" + assert scheduler.tasks == [] + assert scheduler.done == ["one done"] From bfe9e6f91e08cf42a6b5cbf3735f07b62f9866a3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 21:41:29 +0200 Subject: [PATCH 15/76] feat: schelule multiple task --- bopytex/scheduler.py | 2 +- test/test_scheduler.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 3b39bd3..db141c0 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -24,7 +24,7 @@ class Scheduler: return ans def __next__(self): - task = self._tasks.pop() + task = self._tasks.pop(0) ans = self.dispatch(task) self._done.append(ans) return ans diff --git a/test/test_scheduler.py b/test/test_scheduler.py index ce7b1c6..183a878 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -36,3 +36,26 @@ def test_schedule_one_task_with_args(): assert result == "one done" assert scheduler.tasks == [] assert scheduler.done == ["one done"] + + +def test_schedule_multiple_tasks(): + actions = {"DO": lambda deps, args: f"{args['task']} done"} + scheduler = Scheduler(actions) + t1 = Task(action="DO", args={"task": "one"}, deps=[]) + t2 = Task(action="DO", args={"task": "two"}, deps=[]) + t3 = Task(action="DO", args={"task": "three"}, deps=[]) + scheduler.append([t1, t2, t3]) + result = scheduler.__next__() + assert result == "one done" + assert scheduler.tasks == [t2, t3] + assert scheduler.done == ["one done"] + + result = scheduler.__next__() + assert result == "two done" + assert scheduler.tasks == [t3] + assert scheduler.done == ["one done", "two done"] + + result = scheduler.__next__() + assert result == "three done" + assert scheduler.tasks == [] + assert scheduler.done == ["one done", "two done", "three done"] From e0377a3e92663f6ab9a8c6ffd405031db6ed0096 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 21:49:18 +0200 Subject: [PATCH 16/76] Feat: scheduler handle dependencies (basic) --- bopytex/scheduler.py | 9 ++++++++- test/test_scheduler.py | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index db141c0..db00b12 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -19,12 +19,19 @@ class Scheduler: self._tasks += tasks def dispatch(self, task): - """ Do a task """ + """Do a task""" ans = self.actions[task.action](task.deps, task.args) return ans def __next__(self): + undoable = [] + task = self._tasks.pop(0) + while not all([d in self.done for d in task.deps]): + undoable.append(task) + task = self._tasks.pop(0) + + self.append(undoable) ans = self.dispatch(task) self._done.append(ans) return ans diff --git a/test/test_scheduler.py b/test/test_scheduler.py index 183a878..a79c764 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -59,3 +59,28 @@ def test_schedule_multiple_tasks(): assert result == "three done" assert scheduler.tasks == [] assert scheduler.done == ["one done", "two done", "three done"] + + +def test_schedule_multiple_tasks_with_dependencies(): + actions = {"DO": lambda deps, args: f"{args['task']} done"} + scheduler = Scheduler(actions) + t1 = Task(action="DO", args={"task": "one"}, deps=["three done"]) + t2 = Task(action="DO", args={"task": "two"}, deps=["one done"]) + t3 = Task(action="DO", args={"task": "three"}, deps=[]) + scheduler.append([t1, t2, t3]) + + result = scheduler.__next__() + assert result == "three done" + assert scheduler.tasks == [t1, t2] + assert scheduler.done == ["three done"] + + result = scheduler.__next__() + assert result == "one done" + assert scheduler.tasks == [t2] + assert scheduler.done == ["three done", "one done"] + + result = scheduler.__next__() + assert result == "two done" + assert scheduler.tasks == [] + assert scheduler.done == ["three done", "one done", "two done"] + From 2b0c325203410df502004669ec86cf249bf58837 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Fri, 8 Apr 2022 22:04:47 +0200 Subject: [PATCH 17/76] feat: run the scheduler --- bopytex/scheduler.py | 24 ++++++++++++++++++++++-- test/test_scheduler.py | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index db00b12..8155f1c 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -1,6 +1,9 @@ """ Scheduler for action to make """ +from logging import exception + + class Scheduler: def __init__(self, actions: list): self.actions = actions @@ -23,15 +26,32 @@ class Scheduler: ans = self.actions[task.action](task.deps, task.args) return ans + def __iter__(self): + return self def __next__(self): + return self.next() + + def next(self): undoable = [] - task = self._tasks.pop(0) + try: + task = self._tasks.pop(0) + except IndexError: + raise StopIteration + while not all([d in self.done for d in task.deps]): undoable.append(task) - task = self._tasks.pop(0) + try: + task = self._tasks.pop(0) + except IndexError: + self.append(undoable) + raise StopIteration self.append(undoable) ans = self.dispatch(task) self._done.append(ans) return ans + + def run(self): + for i in self: + pass diff --git a/test/test_scheduler.py b/test/test_scheduler.py index a79c764..9176e6e 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -1,5 +1,6 @@ from bopytex.planner import Task from bopytex.scheduler import Scheduler +import pytest def test_schedule_append(): @@ -22,7 +23,7 @@ def test_schedule_one_task(): actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) scheduler.append([Task(action="DO", args={}, deps=[])]) - result = scheduler.__next__() + result = scheduler.next() assert result == "done" assert scheduler.tasks == [] assert scheduler.done == ["done"] @@ -32,7 +33,7 @@ def test_schedule_one_task_with_args(): actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) scheduler.append([Task(action="DO", args={"task": "one"}, deps=[])]) - result = scheduler.__next__() + result = scheduler.next() assert result == "one done" assert scheduler.tasks == [] assert scheduler.done == ["one done"] @@ -45,17 +46,17 @@ def test_schedule_multiple_tasks(): t2 = Task(action="DO", args={"task": "two"}, deps=[]) t3 = Task(action="DO", args={"task": "three"}, deps=[]) scheduler.append([t1, t2, t3]) - result = scheduler.__next__() + result = scheduler.next() assert result == "one done" assert scheduler.tasks == [t2, t3] assert scheduler.done == ["one done"] - result = scheduler.__next__() + result = scheduler.next() assert result == "two done" assert scheduler.tasks == [t3] assert scheduler.done == ["one done", "two done"] - result = scheduler.__next__() + result = scheduler.next() assert result == "three done" assert scheduler.tasks == [] assert scheduler.done == ["one done", "two done", "three done"] @@ -69,18 +70,37 @@ def test_schedule_multiple_tasks_with_dependencies(): t3 = Task(action="DO", args={"task": "three"}, deps=[]) scheduler.append([t1, t2, t3]) - result = scheduler.__next__() + result = scheduler.next() assert result == "three done" assert scheduler.tasks == [t1, t2] assert scheduler.done == ["three done"] - result = scheduler.__next__() + result = scheduler.next() assert result == "one done" assert scheduler.tasks == [t2] assert scheduler.done == ["three done", "one done"] - result = scheduler.__next__() + result = scheduler.next() assert result == "two done" assert scheduler.tasks == [] assert scheduler.done == ["three done", "one done", "two done"] + +def test_schedule_empty_task(): + actions = {"DO": lambda deps, args: f"{args['task']} done"} + scheduler = Scheduler(actions) + scheduler.append([]) + with pytest.raises(StopIteration): + scheduler.next() + + +def test_schedule_multiple_tasks_with_undoable_dependencies(): + actions = {"DO": lambda deps, args: f"{args['task']} done"} + scheduler = Scheduler(actions) + t1 = Task(action="DO", args={"task": "one"}, deps=["three done"]) + t2 = Task(action="DO", args={"task": "two"}, deps=[]) + scheduler.append([t1, t2]) + scheduler.run() + assert scheduler.tasks == [t1] + assert scheduler.done == ["two done"] + From 211cee2f4f482190e7476f2055333157d5d88d68 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 06:13:40 +0200 Subject: [PATCH 18/76] Fix: linting --- bopytex/scheduler.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 8155f1c..7301381 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -1,9 +1,6 @@ """ Scheduler for action to make """ -from logging import exception - - class Scheduler: def __init__(self, actions: list): self.actions = actions @@ -28,6 +25,7 @@ class Scheduler: def __iter__(self): return self + def __next__(self): return self.next() @@ -53,5 +51,5 @@ class Scheduler: return ans def run(self): - for i in self: + for _ in self: pass From 4d76dc89929b309e368371626de83345076a3d12 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 06:32:56 +0200 Subject: [PATCH 19/76] Feat: reactivate output in task --- bopytex/planner.py | 19 +++++++---- bopytex/scheduler.py | 4 +-- test/test_planner.py | 10 +++--- test/test_scheduler.py | 76 ++++++++++++++++++++---------------------- 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index f564fd3..5244f20 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -11,47 +11,54 @@ class Task: action: str args: dict deps: list + output: str -def generate(template: str, meta: dict): +def generate(template: str, meta: dict, output: str): """Create a task to generate a subject""" return Task( action="GENERATE", args=meta, deps=[template], + output=output, ) -def activate_corr_on(src: str): +def activate_corr_on(src: str, output: str): """Create a task to activate correction for src""" return Task( action="ACTIVATE_CORR", args={}, deps=[src], + output=output, ) -def compile_pdf(src: str): +def compile_pdf(src: str, output: str): """Create a task to compile src""" return Task( action="COMPILE", args={}, deps=[src], + output=output, ) -def join_pdfs(pdfs: list): - """ Create task to join pdf together """ + +def join_pdfs(pdfs: list, output: str): + """Create task to join pdf together""" return Task( action="JOIN", args={}, deps=pdfs, + output=output, ) def clean(files: list): - """ Create task to clean files""" + """Create task to clean files""" return Task( action="CLEAN", args={}, deps=files, + output=None, ) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 7301381..0f66917 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -20,7 +20,7 @@ class Scheduler: def dispatch(self, task): """Do a task""" - ans = self.actions[task.action](task.deps, task.args) + ans = self.actions[task.action](task.deps, task.args, task.output) return ans def __iter__(self): @@ -47,7 +47,7 @@ class Scheduler: self.append(undoable) ans = self.dispatch(task) - self._done.append(ans) + self._done.append(task.output) return ans def run(self): diff --git a/test/test_planner.py b/test/test_planner.py index f45ae96..e640ee6 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -3,7 +3,7 @@ from bopytex.planner import activate_corr_on, clean, compile_pdf, generate, join def test_build_task_generate(): template = "tpl_source.tex" - task = generate(template, {"subject": "01"}) + task = generate(template, {"subject": "01"}, output="source.tex") assert task.action == "GENERATE" assert task.args == {"subject": "01"} assert task.deps == [template] @@ -11,7 +11,7 @@ def test_build_task_generate(): def test_build_task_activate_corr_on(): src = "source.tex" - task = activate_corr_on(src) + task = activate_corr_on(src, output="corr_source.tex") assert task.action == "ACTIVATE_CORR" assert task.args == {} assert task.deps == [src] @@ -19,7 +19,7 @@ def test_build_task_activate_corr_on(): def test_build_task_compile(): src = "source.tex" - task = compile_pdf(src) + task = compile_pdf(src, output="source.pdf") assert task.action == "COMPILE" assert task.args == {} assert task.deps == [src] @@ -27,13 +27,13 @@ def test_build_task_compile(): def test_build_task_join(): pdfs = [f"{i}_source.pdf" for i in range(3)] - task = join_pdfs(pdfs) + task = join_pdfs(pdfs, output="joined.pdf") assert task.action == "JOIN" assert task.args == {} assert task.deps == pdfs -def test_build_task_compile(): +def test_build_task_clean(): files = ["source.aux", "source.log"] task = clean(files) assert task.action == "CLEAN" diff --git a/test/test_scheduler.py b/test/test_scheduler.py index 9176e6e..0a96a04 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -3,91 +3,91 @@ from bopytex.scheduler import Scheduler import pytest +def action_done(deps, args, output): + return f"{deps} - {args} - {output} - done" + + +actions = {"DO": action_done} + + def test_schedule_append(): - actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) - tasks = [Task(action="DO", args={}, deps=[])] + tasks = [Task(action="DO", args={}, deps=[], output="end")] scheduler.append(tasks) assert scheduler.tasks == tasks def test_schedule_dispatch(): - actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) - task = Task(action="DO", args={}, deps=[]) + task = Task(action="DO", args={}, deps=[], output="end") result = scheduler.dispatch(task) - assert result == "done" + assert result == "[] - {} - end - done" def test_schedule_one_task(): - actions = {"DO": lambda deps, args: "done"} scheduler = Scheduler(actions) - scheduler.append([Task(action="DO", args={}, deps=[])]) + scheduler.append([Task(action="DO", args={}, deps=[], output="end")]) result = scheduler.next() - assert result == "done" + assert result == "[] - {} - end - done" assert scheduler.tasks == [] - assert scheduler.done == ["done"] + assert scheduler.done == ["end"] def test_schedule_one_task_with_args(): - actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) - scheduler.append([Task(action="DO", args={"task": "one"}, deps=[])]) + scheduler.append([Task(action="DO", args={"task": "one"}, deps=[], output="one")]) result = scheduler.next() - assert result == "one done" + assert result == "[] - {'task': 'one'} - one - done" assert scheduler.tasks == [] - assert scheduler.done == ["one done"] + assert scheduler.done == ["one"] def test_schedule_multiple_tasks(): - actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=[]) - t2 = Task(action="DO", args={"task": "two"}, deps=[]) - t3 = Task(action="DO", args={"task": "three"}, deps=[]) + t1 = Task(action="DO", args={"task": "one"}, deps=[], output="one") + t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") + t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) result = scheduler.next() - assert result == "one done" + assert result == "[] - {'task': 'one'} - one - done" assert scheduler.tasks == [t2, t3] - assert scheduler.done == ["one done"] + assert scheduler.done == ["one"] result = scheduler.next() - assert result == "two done" + assert result == "[] - {'task': 'two'} - two - done" assert scheduler.tasks == [t3] - assert scheduler.done == ["one done", "two done"] + assert scheduler.done == ["one", "two"] result = scheduler.next() - assert result == "three done" + assert result == "[] - {'task': 'three'} - three - done" assert scheduler.tasks == [] - assert scheduler.done == ["one done", "two done", "three done"] + assert scheduler.done == ["one", "two", "three"] def test_schedule_multiple_tasks_with_dependencies(): - actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=["three done"]) - t2 = Task(action="DO", args={"task": "two"}, deps=["one done"]) - t3 = Task(action="DO", args={"task": "three"}, deps=[]) + t1 = Task(action="DO", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="DO", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) result = scheduler.next() - assert result == "three done" + assert result == "[] - {'task': 'three'} - three - done" assert scheduler.tasks == [t1, t2] - assert scheduler.done == ["three done"] + assert scheduler.done == ["three"] result = scheduler.next() - assert result == "one done" + assert result == "['three'] - {'task': 'one'} - one - done" assert scheduler.tasks == [t2] - assert scheduler.done == ["three done", "one done"] + assert scheduler.done == ["three", "one"] result = scheduler.next() - assert result == "two done" + assert result == "['one'] - {'task': 'two'} - two - done" assert scheduler.tasks == [] - assert scheduler.done == ["three done", "one done", "two done"] + assert scheduler.done == ["three", "one", "two"] def test_schedule_empty_task(): - actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) scheduler.append([]) with pytest.raises(StopIteration): @@ -95,12 +95,10 @@ def test_schedule_empty_task(): def test_schedule_multiple_tasks_with_undoable_dependencies(): - actions = {"DO": lambda deps, args: f"{args['task']} done"} scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=["three done"]) - t2 = Task(action="DO", args={"task": "two"}, deps=[]) + t1 = Task(action="DO", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") scheduler.append([t1, t2]) scheduler.run() assert scheduler.tasks == [t1] - assert scheduler.done == ["two done"] - + assert scheduler.done == ["two"] From 32c74ae6792c6fcdde6b0544ab64c34934972202 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 06:56:57 +0200 Subject: [PATCH 20/76] Feat: write is_finishable --- bopytex/scheduler.py | 25 ++++++++++++++++++++++--- test/test_scheduler.py | 15 ++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 0f66917..a770c09 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -1,21 +1,37 @@ """ Scheduler for action to make """ +from bopytex.planner import Task + + class Scheduler: - def __init__(self, actions: list): + def __init__(self, actions: list, done: list[str] = None): self.actions = actions + + if done is None: + self._done = [] + else: + self._done = done + self._tasks = [] - self._done = [] @property def tasks(self): return self._tasks + @property + def all_deps(self): + return {d for task in self.tasks for d in task.deps} + + @property + def all_output(self): + return {task.output for task in self.tasks} + @property def done(self): return self._done - def append(self, tasks): + def append(self, tasks: list[Task]): self._tasks += tasks def dispatch(self, task): @@ -53,3 +69,6 @@ class Scheduler: def run(self): for _ in self: pass + + def is_finishable(self): + return self.all_deps.issubset(self.all_output) diff --git a/test/test_scheduler.py b/test/test_scheduler.py index 0a96a04..f960e0d 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -12,9 +12,14 @@ actions = {"DO": action_done} def test_schedule_append(): scheduler = Scheduler(actions) - tasks = [Task(action="DO", args={}, deps=[], output="end")] + tasks = [ + Task(action="DO", args={}, deps=["dep1", "dep2"], output="end1"), + Task(action="DO", args={}, deps=["dep1", "dep3"], output="end2"), + ] scheduler.append(tasks) assert scheduler.tasks == tasks + assert scheduler.all_deps == {"dep1", "dep2", "dep3"} + assert scheduler.all_output == {"end1", "end2"} def test_schedule_dispatch(): @@ -48,6 +53,9 @@ def test_schedule_multiple_tasks(): t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) + + assert scheduler.is_finishable() == True + result = scheduler.next() assert result == "[] - {'task': 'one'} - one - done" assert scheduler.tasks == [t2, t3] @@ -71,6 +79,8 @@ def test_schedule_multiple_tasks_with_dependencies(): t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) + assert scheduler.is_finishable() == True + result = scheduler.next() assert result == "[] - {'task': 'three'} - three - done" assert scheduler.tasks == [t1, t2] @@ -99,6 +109,9 @@ def test_schedule_multiple_tasks_with_undoable_dependencies(): t1 = Task(action="DO", args={"task": "one"}, deps=["three"], output="one") t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") scheduler.append([t1, t2]) + + assert scheduler.is_finishable() == False + scheduler.run() assert scheduler.tasks == [t1] assert scheduler.done == ["two"] From ebf5bd0c7d03f74f7bcbc29470a4cba5de400c7c Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 06:58:44 +0200 Subject: [PATCH 21/76] refact: rename planner to tasks --- bopytex/scheduler.py | 2 +- bopytex/{planner.py => tasks.py} | 0 test/test_scheduler.py | 2 +- test/{test_planner.py => test_tasks.py} | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename bopytex/{planner.py => tasks.py} (100%) rename test/{test_planner.py => test_tasks.py} (92%) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index a770c09..17cfc0b 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -1,7 +1,7 @@ """ Scheduler for action to make """ -from bopytex.planner import Task +from bopytex.tasks import Task class Scheduler: diff --git a/bopytex/planner.py b/bopytex/tasks.py similarity index 100% rename from bopytex/planner.py rename to bopytex/tasks.py diff --git a/test/test_scheduler.py b/test/test_scheduler.py index f960e0d..c3cf44a 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -1,4 +1,4 @@ -from bopytex.planner import Task +from bopytex.tasks import Task from bopytex.scheduler import Scheduler import pytest diff --git a/test/test_planner.py b/test/test_tasks.py similarity index 92% rename from test/test_planner.py rename to test/test_tasks.py index e640ee6..826fc92 100644 --- a/test/test_planner.py +++ b/test/test_tasks.py @@ -1,4 +1,4 @@ -from bopytex.planner import activate_corr_on, clean, compile_pdf, generate, join_pdfs +from bopytex.tasks import activate_corr_on, clean, compile_pdf, generate, join_pdfs def test_build_task_generate(): From 1865f9ec6303b1a299e4643cc4bcda46b62cc4c9 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 07:30:13 +0200 Subject: [PATCH 22/76] Feat: build subjects metadatas --- bopytex/bopytex.py | 251 ++++++------------------------------------- bopytex/script.py | 87 ++------------- test/test_bopytex.py | 29 +++++ 3 files changed, 68 insertions(+), 299 deletions(-) create mode 100644 test/test_bopytex.py 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"}, + ] From f9dd70a2f1c18e4b3db608c397e7781b3d454ee2 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 15:33:28 +0200 Subject: [PATCH 23/76] feat: planner for normal workflow --- bopytex/planner.py | 67 +++++++++++++ test/test_planner.py | 223 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 bopytex/planner.py create mode 100644 test/test_planner.py diff --git a/bopytex/planner.py b/bopytex/planner.py new file mode 100644 index 0000000..1d54f0e --- /dev/null +++ b/bopytex/planner.py @@ -0,0 +1,67 @@ +from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs + + +def naming_template2source(template: str, metadatas: dict): + return metadatas["number"] + template[3:] + + +def naming_corr(source): + return "corr_" + source + + +def naming_source2pdf(source): + return source[:-4] + ".pdf" + + +def naming_join(template): + return naming_source2pdf("joined" + template[3:]) + + +def planner( + template: str, + subjects: list[dict], + corr: bool = False, + no_join: bool = False, + no_pdf: bool = False, + # dirty: bool = False, +) -> list[Task]: + tasks = [] + + pdfs = [] + corr_pdfs = [] + + for subject in subjects: + source = naming_template2source(template, subject) + + tasks.append(generate(template, subject, source)) + + if not no_pdf: + pdf = naming_source2pdf(source) + tasks.append(compile_pdf(source, pdf)) + pdfs.append(pdf) + + if corr: + corr_source = naming_corr(source) + tasks.append(activate_corr_on(source, corr_source)) + + if not no_pdf: + corr_pdf = naming_source2pdf(corr_source) + tasks.append(compile_pdf(corr_source, corr_pdf)) + corr_pdfs.append(corr_pdf) + + if not no_join: + joined = naming_join(template) + if pdfs: + tasks.append(join_pdfs(pdfs, joined)) + + if corr_pdfs: + corr_joined = naming_corr(joined) + tasks.append(join_pdfs(corr_pdfs, corr_joined)) + + return tasks + + +def only_corr_planner( + no_join: bool = False, + ): + pass diff --git a/test/test_planner.py b/test/test_planner.py new file mode 100644 index 0000000..0aaded4 --- /dev/null +++ b/test/test_planner.py @@ -0,0 +1,223 @@ +from bopytex.planner import planner +from bopytex.tasks import Task + + +def test_planner_generate(): + tasks = planner( + template="tpl_source.tex", + subjects=[{"number": "01"}, {"number": "02"}], + no_pdf=1, + ) + assert tasks == [ + Task( + action="GENERATE", + args={"number": "01"}, + deps=["tpl_source.tex"], + output="01_source.tex", + ), + Task( + action="GENERATE", + args={"number": "02"}, + deps=["tpl_source.tex"], + output="02_source.tex", + ), + ] + +def test_planner_generate_compile(): + tasks = planner( + template="tpl_source.tex", + subjects=[{"number": "01"}, {"number": "02"}], + no_join=1, + ) + assert tasks == [ + Task( + action="GENERATE", + args={"number": "01"}, + deps=["tpl_source.tex"], + output="01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["01_source.tex"], + output="01_source.pdf", + ), + Task( + action="GENERATE", + args={"number": "02"}, + deps=["tpl_source.tex"], + output="02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["02_source.tex"], + output="02_source.pdf", + ), + ] + +def test_planner_generate_compile_join(): + tasks = planner( + template="tpl_source.tex", + subjects=[{"number": "01"}, {"number": "02"}], + ) + assert tasks == [ + Task( + action="GENERATE", + args={"number": "01"}, + deps=["tpl_source.tex"], + output="01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["01_source.tex"], + output="01_source.pdf", + ), + Task( + action="GENERATE", + args={"number": "02"}, + deps=["tpl_source.tex"], + output="02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["02_source.tex"], + output="02_source.pdf", + ), + Task( + action="JOIN", + args={}, + deps=["01_source.pdf", "02_source.pdf"], + output="joined_source.pdf" + ) + ] + +def test_planner_generate_compile_corr(): + tasks = planner( + template="tpl_source.tex", + subjects=[{"number": "01"}, {"number": "02"}], + corr=1, + no_join=1 + ) + assert tasks == [ + Task( + action="GENERATE", + args={"number": "01"}, + deps=["tpl_source.tex"], + output="01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["01_source.tex"], + output="01_source.pdf", + ), + Task( + action="ACTIVATE_CORR", + args={}, + deps=["01_source.tex"], + output="corr_01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_01_source.tex"], + output="corr_01_source.pdf", + ), + Task( + action="GENERATE", + args={"number": "02"}, + deps=["tpl_source.tex"], + output="02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["02_source.tex"], + output="02_source.pdf", + ), + Task( + action="ACTIVATE_CORR", + args={}, + deps=["02_source.tex"], + output="corr_02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_02_source.tex"], + output="corr_02_source.pdf", + ), + ] + +def test_planner_generate_compile_corr_joined(): + tasks = planner( + template="tpl_source.tex", + subjects=[{"number": "01"}, {"number": "02"}], + corr=1, + ) + assert tasks == [ + Task( + action="GENERATE", + args={"number": "01"}, + deps=["tpl_source.tex"], + output="01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["01_source.tex"], + output="01_source.pdf", + ), + Task( + action="ACTIVATE_CORR", + args={}, + deps=["01_source.tex"], + output="corr_01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_01_source.tex"], + output="corr_01_source.pdf", + ), + Task( + action="GENERATE", + args={"number": "02"}, + deps=["tpl_source.tex"], + output="02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["02_source.tex"], + output="02_source.pdf", + ), + Task( + action="ACTIVATE_CORR", + args={}, + deps=["02_source.tex"], + output="corr_02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_02_source.tex"], + output="corr_02_source.pdf", + ), + Task( + action="JOIN", + args={}, + deps=["01_source.pdf", "02_source.pdf"], + output="joined_source.pdf" + ), + Task( + action="JOIN", + args={}, + deps=["corr_01_source.pdf", "corr_02_source.pdf"], + output="corr_joined_source.pdf" + ) + ] + From 7f40b7c38f315555659b00b4143e12b647d82d41 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 15:42:00 +0200 Subject: [PATCH 24/76] feat: only_corr_planner --- bopytex/planner.py | 25 ++++++++++++++++++-- test/test_planner.py | 54 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 9 deletions(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index 1d54f0e..0d38a78 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -62,6 +62,27 @@ def planner( def only_corr_planner( + sources=[], + no_pdf: bool = False, no_join: bool = False, - ): - pass +) -> list[Task]: + tasks = [] + corr_pdfs = [] + + for source in sources: + corr_source = naming_corr(source) + tasks.append(activate_corr_on(source, corr_source)) + + if not no_pdf: + corr_pdf = naming_source2pdf(corr_source) + tasks.append(compile_pdf(corr_source, corr_pdf)) + corr_pdfs.append(corr_pdf) + + if not no_join: + joined = "joined.pdf" + + if corr_pdfs: + corr_joined = naming_corr(joined) + tasks.append(join_pdfs(corr_pdfs, corr_joined)) + + return tasks diff --git a/test/test_planner.py b/test/test_planner.py index 0aaded4..9582726 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -1,4 +1,4 @@ -from bopytex.planner import planner +from bopytex.planner import only_corr_planner, planner from bopytex.tasks import Task @@ -23,6 +23,7 @@ def test_planner_generate(): ), ] + def test_planner_generate_compile(): tasks = planner( template="tpl_source.tex", @@ -56,6 +57,7 @@ def test_planner_generate_compile(): ), ] + def test_planner_generate_compile_join(): tasks = planner( template="tpl_source.tex", @@ -90,16 +92,17 @@ def test_planner_generate_compile_join(): action="JOIN", args={}, deps=["01_source.pdf", "02_source.pdf"], - output="joined_source.pdf" - ) + output="joined_source.pdf", + ), ] + def test_planner_generate_compile_corr(): tasks = planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], corr=1, - no_join=1 + no_join=1, ) assert tasks == [ Task( @@ -152,6 +155,7 @@ def test_planner_generate_compile_corr(): ), ] + def test_planner_generate_compile_corr_joined(): tasks = planner( template="tpl_source.tex", @@ -211,13 +215,49 @@ def test_planner_generate_compile_corr_joined(): action="JOIN", args={}, deps=["01_source.pdf", "02_source.pdf"], - output="joined_source.pdf" + output="joined_source.pdf", ), Task( action="JOIN", args={}, deps=["corr_01_source.pdf", "corr_02_source.pdf"], - output="corr_joined_source.pdf" - ) + output="corr_joined_source.pdf", + ), ] +def test_only_corr_planner(): + tasks = only_corr_planner( + sources = ["01_source.tex", "02_source.tex"], + ) + assert tasks == [ + Task( + action="ACTIVATE_CORR", + args={}, + deps=["01_source.tex"], + output="corr_01_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_01_source.tex"], + output="corr_01_source.pdf", + ), + Task( + action="ACTIVATE_CORR", + args={}, + deps=["02_source.tex"], + output="corr_02_source.tex", + ), + Task( + action="COMPILE", + args={}, + deps=["corr_02_source.tex"], + output="corr_02_source.pdf", + ), + Task( + action="JOIN", + args={}, + deps=["corr_01_source.pdf", "corr_02_source.pdf"], + output="corr_joined.pdf", + ), + ] From 4e7805f40df77cad7fc543cf2227c36540eb17de Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 16:08:10 +0200 Subject: [PATCH 25/76] Feat: add list tex files with no tpl --- bopytex/bopytex.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index c863828..a9aaa46 100755 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -7,6 +7,7 @@ Producing then compiling templates import logging import csv +import os formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -32,6 +33,7 @@ def build_subject_list_from_qty(qty: int) -> list[dict]: 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: @@ -41,6 +43,14 @@ def build_subjects(students_csv, quantity_subjects): return build_subject_list_from_qty(quantity_subjects) +def list_tex_files_no_tpl(dir="."): + tex_files = [] + for file in os.listdir(dir): + if file.endswith(".tex") and not file.startswith(tpl_): + tex_files.append(file) + return tex_files + + def bopytex( template: str, working_dir: str, From dca69f94aa7b23520733a6d886a1a990d9c1d30c Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 16:16:39 +0200 Subject: [PATCH 26/76] Fix: actions typehint --- bopytex/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 17cfc0b..7ae6c9a 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -5,7 +5,7 @@ from bopytex.tasks import Task class Scheduler: - def __init__(self, actions: list, done: list[str] = None): + def __init__(self, actions: dict, done: list[str] = None): self.actions = actions if done is None: From 374c5f7467a3f351402e35e24aba6815287ff99b Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 16:16:54 +0200 Subject: [PATCH 27/76] fix: remove comment --- bopytex/planner.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index 0d38a78..21d00ca 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -23,7 +23,6 @@ def planner( corr: bool = False, no_join: bool = False, no_pdf: bool = False, - # dirty: bool = False, ) -> list[Task]: tasks = [] From dc12a919d0a837983b7c04cdaceda71036ca5780 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 16:18:56 +0200 Subject: [PATCH 28/76] feat: dirty bopytex, need to test it --- bopytex/bopytex.py | 26 +++++++++++++++++++++----- test/test_bopytex.py | 3 +++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index a9aaa46..ab96156 100755 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -8,6 +8,10 @@ Producing then compiling templates import logging import csv import os +from bopytex.actions import ACTIONS + +from bopytex.planner import only_corr_planner, planner +from bopytex.scheduler import Scheduler formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -46,23 +50,35 @@ def build_subjects(students_csv, quantity_subjects): def list_tex_files_no_tpl(dir="."): tex_files = [] for file in os.listdir(dir): - if file.endswith(".tex") and not file.startswith(tpl_): + if file.endswith(".tex") and not file.startswith("tpl_"): tex_files.append(file) return tex_files 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, + no_pdf: bool, + actions: dict = ACTIONS, ): - pass + + if only_corr: + tex_files = list_tex_files_no_tpl() + tasks = only_corr_planner(sources=tex_files, no_pdf=no_pdf, no_join=no_join) + else: + subjects = build_subjects( + students_csv=students_csv, quantity_subjects=quantity_subjects + ) + tasks = planner(template, subjects, corr, no_join, no_pdf) + + scheduler = Scheduler(actions, [template]) + scheduler.append(tasks) + + scheduler.run() # ----------------------------- diff --git a/test/test_bopytex.py b/test/test_bopytex.py index c8b69c5..a752b8e 100644 --- a/test/test_bopytex.py +++ b/test/test_bopytex.py @@ -27,3 +27,6 @@ def test_build_subject_list_from_infos(): {"name": "test1", "date": "today", "number": "1"}, {"name": "test2", "date": "tomorow", "number": "2"}, ] + +def test_bopytex(): + pass From 03482d4b3de909873f80fd75f324a3a422a0c2b3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 17:08:05 +0200 Subject: [PATCH 29/76] Feat: add doable_task to scheduler --- bopytex/scheduler.py | 8 ++++++++ test/test_scheduler.py | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 7ae6c9a..34b68b7 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -19,6 +19,14 @@ class Scheduler: def tasks(self): return self._tasks + @property + def doable_tasks(self): + return [ + task + for task in self.tasks + if not task.deps or all([d in self.done for d in task.deps]) + ] + @property def all_deps(self): return {d for task in self.tasks for d in task.deps} diff --git a/test/test_scheduler.py b/test/test_scheduler.py index c3cf44a..cca528b 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -31,7 +31,9 @@ def test_schedule_dispatch(): def test_schedule_one_task(): scheduler = Scheduler(actions) - scheduler.append([Task(action="DO", args={}, deps=[], output="end")]) + tasks = [Task(action="DO", args={}, deps=[], output="end")] + scheduler.append(tasks) + assert scheduler.doable_tasks == tasks result = scheduler.next() assert result == "[] - {} - end - done" assert scheduler.tasks == [] @@ -54,7 +56,8 @@ def test_schedule_multiple_tasks(): t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) - assert scheduler.is_finishable() == True + assert scheduler.doable_tasks == [t1, t2, t3] + assert scheduler.is_finishable() result = scheduler.next() assert result == "[] - {'task': 'one'} - one - done" @@ -79,17 +82,20 @@ def test_schedule_multiple_tasks_with_dependencies(): t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) - assert scheduler.is_finishable() == True + assert scheduler.doable_tasks == [t3] + assert scheduler.is_finishable() result = scheduler.next() assert result == "[] - {'task': 'three'} - three - done" assert scheduler.tasks == [t1, t2] assert scheduler.done == ["three"] + assert scheduler.doable_tasks == [t1] result = scheduler.next() assert result == "['three'] - {'task': 'one'} - one - done" assert scheduler.tasks == [t2] assert scheduler.done == ["three", "one"] + assert scheduler.doable_tasks == [t2] result = scheduler.next() assert result == "['one'] - {'task': 'two'} - two - done" @@ -110,8 +116,10 @@ def test_schedule_multiple_tasks_with_undoable_dependencies(): t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") scheduler.append([t1, t2]) - assert scheduler.is_finishable() == False + assert scheduler.doable_tasks == [t2] + assert not scheduler.is_finishable() scheduler.run() assert scheduler.tasks == [t1] assert scheduler.done == ["two"] + assert scheduler.doable_tasks == [] From 1f0547c1ac2ab3606558b130599b1e6bf76fba65 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 21:23:28 +0200 Subject: [PATCH 30/76] doc: add docstring and typehints --- bopytex/scheduler.py | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 34b68b7..0e22d98 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -16,11 +16,13 @@ class Scheduler: self._tasks = [] @property - def tasks(self): + def tasks(self) -> list[Task]: + """ list all the tasks """ return self._tasks @property - def doable_tasks(self): + def doable_tasks(self) -> list[Task]: + """ list all doable tasks """ return [ task for task in self.tasks @@ -28,15 +30,17 @@ class Scheduler: ] @property - def all_deps(self): + def all_deps(self) -> list[str]: + """ List dependencies of all tasks """ return {d for task in self.tasks for d in task.deps} @property - def all_output(self): + def all_output(self) -> list[str]: + """ List ouput of all tasks """ return {task.output for task in self.tasks} @property - def done(self): + def done(self) -> list[str]: return self._done def append(self, tasks: list[Task]): @@ -54,24 +58,14 @@ class Scheduler: return self.next() def next(self): - undoable = [] - try: - task = self._tasks.pop(0) + task = self.doable_tasks[0] except IndexError: raise StopIteration - while not all([d in self.done for d in task.deps]): - undoable.append(task) - try: - task = self._tasks.pop(0) - except IndexError: - self.append(undoable) - raise StopIteration - - self.append(undoable) ans = self.dispatch(task) self._done.append(task.output) + self._tasks.remove(task) return ans def run(self): From 963348611a1f4d027d2307190b0a04359d45d309 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 21:46:24 +0200 Subject: [PATCH 31/76] Feat: scheduler don't manage actions --- bopytex/scheduler.py | 40 ++++++---------- test/test_scheduler.py | 106 +++++++++++++++++++++-------------------- 2 files changed, 70 insertions(+), 76 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 0e22d98..212c726 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -5,8 +5,9 @@ from bopytex.tasks import Task class Scheduler: - def __init__(self, actions: dict, done: list[str] = None): - self.actions = actions + """Scheduler is responsible of getting tasks (the tasks) and yield those that can be done""" + + def __init__(self, done: list[str] = None): if done is None: self._done = [] @@ -17,12 +18,12 @@ class Scheduler: @property def tasks(self) -> list[Task]: - """ list all the tasks """ + """List all the tasks todo""" return self._tasks @property def doable_tasks(self) -> list[Task]: - """ list all doable tasks """ + """List all doable tasks""" return [ task for task in self.tasks @@ -31,12 +32,12 @@ class Scheduler: @property def all_deps(self) -> list[str]: - """ List dependencies of all tasks """ + """List dependencies of all tasks""" return {d for task in self.tasks for d in task.deps} @property def all_output(self) -> list[str]: - """ List ouput of all tasks """ + """List ouput of all tasks""" return {task.output for task in self.tasks} @property @@ -46,31 +47,20 @@ class Scheduler: def append(self, tasks: list[Task]): self._tasks += tasks - def dispatch(self, task): - """Do a task""" - ans = self.actions[task.action](task.deps, task.args, task.output) - return ans + def is_finishable(self): + return self.all_deps.issubset(self.all_output) - def __iter__(self): - return self - - def __next__(self): - return self.next() - - def next(self): + def next_task(self): try: task = self.doable_tasks[0] except IndexError: raise StopIteration - ans = self.dispatch(task) self._done.append(task.output) self._tasks.remove(task) - return ans + return task - def run(self): - for _ in self: - pass - - def is_finishable(self): - return self.all_deps.issubset(self.all_output) + def backlog(self): + """ Yield tasks sorted according to dependencies """ + while self.doable_tasks: + yield self.next_task() diff --git a/test/test_scheduler.py b/test/test_scheduler.py index cca528b..4ac1475 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -3,18 +3,11 @@ from bopytex.scheduler import Scheduler import pytest -def action_done(deps, args, output): - return f"{deps} - {args} - {output} - done" - - -actions = {"DO": action_done} - - def test_schedule_append(): - scheduler = Scheduler(actions) + scheduler = Scheduler() tasks = [ - Task(action="DO", args={}, deps=["dep1", "dep2"], output="end1"), - Task(action="DO", args={}, deps=["dep1", "dep3"], output="end2"), + Task(action="FOO", args={}, deps=["dep1", "dep2"], output="end1"), + Task(action="FOO", args={}, deps=["dep1", "dep3"], output="end2"), ] scheduler.append(tasks) assert scheduler.tasks == tasks @@ -22,104 +15,115 @@ def test_schedule_append(): assert scheduler.all_output == {"end1", "end2"} -def test_schedule_dispatch(): - scheduler = Scheduler(actions) - task = Task(action="DO", args={}, deps=[], output="end") - result = scheduler.dispatch(task) - assert result == "[] - {} - end - done" - - def test_schedule_one_task(): - scheduler = Scheduler(actions) - tasks = [Task(action="DO", args={}, deps=[], output="end")] + scheduler = Scheduler() + tasks = [Task(action="FOO", args={}, deps=[], output="end")] scheduler.append(tasks) + assert scheduler.doable_tasks == tasks - result = scheduler.next() - assert result == "[] - {} - end - done" + + result = scheduler.next_task() + assert result == tasks[0] assert scheduler.tasks == [] assert scheduler.done == ["end"] def test_schedule_one_task_with_args(): - scheduler = Scheduler(actions) - scheduler.append([Task(action="DO", args={"task": "one"}, deps=[], output="one")]) - result = scheduler.next() - assert result == "[] - {'task': 'one'} - one - done" + scheduler = Scheduler() + tasks = [Task(action="FOO", args={"task": "one"}, deps=[], output="one")] + scheduler.append(tasks) + + result = scheduler.next_task() + + assert result == tasks[0] assert scheduler.tasks == [] assert scheduler.done == ["one"] def test_schedule_multiple_tasks(): - scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=[], output="one") - t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") - t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") + scheduler = Scheduler() + t1 = Task(action="FOO", args={"task": "one"}, deps=[], output="one") + t2 = Task(action="FOO", args={"task": "two"}, deps=[], output="two") + t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) assert scheduler.doable_tasks == [t1, t2, t3] assert scheduler.is_finishable() - result = scheduler.next() - assert result == "[] - {'task': 'one'} - one - done" + result = scheduler.next_task() + assert result == t1 assert scheduler.tasks == [t2, t3] assert scheduler.done == ["one"] - result = scheduler.next() - assert result == "[] - {'task': 'two'} - two - done" + result = scheduler.next_task() + assert result == t2 assert scheduler.tasks == [t3] assert scheduler.done == ["one", "two"] - result = scheduler.next() - assert result == "[] - {'task': 'three'} - three - done" + result = scheduler.next_task() + assert result == t3 assert scheduler.tasks == [] assert scheduler.done == ["one", "two", "three"] def test_schedule_multiple_tasks_with_dependencies(): - scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=["three"], output="one") - t2 = Task(action="DO", args={"task": "two"}, deps=["one"], output="two") - t3 = Task(action="DO", args={"task": "three"}, deps=[], output="three") + scheduler = Scheduler() + t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FOO", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) assert scheduler.doable_tasks == [t3] assert scheduler.is_finishable() - result = scheduler.next() - assert result == "[] - {'task': 'three'} - three - done" + result = scheduler.next_task() + assert result == t3 assert scheduler.tasks == [t1, t2] assert scheduler.done == ["three"] assert scheduler.doable_tasks == [t1] - result = scheduler.next() - assert result == "['three'] - {'task': 'one'} - one - done" + result = scheduler.next_task() + assert result == t1 assert scheduler.tasks == [t2] assert scheduler.done == ["three", "one"] assert scheduler.doable_tasks == [t2] - result = scheduler.next() - assert result == "['one'] - {'task': 'two'} - two - done" + result = scheduler.next_task() + assert result == t2 assert scheduler.tasks == [] assert scheduler.done == ["three", "one", "two"] +def test_schedule_multiple_tasks_with_dependencies_loop(): + scheduler = Scheduler() + t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FOO", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") + scheduler.append([t1, t2, t3]) + + ordered_tasks = [] + for task in scheduler.backlog(): + ordered_tasks.append(task) + assert ordered_tasks == [t3, t1, t2] def test_schedule_empty_task(): - scheduler = Scheduler(actions) + scheduler = Scheduler() scheduler.append([]) with pytest.raises(StopIteration): - scheduler.next() + scheduler.next_task() def test_schedule_multiple_tasks_with_undoable_dependencies(): - scheduler = Scheduler(actions) - t1 = Task(action="DO", args={"task": "one"}, deps=["three"], output="one") - t2 = Task(action="DO", args={"task": "two"}, deps=[], output="two") + scheduler = Scheduler() + t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FOO", args={"task": "two"}, deps=[], output="two") scheduler.append([t1, t2]) assert scheduler.doable_tasks == [t2] assert not scheduler.is_finishable() - scheduler.run() + for _ in scheduler.backlog(): + pass + assert scheduler.tasks == [t1] assert scheduler.done == ["two"] assert scheduler.doable_tasks == [] From e52b6eb06496dc8732a589c119e60c75f24a7282 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 21:53:00 +0200 Subject: [PATCH 32/76] refact: rename bopytex to service --- bopytex/script.py | 9 ++++++--- bopytex/{bopytex.py => service.py} | 6 ++++-- test/{test_bopytex.py => test_service.py} | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) rename bopytex/{bopytex.py => service.py} (96%) rename test/{test_bopytex.py => test_service.py} (92%) diff --git a/bopytex/script.py b/bopytex/script.py index d4747e6..ee50405 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -4,9 +4,8 @@ import click import logging -from pathlib import Path -from bopytex.bopytex import bopytex +from bopytex.service import bopytex formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -38,7 +37,11 @@ logger.addHandler(steam_handler) help="CSV containing list of students names", ) @click.option( - "-d", "--dirty", is_flag=True, default=False, help="Do not clean after compilation", + "-d", + "--dirty", + is_flag=True, + default=False, + help="Do not clean after compilation", ) @click.option( "-n", diff --git a/bopytex/bopytex.py b/bopytex/service.py similarity index 96% rename from bopytex/bopytex.py rename to bopytex/service.py index ab96156..77ec753 100755 --- a/bopytex/bopytex.py +++ b/bopytex/service.py @@ -75,10 +75,12 @@ def bopytex( ) tasks = planner(template, subjects, corr, no_join, no_pdf) - scheduler = Scheduler(actions, [template]) + + scheduler = Scheduler([template]) scheduler.append(tasks) - scheduler.run() + for _ in scheduler.backlog: + pass # ----------------------------- diff --git a/test/test_bopytex.py b/test/test_service.py similarity index 92% rename from test/test_bopytex.py rename to test/test_service.py index a752b8e..d8303c5 100644 --- a/test/test_bopytex.py +++ b/test/test_service.py @@ -1,4 +1,4 @@ -from bopytex.bopytex import build_subject_list_from_infos, build_subject_list_from_qty +from bopytex.service import build_subject_list_from_infos, build_subject_list_from_qty def test_build_subject_list_from_qty(): @@ -28,5 +28,6 @@ def test_build_subject_list_from_infos(): {"name": "test2", "date": "tomorow", "number": "2"}, ] + def test_bopytex(): pass From 8c9d7bf9a2fd452ecfe28047b07118ae90d87e12 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 22:06:42 +0200 Subject: [PATCH 33/76] refact: rename planner to default_planner --- bopytex/planner.py | 2 +- bopytex/service.py | 25 +++++++++---------------- test/test_planner.py | 15 ++++++++------- 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index 21d00ca..28fea3b 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -17,7 +17,7 @@ def naming_join(template): return naming_source2pdf("joined" + template[3:]) -def planner( +def default_planner( template: str, subjects: list[dict], corr: bool = False, diff --git a/bopytex/service.py b/bopytex/service.py index 77ec753..5bb2b3a 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -5,22 +5,13 @@ Producing then compiling templates """ -import logging import csv import os from bopytex.actions import ACTIONS -from bopytex.planner import only_corr_planner, planner +from bopytex.planner import only_corr_planner, default_planner from bopytex.scheduler import Scheduler -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 build_subject_list_from_infos(infos: list[dict]) -> list[dict]: subjects = [] @@ -47,10 +38,10 @@ def build_subjects(students_csv, quantity_subjects): return build_subject_list_from_qty(quantity_subjects) -def list_tex_files_no_tpl(dir="."): +def list_files(dir=".", accept=lambda _: True, reject=lambda _: False): tex_files = [] for file in os.listdir(dir): - if file.endswith(".tex") and not file.startswith("tpl_"): + if accept(file) and not reject(file): tex_files.append(file) return tex_files @@ -67,19 +58,21 @@ def bopytex( ): if only_corr: - tex_files = list_tex_files_no_tpl() + tex_files = list_files( + accept=lambda x: x.endswith(".tex"), + reject=lambda x: x.startswith("tpl_"), + ) tasks = only_corr_planner(sources=tex_files, no_pdf=no_pdf, no_join=no_join) else: subjects = build_subjects( students_csv=students_csv, quantity_subjects=quantity_subjects ) - tasks = planner(template, subjects, corr, no_join, no_pdf) - + tasks = default_planner(template, subjects, corr, no_join, no_pdf) scheduler = Scheduler([template]) scheduler.append(tasks) - for _ in scheduler.backlog: + for task in scheduler.backlog: pass diff --git a/test/test_planner.py b/test/test_planner.py index 9582726..41c542f 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -1,9 +1,9 @@ -from bopytex.planner import only_corr_planner, planner +from bopytex.planner import only_corr_planner, default_planner from bopytex.tasks import Task def test_planner_generate(): - tasks = planner( + tasks = default_planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], no_pdf=1, @@ -25,7 +25,7 @@ def test_planner_generate(): def test_planner_generate_compile(): - tasks = planner( + tasks = default_planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], no_join=1, @@ -59,7 +59,7 @@ def test_planner_generate_compile(): def test_planner_generate_compile_join(): - tasks = planner( + tasks = default_planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], ) @@ -98,7 +98,7 @@ def test_planner_generate_compile_join(): def test_planner_generate_compile_corr(): - tasks = planner( + tasks = default_planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], corr=1, @@ -157,7 +157,7 @@ def test_planner_generate_compile_corr(): def test_planner_generate_compile_corr_joined(): - tasks = planner( + tasks = default_planner( template="tpl_source.tex", subjects=[{"number": "01"}, {"number": "02"}], corr=1, @@ -225,9 +225,10 @@ def test_planner_generate_compile_corr_joined(): ), ] + def test_only_corr_planner(): tasks = only_corr_planner( - sources = ["01_source.tex", "02_source.tex"], + sources=["01_source.tex", "02_source.tex"], ) assert tasks == [ Task( From dfcc48dd20e3b25aa47a0111bc3c46b26e426bbb Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 22:29:58 +0200 Subject: [PATCH 34/76] refact: planners accept only options in parameters --- bopytex/planner.py | 44 +++++++++++++++++++++++++++++++++++-------- test/test_planner.py | 45 ++++++++++++++++++++++++++++---------------- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/bopytex/planner.py b/bopytex/planner.py index 28fea3b..2104028 100644 --- a/bopytex/planner.py +++ b/bopytex/planner.py @@ -1,6 +1,10 @@ from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs +class PlannerMissingOption(Exception): + pass + + def naming_template2source(template: str, metadatas: dict): return metadatas["number"] + template[3:] @@ -18,12 +22,25 @@ def naming_join(template): def default_planner( - template: str, - subjects: list[dict], - corr: bool = False, - no_join: bool = False, - no_pdf: bool = False, + options: dict, ) -> list[Task]: + + opt = { + "corr": False, + "no_join": False, + "no_pdf": False, + } + opt.update(options) + + try: + template = opt["template"] + subjects = opt["subjects"] + corr = opt["corr"] + no_join = opt["no_join"] + no_pdf = opt["no_pdf"] + except KeyError: + raise PlannerMissingOption("An option is missing") + tasks = [] pdfs = [] @@ -61,10 +78,21 @@ def default_planner( def only_corr_planner( - sources=[], - no_pdf: bool = False, - no_join: bool = False, + options: dict, ) -> list[Task]: + opt = { + "no_join": False, + "no_pdf": False, + } + opt.update(options) + + try: + sources = opt["sources"] + no_join = opt["no_join"] + no_pdf = opt["no_pdf"] + except KeyError: + raise PlannerMissingOption("An option is missing") + tasks = [] corr_pdfs = [] diff --git a/test/test_planner.py b/test/test_planner.py index 41c542f..0306805 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -4,9 +4,11 @@ from bopytex.tasks import Task def test_planner_generate(): tasks = default_planner( - template="tpl_source.tex", - subjects=[{"number": "01"}, {"number": "02"}], - no_pdf=1, + options={ + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + "no_pdf": True, + } ) assert tasks == [ Task( @@ -26,9 +28,11 @@ def test_planner_generate(): def test_planner_generate_compile(): tasks = default_planner( - template="tpl_source.tex", - subjects=[{"number": "01"}, {"number": "02"}], - no_join=1, + options={ + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + "no_join": True, + } ) assert tasks == [ Task( @@ -60,8 +64,10 @@ def test_planner_generate_compile(): def test_planner_generate_compile_join(): tasks = default_planner( - template="tpl_source.tex", - subjects=[{"number": "01"}, {"number": "02"}], + options={ + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + } ) assert tasks == [ Task( @@ -99,10 +105,12 @@ def test_planner_generate_compile_join(): def test_planner_generate_compile_corr(): tasks = default_planner( - template="tpl_source.tex", - subjects=[{"number": "01"}, {"number": "02"}], - corr=1, - no_join=1, + options={ + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + "corr": 1, + "no_join": 1, + } ) assert tasks == [ Task( @@ -158,9 +166,12 @@ def test_planner_generate_compile_corr(): def test_planner_generate_compile_corr_joined(): tasks = default_planner( - template="tpl_source.tex", - subjects=[{"number": "01"}, {"number": "02"}], - corr=1, + options={ + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + "corr": True, + "no_join": False, + } ) assert tasks == [ Task( @@ -228,7 +239,9 @@ def test_planner_generate_compile_corr_joined(): def test_only_corr_planner(): tasks = only_corr_planner( - sources=["01_source.tex", "02_source.tex"], + options={ + "sources": ["01_source.tex", "02_source.tex"], + } ) assert tasks == [ Task( From c89959673b5bc3bb50f7aa4fb222867d329020f3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 22:46:09 +0200 Subject: [PATCH 35/76] refact: move planners to own directory --- bopytex/planner.py | 115 --------------------------- bopytex/planner/__init__.py | 2 + bopytex/planner/default_planner.py | 62 +++++++++++++++ bopytex/planner/exceptions.py | 2 + bopytex/planner/naming.py | 15 ++++ bopytex/planner/only_corr_planner.py | 44 ++++++++++ test/test_planner.py | 27 ++++--- 7 files changed, 139 insertions(+), 128 deletions(-) delete mode 100644 bopytex/planner.py create mode 100644 bopytex/planner/__init__.py create mode 100644 bopytex/planner/default_planner.py create mode 100644 bopytex/planner/exceptions.py create mode 100644 bopytex/planner/naming.py create mode 100644 bopytex/planner/only_corr_planner.py diff --git a/bopytex/planner.py b/bopytex/planner.py deleted file mode 100644 index 2104028..0000000 --- a/bopytex/planner.py +++ /dev/null @@ -1,115 +0,0 @@ -from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs - - -class PlannerMissingOption(Exception): - pass - - -def naming_template2source(template: str, metadatas: dict): - return metadatas["number"] + template[3:] - - -def naming_corr(source): - return "corr_" + source - - -def naming_source2pdf(source): - return source[:-4] + ".pdf" - - -def naming_join(template): - return naming_source2pdf("joined" + template[3:]) - - -def default_planner( - options: dict, -) -> list[Task]: - - opt = { - "corr": False, - "no_join": False, - "no_pdf": False, - } - opt.update(options) - - try: - template = opt["template"] - subjects = opt["subjects"] - corr = opt["corr"] - no_join = opt["no_join"] - no_pdf = opt["no_pdf"] - except KeyError: - raise PlannerMissingOption("An option is missing") - - tasks = [] - - pdfs = [] - corr_pdfs = [] - - for subject in subjects: - source = naming_template2source(template, subject) - - tasks.append(generate(template, subject, source)) - - if not no_pdf: - pdf = naming_source2pdf(source) - tasks.append(compile_pdf(source, pdf)) - pdfs.append(pdf) - - if corr: - corr_source = naming_corr(source) - tasks.append(activate_corr_on(source, corr_source)) - - if not no_pdf: - corr_pdf = naming_source2pdf(corr_source) - tasks.append(compile_pdf(corr_source, corr_pdf)) - corr_pdfs.append(corr_pdf) - - if not no_join: - joined = naming_join(template) - if pdfs: - tasks.append(join_pdfs(pdfs, joined)) - - if corr_pdfs: - corr_joined = naming_corr(joined) - tasks.append(join_pdfs(corr_pdfs, corr_joined)) - - return tasks - - -def only_corr_planner( - options: dict, -) -> list[Task]: - opt = { - "no_join": False, - "no_pdf": False, - } - opt.update(options) - - try: - sources = opt["sources"] - no_join = opt["no_join"] - no_pdf = opt["no_pdf"] - except KeyError: - raise PlannerMissingOption("An option is missing") - - tasks = [] - corr_pdfs = [] - - for source in sources: - corr_source = naming_corr(source) - tasks.append(activate_corr_on(source, corr_source)) - - if not no_pdf: - corr_pdf = naming_source2pdf(corr_source) - tasks.append(compile_pdf(corr_source, corr_pdf)) - corr_pdfs.append(corr_pdf) - - if not no_join: - joined = "joined.pdf" - - if corr_pdfs: - corr_joined = naming_corr(joined) - tasks.append(join_pdfs(corr_pdfs, corr_joined)) - - return tasks diff --git a/bopytex/planner/__init__.py b/bopytex/planner/__init__.py new file mode 100644 index 0000000..9ceb248 --- /dev/null +++ b/bopytex/planner/__init__.py @@ -0,0 +1,2 @@ +from .default_planner import default_planner +from .only_corr_planner import only_corr_planner diff --git a/bopytex/planner/default_planner.py b/bopytex/planner/default_planner.py new file mode 100644 index 0000000..9255ae1 --- /dev/null +++ b/bopytex/planner/default_planner.py @@ -0,0 +1,62 @@ +from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs +import bopytex.planner.naming as naming + + +def default_planner(options: dict) -> list[Task]: + pass + + +def default_tasks_builder( + options: dict, +) -> list[Task]: + + opt = { + "corr": False, + "no_join": False, + "no_pdf": False, + } + opt.update(options) + + try: + template = opt["template"] + subjects = opt["subjects"] + corr = opt["corr"] + no_join = opt["no_join"] + no_pdf = opt["no_pdf"] + except KeyError: + raise PlannerMissingOption("An option is missing") + + tasks = [] + + pdfs = [] + corr_pdfs = [] + + for subject in subjects: + source = naming.template2source(template, subject) + + tasks.append(generate(template, subject, source)) + + if not no_pdf: + pdf = naming.source2pdf(source) + tasks.append(compile_pdf(source, pdf)) + pdfs.append(pdf) + + if corr: + corr_source = naming.corr(source) + tasks.append(activate_corr_on(source, corr_source)) + + if not no_pdf: + corr_pdf = naming.source2pdf(corr_source) + tasks.append(compile_pdf(corr_source, corr_pdf)) + corr_pdfs.append(corr_pdf) + + if not no_join: + joined = naming.join(template) + if pdfs: + tasks.append(join_pdfs(pdfs, joined)) + + if corr_pdfs: + corr_joined = naming.corr(joined) + tasks.append(join_pdfs(corr_pdfs, corr_joined)) + + return tasks diff --git a/bopytex/planner/exceptions.py b/bopytex/planner/exceptions.py new file mode 100644 index 0000000..65c7a29 --- /dev/null +++ b/bopytex/planner/exceptions.py @@ -0,0 +1,2 @@ +class PlannerMissingOption(Exception): + pass diff --git a/bopytex/planner/naming.py b/bopytex/planner/naming.py new file mode 100644 index 0000000..bf022b4 --- /dev/null +++ b/bopytex/planner/naming.py @@ -0,0 +1,15 @@ +def template2source(template: str, metadatas: dict): + return metadatas["number"] + template[3:] + + +def corr(source): + return "corr_" + source + + +def source2pdf(source): + return source[:-4] + ".pdf" + + +def join(template): + return source2pdf("joined" + template[3:]) + diff --git a/bopytex/planner/only_corr_planner.py b/bopytex/planner/only_corr_planner.py new file mode 100644 index 0000000..4656659 --- /dev/null +++ b/bopytex/planner/only_corr_planner.py @@ -0,0 +1,44 @@ +from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs +import bopytex.planner.naming as naming + + +def only_corr_planner(options: dict) -> list[Task]: + pass + + +def only_corr_tasks_builder( + options: dict, +) -> list[Task]: + opt = { + "no_join": False, + "no_pdf": False, + } + opt.update(options) + + try: + sources = opt["sources"] + no_join = opt["no_join"] + no_pdf = opt["no_pdf"] + except KeyError: + raise PlannerMissingOption("An option is missing") + + tasks = [] + corr_pdfs = [] + + for source in sources: + corr_source = naming.corr(source) + tasks.append(activate_corr_on(source, corr_source)) + + if not no_pdf: + corr_pdf = naming.source2pdf(corr_source) + tasks.append(compile_pdf(corr_source, corr_pdf)) + corr_pdfs.append(corr_pdf) + + if not no_join: + joined = "joined.pdf" + + if corr_pdfs: + corr_joined = naming.corr(joined) + tasks.append(join_pdfs(corr_pdfs, corr_joined)) + + return tasks diff --git a/test/test_planner.py b/test/test_planner.py index 0306805..0317abf 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -1,9 +1,10 @@ -from bopytex.planner import only_corr_planner, default_planner +from bopytex.planner.default_planner import default_tasks_builder +from bopytex.planner.only_corr_planner import only_corr_tasks_builder from bopytex.tasks import Task -def test_planner_generate(): - tasks = default_planner( +def test_tasks_builder_generate(): + tasks = default_tasks_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -26,8 +27,8 @@ def test_planner_generate(): ] -def test_planner_generate_compile(): - tasks = default_planner( +def test_tasks_builder_generate_compile(): + tasks = default_tasks_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -62,8 +63,8 @@ def test_planner_generate_compile(): ] -def test_planner_generate_compile_join(): - tasks = default_planner( +def test_tasks_builder_generate_compile_join(): + tasks = default_tasks_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -103,8 +104,8 @@ def test_planner_generate_compile_join(): ] -def test_planner_generate_compile_corr(): - tasks = default_planner( +def test_tasks_builder_generate_compile_corr(): + tasks = default_tasks_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -164,8 +165,8 @@ def test_planner_generate_compile_corr(): ] -def test_planner_generate_compile_corr_joined(): - tasks = default_planner( +def test_tasks_builder_generate_compile_corr_joined(): + tasks = default_tasks_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -237,8 +238,8 @@ def test_planner_generate_compile_corr_joined(): ] -def test_only_corr_planner(): - tasks = only_corr_planner( +def test_only_corr_tasks_builder(): + tasks = only_corr_tasks_builder( options={ "sources": ["01_source.tex", "02_source.tex"], } From abd517b33983257d7e12d6632db9d8f0820537dc Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 9 Apr 2022 23:00:43 +0200 Subject: [PATCH 36/76] Feat: planner generate tasks all by themselves --- bopytex/planner/default_planner.py | 36 +++++++++++++++++++++++++++- bopytex/planner/only_corr_planner.py | 16 ++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/bopytex/planner/default_planner.py b/bopytex/planner/default_planner.py index 9255ae1..5d56286 100644 --- a/bopytex/planner/default_planner.py +++ b/bopytex/planner/default_planner.py @@ -1,9 +1,43 @@ from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs import bopytex.planner.naming as naming +from bopytex.planner.exceptions import PlannerMissingOption +import csv + + +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 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 default_planner(options: dict) -> list[Task]: - pass + try: + students_csv = options["students_csv"] + + except KeyError: + try: + quantity_subjects = options["quantity_subjects"] + except KeyError: + raise PlannerMissingOption("students_csv or quantity_subjects is required") + else: + options["subjects"] = build_subject_list_from_qty(qty=quantity_subjects) + + else: + with open(students_csv, "r") as csv_file: + infos = csv.DictReader(csv_file) + options["subjects"] = build_subject_list_from_infos(infos) + + return default_tasks_builder(options) def default_tasks_builder( diff --git a/bopytex/planner/only_corr_planner.py b/bopytex/planner/only_corr_planner.py index 4656659..4c05f2a 100644 --- a/bopytex/planner/only_corr_planner.py +++ b/bopytex/planner/only_corr_planner.py @@ -1,9 +1,23 @@ from bopytex.tasks import Task, activate_corr_on, compile_pdf, generate, join_pdfs import bopytex.planner.naming as naming +import os + + +def list_files(dir=".", accept=lambda _: True, reject=lambda _: False): + files = [] + for file in os.listdir(dir): + if accept(file) and not reject(file): + files.append(file) + return files def only_corr_planner(options: dict) -> list[Task]: - pass + sources = list_files( + accept=lambda x: x.endswith(".tex"), + reject=lambda x: x.startswith("tpl_"), + ) + options["sources"] = sources + return only_corr_tasks_builder(options) def only_corr_tasks_builder( From 3f1464f3f6c264391de0f260389a296ed690e18a Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 10 Apr 2022 06:45:10 +0200 Subject: [PATCH 37/76] Feat: service work but do nothing! --- bopytex/planner/fake_planner.py | 9 +++++ bopytex/service.py | 67 ++++----------------------------- test/test_service.py | 39 ++++--------------- 3 files changed, 24 insertions(+), 91 deletions(-) create mode 100644 bopytex/planner/fake_planner.py diff --git a/bopytex/planner/fake_planner.py b/bopytex/planner/fake_planner.py new file mode 100644 index 0000000..380e23f --- /dev/null +++ b/bopytex/planner/fake_planner.py @@ -0,0 +1,9 @@ +from bopytex.tasks import Task + + +def simple(options: dict) -> list[Task]: + """Simple planner with options['quantity'] tasks and no dependencies""" + return [ + Task("Do", args={"number": i}, deps=[], output=f"{i}") + for i in range(options["quantity"]) + ] diff --git a/bopytex/service.py b/bopytex/service.py index 5bb2b3a..723af4f 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -5,75 +5,22 @@ Producing then compiling templates """ -import csv -import os from bopytex.actions import ACTIONS - -from bopytex.planner import only_corr_planner, default_planner from bopytex.scheduler import Scheduler -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 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 list_files(dir=".", accept=lambda _: True, reject=lambda _: False): - tex_files = [] - for file in os.listdir(dir): - if accept(file) and not reject(file): - tex_files.append(file) - return tex_files - - -def bopytex( - template: str, - students_csv: str, - quantity_subjects: int, - corr: bool, - only_corr: bool, - no_join: bool, - no_pdf: bool, +def orcherstrator( + options: dict, + planner, actions: dict = ACTIONS, ): + tasks = planner(options) - if only_corr: - tex_files = list_files( - accept=lambda x: x.endswith(".tex"), - reject=lambda x: x.startswith("tpl_"), - ) - tasks = only_corr_planner(sources=tex_files, no_pdf=no_pdf, no_join=no_join) - else: - subjects = build_subjects( - students_csv=students_csv, quantity_subjects=quantity_subjects - ) - tasks = default_planner(template, subjects, corr, no_join, no_pdf) - - scheduler = Scheduler([template]) + scheduler = Scheduler([options["template"]]) scheduler.append(tasks) - for task in scheduler.backlog: - pass + for task in scheduler.backlog(): + yield task # ----------------------------- diff --git a/test/test_service.py b/test/test_service.py index d8303c5..bf200be 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -1,33 +1,10 @@ -from bopytex.service import build_subject_list_from_infos, build_subject_list_from_qty +from bopytex.planner import fake_planner +from bopytex.service import orcherstrator +from bopytex.tasks import Task -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"}, - ] - - -def test_bopytex(): - pass +def test_service(): + options = {"quantity": 3, "template": "tpl_src.tex"} + service = orcherstrator(options, fake_planner.simple) + for i, task in enumerate(service): + assert task == Task("Do", args={"number": i}, deps=[], output=f"{i}") From 08411bd42df031bef3b312f4ac582d42a6b74fd8 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 10 Apr 2022 14:37:19 +0200 Subject: [PATCH 38/76] Feat: move action to worker with a dispatcher --- bopytex/planner/fake_planner.py | 2 +- bopytex/service.py | 5 ++--- bopytex/worker/__init__.py | 23 +++++++++++++++++++++++ bopytex/{actions.py => worker/worker.py} | 5 +++-- test/test_service.py | 10 +++++++--- 5 files changed, 36 insertions(+), 9 deletions(-) create mode 100644 bopytex/worker/__init__.py rename bopytex/{actions.py => worker/worker.py} (79%) diff --git a/bopytex/planner/fake_planner.py b/bopytex/planner/fake_planner.py index 380e23f..6617992 100644 --- a/bopytex/planner/fake_planner.py +++ b/bopytex/planner/fake_planner.py @@ -4,6 +4,6 @@ from bopytex.tasks import Task def simple(options: dict) -> list[Task]: """Simple planner with options['quantity'] tasks and no dependencies""" return [ - Task("Do", args={"number": i}, deps=[], output=f"{i}") + Task("DO", args={"number": i}, deps=[], output=f"{i}") for i in range(options["quantity"]) ] diff --git a/bopytex/service.py b/bopytex/service.py index 723af4f..217ed67 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -5,14 +5,13 @@ Producing then compiling templates """ -from bopytex.actions import ACTIONS from bopytex.scheduler import Scheduler def orcherstrator( options: dict, planner, - actions: dict = ACTIONS, + dispatcher, ): tasks = planner(options) @@ -20,7 +19,7 @@ def orcherstrator( scheduler.append(tasks) for task in scheduler.backlog(): - yield task + yield from dispatcher(task) # ----------------------------- diff --git a/bopytex/worker/__init__.py b/bopytex/worker/__init__.py new file mode 100644 index 0000000..fa48f63 --- /dev/null +++ b/bopytex/worker/__init__.py @@ -0,0 +1,23 @@ +class ActionNotFound(Exception): + pass + + +class Dispatcher: + def __init__(self, actions: list): + self._actions = actions + + def __call__(self, task): + try: + choosen_action = self._actions[task.action] + except KeyError: + raise ActionNotFound(f"The action {task.action} is not in {self._actions.keys()}") + + return choosen_action( + args=task.args, + deps=task.deps, + output=task.output + ) + +def fake_worker(args, deps, output): + yield f"FAKE - {args} - {deps} - {output}" + diff --git a/bopytex/actions.py b/bopytex/worker/worker.py similarity index 79% rename from bopytex/actions.py rename to bopytex/worker/worker.py index 89805a4..d544d77 100644 --- a/bopytex/actions.py +++ b/bopytex/worker/worker.py @@ -1,3 +1,4 @@ +""" A worker consumes tasks """ def generate(): pass @@ -18,10 +19,10 @@ def clean(): pass -ACTIONS = { +WORKERS = { "GENERATE": generate, "COMPILE": compile, "ACTIVATE_CORR": activate_corr, "JOIN_PDF": join_pdf, - "clean": clean, + "CLEAN": clean, } diff --git a/test/test_service.py b/test/test_service.py index bf200be..2e6970a 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -1,10 +1,14 @@ from bopytex.planner import fake_planner from bopytex.service import orcherstrator from bopytex.tasks import Task +from bopytex.worker import Dispatcher, fake_worker def test_service(): options = {"quantity": 3, "template": "tpl_src.tex"} - service = orcherstrator(options, fake_planner.simple) - for i, task in enumerate(service): - assert task == Task("Do", args={"number": i}, deps=[], output=f"{i}") + dispatcher = Dispatcher(actions={"DO": fake_worker}) + + service = orcherstrator(options, fake_planner.simple, dispatcher) + + for i, task_output in enumerate(service): + assert task_output == f"FAKE - {{'number': {i}}} - [] - {i}" From 76a033cf437ec2cd5108de1eeaf64e34e38c514c Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 10 Apr 2022 15:50:58 +0200 Subject: [PATCH 39/76] feat: create generate worker --- bopytex/worker/generate.py | 13 +++++++++++ bopytex/worker/worker.py | 28 ---------------------- test/worker/test_generate.py | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 bopytex/worker/generate.py delete mode 100644 bopytex/worker/worker.py create mode 100644 test/worker/test_generate.py diff --git a/bopytex/worker/generate.py b/bopytex/worker/generate.py new file mode 100644 index 0000000..ff80f76 --- /dev/null +++ b/bopytex/worker/generate.py @@ -0,0 +1,13 @@ +from jinja2.environment import Template + + +def generate(args, deps, output): + env = args["jinja2"]["environment"] + template = env.get_template(deps[0]) + with open(output, "w") as out: + out.write(tpl2tex(template, metas=args)) + yield f"GENERATE - {deps[0]} to {output}" + + +def tpl2tex(template: Template, metas: dict = {}) -> str: + return template.render(metas) diff --git a/bopytex/worker/worker.py b/bopytex/worker/worker.py deleted file mode 100644 index d544d77..0000000 --- a/bopytex/worker/worker.py +++ /dev/null @@ -1,28 +0,0 @@ -""" A worker consumes tasks """ -def generate(): - pass - - -def compile(): - pass - - -def activate_corr(): - pass - - -def join_pdf(): - pass - - -def clean(): - pass - - -WORKERS = { - "GENERATE": generate, - "COMPILE": compile, - "ACTIVATE_CORR": activate_corr, - "JOIN_PDF": join_pdf, - "CLEAN": clean, -} diff --git a/test/worker/test_generate.py b/test/worker/test_generate.py new file mode 100644 index 0000000..ab90ba7 --- /dev/null +++ b/test/worker/test_generate.py @@ -0,0 +1,45 @@ +import os +import jinja2 +from pathlib import Path +from bopytex.worker.generate import tpl2tex, generate +import pytest + + +def test_tpl2tex(): + tpl = "Plop {{a}}" + jinja2_tpl = jinja2.Template(tpl) + fed = tpl2tex(jinja2_tpl, metas={"a": 1}) + assert fed == "Plop 1" + + +@pytest.fixture +def template_path(tmp_path): + template = tmp_path / "template.j2" + with open(template, "w") as tpl: + tpl.write("Plop {{ a }}") + return template + + +@pytest.fixture +def jinja2_env(tmp_path): + templateEnv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmp_path)) + return templateEnv + + +def test_generate(template_path, jinja2_env): + tmp_path = template_path.parent + os.chdir(tmp_path) + + assert template_path.exists + template = str(template_path.name) + output = "output" + + result = next(generate( + args={"a": 2, "jinja2": {"environment": jinja2_env}}, + deps=[template], + output=output, + )) + assert result == "GENERATE - template.j2 to output" + with open(output, "r") as out: + lines = out.readlines() + assert lines == ["Plop 2"] From ac8fe3dfdd9bf64fa3464bd6d4802556f9ccb32a Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 10 Apr 2022 16:27:12 +0200 Subject: [PATCH 40/76] Feat: latexmk worker to compile tex --- bopytex/worker/compile.py | 12 ++++++++++++ test/worker/test_compile.py | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 bopytex/worker/compile.py create mode 100644 test/worker/test_compile.py diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py new file mode 100644 index 0000000..24a9531 --- /dev/null +++ b/bopytex/worker/compile.py @@ -0,0 +1,12 @@ +import subprocess + + +def latexmk(args: dict, deps, output): + compile_process = subprocess.Popen( + ["latexmk", deps[0]], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True + ) + yield from compile_process.stderr + diff --git a/test/worker/test_compile.py b/test/worker/test_compile.py new file mode 100644 index 0000000..7e1c96e --- /dev/null +++ b/test/worker/test_compile.py @@ -0,0 +1,35 @@ +import os + +from pathlib import Path +from bopytex.worker.compile import latexmk +import pytest + + +@pytest.fixture +def tex_path(tmp_path): + source = tmp_path / "source.tex" + with open(source, "w") as src: + src.write( + """ +\\documentclass{article} + +\\begin{document} +First document. This is a simple example, with no +extra parameters or packages included. +\\end{document} + """ + ) + return source + + +def test_compile(tex_path): + tmp_path = tex_path.parent + os.chdir(tmp_path) + + texfile = str(tex_path.name) + output = "source.pdf" + + for err in latexmk({}, [texfile], "source.pdf"): + assert 0 + + assert Path(output).exists From 527ad160cfd2bfecbf2c6e8d5df31db279763540 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 10:46:53 +0200 Subject: [PATCH 41/76] Feat: join_pdf work --- bopytex/worker/join_pdf.py | 17 ++++++++++++ test/worker/source.pdf | Bin 0 -> 5193 bytes test/worker/test_join_pdf.py | 51 +++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 bopytex/worker/join_pdf.py create mode 100644 test/worker/source.pdf create mode 100644 test/worker/test_join_pdf.py diff --git a/bopytex/worker/join_pdf.py b/bopytex/worker/join_pdf.py new file mode 100644 index 0000000..45927f1 --- /dev/null +++ b/bopytex/worker/join_pdf.py @@ -0,0 +1,17 @@ +import subprocess + +def pdfjam(args: dict, deps, output): + joining_process = subprocess.Popen( + ["pdfjam"] + deps + ["-o", output], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + # exit_code is always 66... + exit_code = joining_process.wait() + + if exit_code == 0: + yield "pdfjam success" + else: + yield "pdfjam failed" + diff --git a/test/worker/source.pdf b/test/worker/source.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e74542756c47b43f658665a8ea19c93c7605ead2 GIT binary patch literal 5193 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

t%lkT61B%I4o-t^g-Yv|U0!@&ZEMO|V##w- zykh2gfu6QBUlv=0a32@RK4*9{R#voa@$%RLCau$-&s@+gB%$6|RXP2=cXZ*TO)o*2 zbXT3ZjRs-;I#saSG(eH4FW>7k;9Ylj(bK7EecL|4!sr9x(bs22dnDrNZKMcAKC5W1 z!F`?s;(=PB&w{CpKNcS+qFJVC4e1TF`-W^B8NDxik2JJZ8T5bhuBfIYoLP=HcNyU( z1Nk3>xi=guum%dr+_3vrLJYPtsp*I21G0#o`~=H9MoR1dym`Y4vh^^xePK?>~EayNNut1}=0Y0V$6 z?J>c6qjN%&tvl%cEw7)`S{Z=GSqn2gQ1+?i);lt)zp2one^luIj#5cTOZ`Cw{#l7X zO>TExg3yKU_)+X!jp7=Haf51<64P{F*=xmIF$|#o+4g|Pdl^JF7vvH@qB)kYO0o3u zll;X)S?!=rnnj(8dvyI=f|JUHq@RHzwYVGFx174dNzs?2ZNc$F+nJj+He_pkHD%+e zjK4w=6`)2Mj_$I@{N)Qg%cy9%2<`aYJAuF6%^EcB;u=v%QCs%yMKLocPCel}(HD-x zm&pk2j2TJ>)EW_%KPB+i9`EbXCf~#+gE*JBlss-JDg}qt31$Jc%jjk@52ZS*X8xnk z?Bb%rcV`;7vNUrItIU#mb3_^}=5ux;fc{@NY7$t)nja2>IK$sJhe)c}>oSIQPL4`> z`Ip`RCO%i_LK^(2Z7etjaJq>m;5}l~GM5J5NfMcVq$Ji7Cw-8x(j6Fm1$4<=SOkAg zkL)-WNA%F0={uPU+(vS1R@YUflqJ|{IH@C|YTAvz+ou6PCyJ>%gfEvaYgp8}r})n@ z0KUIv8oqXYK2<%Rdyi_u{I)#O6JdI?J{I~E@Bl~ z4#EmoCtS916PDWpri?HLX_Ej!@6NmFPpBVcQ@cB@xlYbiChXQ1mphh!kaO+od7Sh5 z2)WBrv(MH2H^CzDN3iI6x}kBl1O_gkjk7ku7!$ZURQ$G>Ac5Zz*f!RUFhJ=2U(vzM zRfWLQfdVRUF^HHLL;@-Xg-VD^iVH);_#qH}f*#Th{hw3}Jgwc`F=zs3w08C)FiBkl zRpZ-gIA>=iYggCbPz>xHynuw807*lMU|s}hgz@wu7zh3jy8p%m)OPU300}UlU`0<` zdxGeS^A!{%g!k_$|E9-Mf6VhYO$CB=Y#jBmEp8 z6o2S?jc37%8q_99MkMt1C6Jl4?g{9bVyL0LMwL-eLksCo-w8h};q?PwU7b1Y) zc-wf(^)vV(b&|Jy+FjupP*Vr#7J1DWiYKaFAP6HO%b*iSobhzV&9#Bb3;mSFJG2n4L5Mw3kq;3(de8`vrx*QFO#q+_;U?qc9UWSD$<$%272<6b zxv~&L4TFLh|57FEIsw0iB(WxWQFO+m&Xe9;kB z{W@-8Ulj)qvfFhJ>{<{THebRUYFisbTW01(S7XLj&sN~h=3NCtQ>$mgi8|~1q<3az zbWT0uQev$@BHIy5b)04(iAs;;;U;)8a&~*1V?MnYfWV%0~xzdkbogB zwc55@6?@H>tsl1!?P!>YA1qOC$zrB zFjP5|)6}3<$%lf~M8f6KlNb+Xi*vpzH{3U;lCvd)BR<5`mS*c~K^3gTj={)hDFswx zA^o*RVX=}^gCBQRi#t!Ao-;~vtld=f<1GA}%#-|=h&0y2*a5)^S2V^KXi6A`N&(G* zV0{OF%3n~OXSusUPRTUUiT15#a z29=S9iAz8fm1UG9p)h5Lin5aG-T#jY!Op+?fk;9A$cF!Wlyp3RZ|f$xr?W Qkbp@`Q}FSr>Znot7owO!-~a#s literal 0 HcmV?d00001 diff --git a/test/worker/test_join_pdf.py b/test/worker/test_join_pdf.py new file mode 100644 index 0000000..5edd7a4 --- /dev/null +++ b/test/worker/test_join_pdf.py @@ -0,0 +1,51 @@ +import os + +import shutil +from pathlib import Path +from bopytex.worker.join_pdf import pdfjam +import pytest + + +@pytest.fixture +def multiple_pdf(tmp_path, request): + this_file = Path(request.module.__file__) + + source = this_file.parent / "source.pdf" + assert source.exists() + qty = 3 + dests = [] + for i in range(qty): + dest = tmp_path / f"source_{i}.pdf" + shutil.copyfile(source, dest) + assert dest.exists() + dests.append(dest) + + return dests + + +def test_join_pdf(multiple_pdf): + tmp_path = multiple_pdf[0].parent + os.chdir(tmp_path) + + deps = [str(d.name) for d in multiple_pdf] + + output = "joined.pdf" + + for msg in pdfjam({"pwd": Path.cwd()}, deps, output): + assert msg == "pdfjam success" + + assert Path(output).exists() + + +def test_join_pdf_failed(multiple_pdf): + tmp_path = multiple_pdf[0].parent + os.chdir(tmp_path) + + deps = [str(d.name) for d in multiple_pdf] + ["doesnotexists.pdf"] + + output = "joined.pdf" + + for msg in pdfjam({"pwd": Path.cwd()}, deps, output): + assert msg == "pdfjam failed" + + assert not Path(output).exists() From ca0f498d4e47a05996ab9bb3cf8eecba16ada7a5 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 11:29:30 +0200 Subject: [PATCH 42/76] fix: list to set in typehints --- bopytex/scheduler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 212c726..1004136 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -31,12 +31,12 @@ class Scheduler: ] @property - def all_deps(self) -> list[str]: + def all_deps(self) -> set[str]: """List dependencies of all tasks""" return {d for task in self.tasks for d in task.deps} @property - def all_output(self) -> list[str]: + def all_output(self) -> set[str]: """List ouput of all tasks""" return {task.output for task in self.tasks} From 9c05ef1551a2d707cf48a1e5dcfe2204079f16c6 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 12:26:04 +0200 Subject: [PATCH 43/76] feat: integrate message to worker --- bopytex/message.py | 33 +++++++++++++++++++++++++++++++++ bopytex/worker/compile.py | 3 ++- bopytex/worker/generate.py | 14 +++++++++++--- bopytex/worker/join_pdf.py | 10 +++------- test/worker/test_compile.py | 6 +++--- test/worker/test_generate.py | 9 ++++++--- test/worker/test_join_pdf.py | 8 ++++---- 7 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 bopytex/message.py diff --git a/bopytex/message.py b/bopytex/message.py new file mode 100644 index 0000000..bdfb69c --- /dev/null +++ b/bopytex/message.py @@ -0,0 +1,33 @@ +class Message(): + def __init__(self, status, out, err): + self._status = status + self._out = out + self._err = err + + @property + def status(self): + return self._status + + @property + def out(self): + return self._out + + @property + def err(self): + return self._err + +class SubprocessMessage(Message): + def __init__(self, process): + self._process = process + + @property + def status(self): + return self._process.wait() + + @property + def out(self): + return self._process.stdout + + @property + def err(self): + return self._process.stderr diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py index 24a9531..256c0f5 100644 --- a/bopytex/worker/compile.py +++ b/bopytex/worker/compile.py @@ -1,4 +1,5 @@ import subprocess +from ..message import SubprocessMessage def latexmk(args: dict, deps, output): @@ -8,5 +9,5 @@ def latexmk(args: dict, deps, output): stderr=subprocess.PIPE, universal_newlines=True ) - yield from compile_process.stderr + return SubprocessMessage(compile_process) diff --git a/bopytex/worker/generate.py b/bopytex/worker/generate.py index ff80f76..b6a9cf3 100644 --- a/bopytex/worker/generate.py +++ b/bopytex/worker/generate.py @@ -1,12 +1,20 @@ from jinja2.environment import Template +from bopytex.message import Message + def generate(args, deps, output): env = args["jinja2"]["environment"] template = env.get_template(deps[0]) - with open(output, "w") as out: - out.write(tpl2tex(template, metas=args)) - yield f"GENERATE - {deps[0]} to {output}" + + try: + with open(output, "w") as out: + out.write(tpl2tex(template, metas=args)) + + return Message(0, [f"GENERATE - {deps[0]} to {output}"], []) + + except Exception as e: + return Message(0, [], [e]) def tpl2tex(template: Template, metas: dict = {}) -> str: diff --git a/bopytex/worker/join_pdf.py b/bopytex/worker/join_pdf.py index 45927f1..2a44f04 100644 --- a/bopytex/worker/join_pdf.py +++ b/bopytex/worker/join_pdf.py @@ -1,5 +1,7 @@ import subprocess +from bopytex.message import SubprocessMessage + def pdfjam(args: dict, deps, output): joining_process = subprocess.Popen( ["pdfjam"] + deps + ["-o", output], @@ -7,11 +9,5 @@ def pdfjam(args: dict, deps, output): stderr=subprocess.PIPE, universal_newlines=True, ) - # exit_code is always 66... - exit_code = joining_process.wait() - - if exit_code == 0: - yield "pdfjam success" - else: - yield "pdfjam failed" + return SubprocessMessage(joining_process) diff --git a/test/worker/test_compile.py b/test/worker/test_compile.py index 7e1c96e..edd2fc3 100644 --- a/test/worker/test_compile.py +++ b/test/worker/test_compile.py @@ -22,14 +22,14 @@ extra parameters or packages included. return source -def test_compile(tex_path): +def test_latexmk(tex_path): tmp_path = tex_path.parent os.chdir(tmp_path) texfile = str(tex_path.name) output = "source.pdf" - for err in latexmk({}, [texfile], "source.pdf"): - assert 0 + message = latexmk({}, [texfile], "source.pdf") + assert message.status == 0 assert Path(output).exists diff --git a/test/worker/test_generate.py b/test/worker/test_generate.py index ab90ba7..fbd3baf 100644 --- a/test/worker/test_generate.py +++ b/test/worker/test_generate.py @@ -34,12 +34,15 @@ def test_generate(template_path, jinja2_env): template = str(template_path.name) output = "output" - result = next(generate( + message = generate( args={"a": 2, "jinja2": {"environment": jinja2_env}}, deps=[template], output=output, - )) - assert result == "GENERATE - template.j2 to output" + ) + print(message.err) + assert message.status == 0 + assert message.out == ["GENERATE - template.j2 to output"] + with open(output, "r") as out: lines = out.readlines() assert lines == ["Plop 2"] diff --git a/test/worker/test_join_pdf.py b/test/worker/test_join_pdf.py index 5edd7a4..26ae726 100644 --- a/test/worker/test_join_pdf.py +++ b/test/worker/test_join_pdf.py @@ -31,9 +31,9 @@ def test_join_pdf(multiple_pdf): output = "joined.pdf" - for msg in pdfjam({"pwd": Path.cwd()}, deps, output): - assert msg == "pdfjam success" + message = pdfjam({"pwd": Path.cwd()}, deps, output) + assert message.status == 0 assert Path(output).exists() @@ -45,7 +45,7 @@ def test_join_pdf_failed(multiple_pdf): output = "joined.pdf" - for msg in pdfjam({"pwd": Path.cwd()}, deps, output): - assert msg == "pdfjam failed" + message = pdfjam({"pwd": Path.cwd()}, deps, output) + assert message.status == 66 assert not Path(output).exists() From b455ef23c41631956f328513ab19e8bf360d813f Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 15:09:08 +0200 Subject: [PATCH 44/76] Feat: Integrate message in service, scheduler and dispatcher --- bopytex/message.py | 3 ++ bopytex/scheduler.py | 12 ++++-- bopytex/service.py | 6 +-- bopytex/worker/__init__.py | 13 ++----- test/fakes/__init__.py | 0 test/fakes/dispatcher.py | 10 +++++ test/fakes/planner.py | 9 +++++ test/fakes/workers.py | 13 +++++++ test/test_scheduler.py | 78 +++++++++++++++++++++----------------- test/test_service.py | 8 ++-- 10 files changed, 100 insertions(+), 52 deletions(-) create mode 100644 test/fakes/__init__.py create mode 100644 test/fakes/dispatcher.py create mode 100644 test/fakes/planner.py create mode 100644 test/fakes/workers.py diff --git a/bopytex/message.py b/bopytex/message.py index bdfb69c..0c2a54f 100644 --- a/bopytex/message.py +++ b/bopytex/message.py @@ -16,6 +16,9 @@ class Message(): def err(self): return self._err + def __repr__(self): + return f"Message(status={self.status}, out={self.out}, err={self.err})" + class SubprocessMessage(Message): def __init__(self, process): self._process = process diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index 1004136..ab8b518 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -2,12 +2,14 @@ from bopytex.tasks import Task +from bopytex.worker import Dispatcher class Scheduler: """Scheduler is responsible of getting tasks (the tasks) and yield those that can be done""" - def __init__(self, done: list[str] = None): + def __init__(self, dispatcher:Dispatcher, done: list[str] = None): + self._dispatcher = dispatcher if done is None: self._done = [] @@ -56,9 +58,13 @@ class Scheduler: except IndexError: raise StopIteration - self._done.append(task.output) self._tasks.remove(task) - return task + message = self._dispatcher(task) + + if message.status == 0: + self._done.append(task.output) + + return message def backlog(self): """ Yield tasks sorted according to dependencies """ diff --git a/bopytex/service.py b/bopytex/service.py index 217ed67..4e1a429 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -15,11 +15,11 @@ def orcherstrator( ): tasks = planner(options) - scheduler = Scheduler([options["template"]]) + scheduler = Scheduler(dispatcher, [options["template"]]) scheduler.append(tasks) - for task in scheduler.backlog(): - yield from dispatcher(task) + for message in scheduler.backlog(): + yield message # ----------------------------- diff --git a/bopytex/worker/__init__.py b/bopytex/worker/__init__.py index fa48f63..cf084dd 100644 --- a/bopytex/worker/__init__.py +++ b/bopytex/worker/__init__.py @@ -10,14 +10,9 @@ class Dispatcher: try: choosen_action = self._actions[task.action] except KeyError: - raise ActionNotFound(f"The action {task.action} is not in {self._actions.keys()}") + raise ActionNotFound( + f"The action {task.action} is not in {self._actions.keys()}" + ) - return choosen_action( - args=task.args, - deps=task.deps, - output=task.output - ) - -def fake_worker(args, deps, output): - yield f"FAKE - {args} - {deps} - {output}" + return choosen_action(args=task.args, deps=task.deps, output=task.output) diff --git a/test/fakes/__init__.py b/test/fakes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/fakes/dispatcher.py b/test/fakes/dispatcher.py new file mode 100644 index 0000000..a0fa672 --- /dev/null +++ b/test/fakes/dispatcher.py @@ -0,0 +1,10 @@ +from bopytex.worker import Dispatcher +from .workers import fake_worker, success_worker, fail_worker + +fake_dispatcher = Dispatcher( + { + "FAKE": fake_worker, + "SUCCESS": success_worker, + "FAILURE": fail_worker, + } +) diff --git a/test/fakes/planner.py b/test/fakes/planner.py new file mode 100644 index 0000000..dd50afe --- /dev/null +++ b/test/fakes/planner.py @@ -0,0 +1,9 @@ +from bopytex.tasks import Task + + +def simple(options: dict) -> list[Task]: + """Simple planner with options['quantity'] tasks and no dependencies""" + return [ + Task("FAKE", args={"number": i}, deps=[], output=f"{i}") + for i in range(options["quantity"]) + ] diff --git a/test/fakes/workers.py b/test/fakes/workers.py new file mode 100644 index 0000000..fbae507 --- /dev/null +++ b/test/fakes/workers.py @@ -0,0 +1,13 @@ +from bopytex.message import Message + +def fake_worker(args, deps, output): + return Message(0, [f"FAKE - {args} - {deps} - {output}"], []) + + +def success_worker(args, deps, output): + return Message(0, [f"SUCCESS - {args} - {deps} - {output}"], []) + + +def fail_worker(): + return Message(1, [f"FAILURE - {args} - {deps} - {output}"], []) + diff --git a/test/test_scheduler.py b/test/test_scheduler.py index 4ac1475..f293cba 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -1,13 +1,15 @@ +from bopytex.message import Message from bopytex.tasks import Task from bopytex.scheduler import Scheduler +from .fakes.dispatcher import fake_dispatcher import pytest def test_schedule_append(): - scheduler = Scheduler() + scheduler = Scheduler(dispatcher=fake_dispatcher) tasks = [ - Task(action="FOO", args={}, deps=["dep1", "dep2"], output="end1"), - Task(action="FOO", args={}, deps=["dep1", "dep3"], output="end2"), + Task(action="FAKE", args={}, deps=["dep1", "dep2"], output="end1"), + Task(action="FAKE", args={}, deps=["dep1", "dep3"], output="end2"), ] scheduler.append(tasks) assert scheduler.tasks == tasks @@ -16,106 +18,114 @@ def test_schedule_append(): def test_schedule_one_task(): - scheduler = Scheduler() - tasks = [Task(action="FOO", args={}, deps=[], output="end")] + scheduler = Scheduler(dispatcher=fake_dispatcher) + tasks = [Task(action="FAKE", args={}, deps=[], output="end")] scheduler.append(tasks) assert scheduler.doable_tasks == tasks result = scheduler.next_task() - assert result == tasks[0] + assert result.status == 0 + assert result.out == ["FAKE - {} - [] - end"] + assert result.err == [] + assert scheduler.tasks == [] assert scheduler.done == ["end"] def test_schedule_one_task_with_args(): - scheduler = Scheduler() - tasks = [Task(action="FOO", args={"task": "one"}, deps=[], output="one")] + scheduler = Scheduler(dispatcher=fake_dispatcher) + tasks = [Task(action="FAKE", args={"task": "one"}, deps=[], output="one")] scheduler.append(tasks) result = scheduler.next_task() - assert result == tasks[0] + assert result.status == 0 + assert result.out == ["FAKE - {'task': 'one'} - [] - one"] + assert result.err == [] + assert scheduler.tasks == [] assert scheduler.done == ["one"] def test_schedule_multiple_tasks(): - scheduler = Scheduler() - t1 = Task(action="FOO", args={"task": "one"}, deps=[], output="one") - t2 = Task(action="FOO", args={"task": "two"}, deps=[], output="two") - t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") + scheduler = Scheduler(dispatcher=fake_dispatcher) + t1 = Task(action="FAKE", args={"task": "one"}, deps=[], output="one") + t2 = Task(action="FAKE", args={"task": "two"}, deps=[], output="two") + t3 = Task(action="FAKE", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) assert scheduler.doable_tasks == [t1, t2, t3] assert scheduler.is_finishable() result = scheduler.next_task() - assert result == t1 + assert result.status == 0 assert scheduler.tasks == [t2, t3] assert scheduler.done == ["one"] result = scheduler.next_task() - assert result == t2 + assert result.status == 0 assert scheduler.tasks == [t3] assert scheduler.done == ["one", "two"] result = scheduler.next_task() - assert result == t3 + assert result.status == 0 assert scheduler.tasks == [] assert scheduler.done == ["one", "two", "three"] def test_schedule_multiple_tasks_with_dependencies(): - scheduler = Scheduler() - t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") - t2 = Task(action="FOO", args={"task": "two"}, deps=["one"], output="two") - t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") + scheduler = Scheduler(dispatcher=fake_dispatcher) + t1 = Task(action="FAKE", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FAKE", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="FAKE", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) assert scheduler.doable_tasks == [t3] assert scheduler.is_finishable() result = scheduler.next_task() - assert result == t3 + assert result.status == 0 assert scheduler.tasks == [t1, t2] assert scheduler.done == ["three"] assert scheduler.doable_tasks == [t1] result = scheduler.next_task() - assert result == t1 + assert result.status == 0 assert scheduler.tasks == [t2] assert scheduler.done == ["three", "one"] assert scheduler.doable_tasks == [t2] result = scheduler.next_task() - assert result == t2 + assert result.status == 0 assert scheduler.tasks == [] assert scheduler.done == ["three", "one", "two"] + def test_schedule_multiple_tasks_with_dependencies_loop(): - scheduler = Scheduler() - t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") - t2 = Task(action="FOO", args={"task": "two"}, deps=["one"], output="two") - t3 = Task(action="FOO", args={"task": "three"}, deps=[], output="three") + scheduler = Scheduler(dispatcher=fake_dispatcher) + t1 = Task(action="FAKE", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FAKE", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="FAKE", args={"task": "three"}, deps=[], output="three") scheduler.append([t1, t2, t3]) - ordered_tasks = [] for task in scheduler.backlog(): - ordered_tasks.append(task) - assert ordered_tasks == [t3, t1, t2] + pass + + assert scheduler.done == ["three", "one", "two"] + def test_schedule_empty_task(): - scheduler = Scheduler() + scheduler = Scheduler(dispatcher=fake_dispatcher) scheduler.append([]) with pytest.raises(StopIteration): scheduler.next_task() def test_schedule_multiple_tasks_with_undoable_dependencies(): - scheduler = Scheduler() - t1 = Task(action="FOO", args={"task": "one"}, deps=["three"], output="one") - t2 = Task(action="FOO", args={"task": "two"}, deps=[], output="two") + scheduler = Scheduler(dispatcher=fake_dispatcher) + t1 = Task(action="FAKE", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FAKE", args={"task": "two"}, deps=[], output="two") scheduler.append([t1, t2]) assert scheduler.doable_tasks == [t2] diff --git a/test/test_service.py b/test/test_service.py index 2e6970a..80138f3 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -1,7 +1,8 @@ from bopytex.planner import fake_planner from bopytex.service import orcherstrator from bopytex.tasks import Task -from bopytex.worker import Dispatcher, fake_worker +from bopytex.worker import Dispatcher +from .fakes.workers import fake_worker def test_service(): @@ -10,5 +11,6 @@ def test_service(): service = orcherstrator(options, fake_planner.simple, dispatcher) - for i, task_output in enumerate(service): - assert task_output == f"FAKE - {{'number': {i}}} - [] - {i}" + for i, message in enumerate(service): + assert message.status == 0 + assert message.out == [f"FAKE - {{'number': {i}}} - [] - {i}"] From fe18dc4ef173a7ece97fdcb2913c5652460957f1 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 15:20:34 +0200 Subject: [PATCH 45/76] Feat: manage failing tasks in scheduler --- bopytex/scheduler.py | 24 +++++++++++++++--------- test/fakes/workers.py | 2 +- test/test_scheduler.py | 42 ++++++++++++++++++++++++++++++++---------- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/bopytex/scheduler.py b/bopytex/scheduler.py index ab8b518..4c46ade 100644 --- a/bopytex/scheduler.py +++ b/bopytex/scheduler.py @@ -8,15 +8,16 @@ from bopytex.worker import Dispatcher class Scheduler: """Scheduler is responsible of getting tasks (the tasks) and yield those that can be done""" - def __init__(self, dispatcher:Dispatcher, done: list[str] = None): + def __init__(self, dispatcher:Dispatcher, output_done: list[str] = None): self._dispatcher = dispatcher - if done is None: - self._done = [] + if output_done is None: + self._output_done = [] else: - self._done = done + self._output_done = output_done self._tasks = [] + self._failed_tasks = [] @property def tasks(self) -> list[Task]: @@ -29,7 +30,7 @@ class Scheduler: return [ task for task in self.tasks - if not task.deps or all([d in self.done for d in task.deps]) + if not task.deps or all([d in self.output_done for d in task.deps]) ] @property @@ -43,8 +44,12 @@ class Scheduler: return {task.output for task in self.tasks} @property - def done(self) -> list[str]: - return self._done + def output_done(self) -> list[str]: + return self._output_done + + @property + def failed_tasks(self) -> list[Task]: + return self._failed_tasks def append(self, tasks: list[Task]): self._tasks += tasks @@ -60,9 +65,10 @@ class Scheduler: self._tasks.remove(task) message = self._dispatcher(task) - if message.status == 0: - self._done.append(task.output) + self._output_done.append(task.output) + else: + self._failed_tasks.append(task) return message diff --git a/test/fakes/workers.py b/test/fakes/workers.py index fbae507..919ad1b 100644 --- a/test/fakes/workers.py +++ b/test/fakes/workers.py @@ -8,6 +8,6 @@ def success_worker(args, deps, output): return Message(0, [f"SUCCESS - {args} - {deps} - {output}"], []) -def fail_worker(): +def fail_worker(args, deps, output): return Message(1, [f"FAILURE - {args} - {deps} - {output}"], []) diff --git a/test/test_scheduler.py b/test/test_scheduler.py index f293cba..737637b 100644 --- a/test/test_scheduler.py +++ b/test/test_scheduler.py @@ -30,7 +30,7 @@ def test_schedule_one_task(): assert result.err == [] assert scheduler.tasks == [] - assert scheduler.done == ["end"] + assert scheduler.output_done == ["end"] def test_schedule_one_task_with_args(): @@ -45,7 +45,7 @@ def test_schedule_one_task_with_args(): assert result.err == [] assert scheduler.tasks == [] - assert scheduler.done == ["one"] + assert scheduler.output_done == ["one"] def test_schedule_multiple_tasks(): @@ -61,17 +61,17 @@ def test_schedule_multiple_tasks(): result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [t2, t3] - assert scheduler.done == ["one"] + assert scheduler.output_done == ["one"] result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [t3] - assert scheduler.done == ["one", "two"] + assert scheduler.output_done == ["one", "two"] result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [] - assert scheduler.done == ["one", "two", "three"] + assert scheduler.output_done == ["one", "two", "three"] def test_schedule_multiple_tasks_with_dependencies(): @@ -87,19 +87,19 @@ def test_schedule_multiple_tasks_with_dependencies(): result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [t1, t2] - assert scheduler.done == ["three"] + assert scheduler.output_done == ["three"] assert scheduler.doable_tasks == [t1] result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [t2] - assert scheduler.done == ["three", "one"] + assert scheduler.output_done == ["three", "one"] assert scheduler.doable_tasks == [t2] result = scheduler.next_task() assert result.status == 0 assert scheduler.tasks == [] - assert scheduler.done == ["three", "one", "two"] + assert scheduler.output_done == ["three", "one", "two"] def test_schedule_multiple_tasks_with_dependencies_loop(): @@ -112,7 +112,7 @@ def test_schedule_multiple_tasks_with_dependencies_loop(): for task in scheduler.backlog(): pass - assert scheduler.done == ["three", "one", "two"] + assert scheduler.output_done == ["three", "one", "two"] def test_schedule_empty_task(): @@ -135,5 +135,27 @@ def test_schedule_multiple_tasks_with_undoable_dependencies(): pass assert scheduler.tasks == [t1] - assert scheduler.done == ["two"] + assert scheduler.output_done == ["two"] + assert scheduler.doable_tasks == [] + + +def test_schedule_multiple_tasks_with_failling_tasks(): + scheduler = Scheduler(dispatcher=fake_dispatcher) + t1 = Task(action="FAILURE", args={"task": "one"}, deps=["three"], output="one") + t2 = Task(action="FAKE", args={"task": "two"}, deps=["one"], output="two") + t3 = Task(action="FAKE", args={"task": "three"}, deps=[], output="three") + t4 = Task(action="FAILURE", args={"task": "four"}, deps=[], output="four") + scheduler.append([t1, t2, t3, t4]) + + assert scheduler.doable_tasks == [t3, t4] + assert scheduler.is_finishable() + + status = [] + for message in scheduler.backlog(): + status.append(message.status) + + assert status == [0, 1, 1] + assert scheduler.tasks == [t2] + assert scheduler.failed_tasks == [t1, t4] + assert scheduler.output_done == ["three"] assert scheduler.doable_tasks == [] From 5af4662d43d1295d8f2faf5eadfc530b5870b0d3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 20:41:34 +0200 Subject: [PATCH 46/76] Fix: use tmp_path fixture --- test/worker/test_compile.py | 4 ++-- test/worker/test_join_pdf.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/worker/test_compile.py b/test/worker/test_compile.py index edd2fc3..c046473 100644 --- a/test/worker/test_compile.py +++ b/test/worker/test_compile.py @@ -22,8 +22,8 @@ extra parameters or packages included. return source -def test_latexmk(tex_path): - tmp_path = tex_path.parent +def test_latexmk(tex_path, tmp_path): + #tmp_path = tex_path.parent os.chdir(tmp_path) texfile = str(tex_path.name) diff --git a/test/worker/test_join_pdf.py b/test/worker/test_join_pdf.py index 26ae726..5d6b843 100644 --- a/test/worker/test_join_pdf.py +++ b/test/worker/test_join_pdf.py @@ -37,8 +37,7 @@ def test_join_pdf(multiple_pdf): assert Path(output).exists() -def test_join_pdf_failed(multiple_pdf): - tmp_path = multiple_pdf[0].parent +def test_join_pdf_failed(multiple_pdf, tmp_path): os.chdir(tmp_path) deps = [str(d.name) for d in multiple_pdf] + ["doesnotexists.pdf"] From 1bcbf2a9a69b0acbc22b62054ee7dbc4c0034e4b Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 14 Apr 2022 11:29:39 +0200 Subject: [PATCH 47/76] core: remove tests from .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 09b9778..0f1edb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -tests/ __pycache__/ *.pyc documentation/build/ From 1fdf223689bf2594e925c2e6a5adcbec18d1c7eb Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 13 Apr 2022 22:14:27 +0200 Subject: [PATCH 48/76] Feat: e2e test on default planner --- bopytex/planner/default_planner.py | 2 +- bopytex/worker/compile.py | 5 +- test/test_e2e.py | 61 +++++++++++++++++++ test/test_planner.py | 94 ++++++++++++++++++++++++++---- 4 files changed, 148 insertions(+), 14 deletions(-) create mode 100644 test/test_e2e.py diff --git a/bopytex/planner/default_planner.py b/bopytex/planner/default_planner.py index 5d56286..6151061 100644 --- a/bopytex/planner/default_planner.py +++ b/bopytex/planner/default_planner.py @@ -68,7 +68,7 @@ def default_tasks_builder( for subject in subjects: source = naming.template2source(template, subject) - tasks.append(generate(template, subject, source)) + tasks.append(generate(template, {**subject, **opt}, source)) if not no_pdf: pdf = naming.source2pdf(source) diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py index 256c0f5..f5ea32f 100644 --- a/bopytex/worker/compile.py +++ b/bopytex/worker/compile.py @@ -1,4 +1,6 @@ import subprocess + +from bopytex.message import Message from ..message import SubprocessMessage @@ -9,5 +11,6 @@ def latexmk(args: dict, deps, output): stderr=subprocess.PIPE, universal_newlines=True ) - return SubprocessMessage(compile_process) + #return SubprocessMessage(compile_process) + return Message(compile_process.wait(), list(compile_process.stdout), list(compile_process.stderr)) diff --git a/test/test_e2e.py b/test/test_e2e.py new file mode 100644 index 0000000..a921289 --- /dev/null +++ b/test/test_e2e.py @@ -0,0 +1,61 @@ +import os +import jinja2 +from pathlib import Path +from bopytex.message import Message +from bopytex.planner.default_planner import default_planner +from bopytex.service import orcherstrator +from bopytex.worker import Dispatcher +from bopytex.worker.clean import clean +from bopytex.worker.compile import latexmk +from bopytex.worker.generate import generate +from bopytex.worker.join_pdf import pdfjam +import pytest + + +@pytest.fixture +def template_path(tmp_path): + template = tmp_path / "tpl_source.tex" + with open(template, "w") as tpl: + tpl.write( + """ +\\documentclass{article} + +\\begin{document} +First document. + +Subject {{ subject }} +\\end{document} + """ + ) + return template + + +@pytest.fixture +def jinja2_env(tmp_path): + templateEnv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmp_path)) + return templateEnv + + +def test_with_default_planner(template_path, jinja2_env, tmp_path): + os.chdir(tmp_path) + + options = { + "template": str(template_path.name), + "quantity_subjects": 3, + "corr": False, + "no_join": False, + "no_pdf": False, + "jinja2": { + "environment": jinja2_env, + }, + } + dispatcher = Dispatcher( + {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} + ) + orcherstre = orcherstrator(options, planner=default_planner, dispatcher=dispatcher) + + messages = [] + for message in orcherstre: + assert message.status == 0 + + assert Path("joined_source.pdf").exists() diff --git a/test/test_planner.py b/test/test_planner.py index 0317abf..a1c1dbd 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -14,13 +14,27 @@ def test_tasks_builder_generate(): assert tasks == [ Task( action="GENERATE", - args={"number": "01"}, + args={ + "number": "01", + "corr": False, + "no_join": False, + "no_pdf": True, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="01_source.tex", ), Task( action="GENERATE", - args={"number": "02"}, + args={ + "number": "02", + "corr": False, + "no_join": False, + "no_pdf": True, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="02_source.tex", ), @@ -38,7 +52,14 @@ def test_tasks_builder_generate_compile(): assert tasks == [ Task( action="GENERATE", - args={"number": "01"}, + args={ + "number": "01", + "corr": False, + "no_join": True, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="01_source.tex", ), @@ -50,7 +71,14 @@ def test_tasks_builder_generate_compile(): ), Task( action="GENERATE", - args={"number": "02"}, + args={ + "number": "02", + "corr": False, + "no_join": True, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="02_source.tex", ), @@ -73,7 +101,14 @@ def test_tasks_builder_generate_compile_join(): assert tasks == [ Task( action="GENERATE", - args={"number": "01"}, + args={ + "number": "01", + "corr": False, + "no_join": False, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="01_source.tex", ), @@ -85,7 +120,14 @@ def test_tasks_builder_generate_compile_join(): ), Task( action="GENERATE", - args={"number": "02"}, + args={ + "number": "02", + "corr": False, + "no_join": False, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="02_source.tex", ), @@ -109,14 +151,21 @@ def test_tasks_builder_generate_compile_corr(): options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], - "corr": 1, - "no_join": 1, + "corr": True, + "no_join": True, } ) assert tasks == [ Task( action="GENERATE", - args={"number": "01"}, + args={ + "number": "01", + "corr": True, + "no_join": True, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="01_source.tex", ), @@ -140,7 +189,14 @@ def test_tasks_builder_generate_compile_corr(): ), Task( action="GENERATE", - args={"number": "02"}, + args={ + "number": "02", + "corr": True, + "no_join": True, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="02_source.tex", ), @@ -177,7 +233,14 @@ def test_tasks_builder_generate_compile_corr_joined(): assert tasks == [ Task( action="GENERATE", - args={"number": "01"}, + args={ + "number": "01", + "corr": True, + "no_join": False, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="01_source.tex", ), @@ -201,7 +264,14 @@ def test_tasks_builder_generate_compile_corr_joined(): ), Task( action="GENERATE", - args={"number": "02"}, + args={ + "number": "02", + "corr": True, + "no_join": False, + "no_pdf": False, + "template": "tpl_source.tex", + "subjects": [{"number": "01"}, {"number": "02"}], + }, deps=["tpl_source.tex"], output="02_source.tex", ), From ca2a47f82ab5168f8f5404778a2577a7f20edf8a Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 16 Apr 2022 07:14:49 +0200 Subject: [PATCH 49/76] Fix: empty clean worker --- bopytex/worker/clean.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 bopytex/worker/clean.py diff --git a/bopytex/worker/clean.py b/bopytex/worker/clean.py new file mode 100644 index 0000000..593acaa --- /dev/null +++ b/bopytex/worker/clean.py @@ -0,0 +1,2 @@ +def clean(args: dict, deps, output): + pass From 04db450ceb88cab607b4a9002e5778184581811e Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 16 Apr 2022 07:30:47 +0200 Subject: [PATCH 50/76] Feat: remise sur pieds pour les tests --- bopytex/planner/__init__.py | 2 - ... => activate_corr_compile_join_planner.py} | 6 +-- ...er.py => generate_compile_join_planner.py} | 9 ++-- test/test_e2e.py | 4 +- test/test_planner.py | 54 +++++++------------ 5 files changed, 28 insertions(+), 47 deletions(-) rename bopytex/planner/{only_corr_planner.py => activate_corr_compile_join_planner.py} (91%) rename bopytex/planner/{default_planner.py => generate_compile_join_planner.py} (92%) diff --git a/bopytex/planner/__init__.py b/bopytex/planner/__init__.py index 9ceb248..e69de29 100644 --- a/bopytex/planner/__init__.py +++ b/bopytex/planner/__init__.py @@ -1,2 +0,0 @@ -from .default_planner import default_planner -from .only_corr_planner import only_corr_planner diff --git a/bopytex/planner/only_corr_planner.py b/bopytex/planner/activate_corr_compile_join_planner.py similarity index 91% rename from bopytex/planner/only_corr_planner.py rename to bopytex/planner/activate_corr_compile_join_planner.py index 4c05f2a..a67c20f 100644 --- a/bopytex/planner/only_corr_planner.py +++ b/bopytex/planner/activate_corr_compile_join_planner.py @@ -11,16 +11,16 @@ def list_files(dir=".", accept=lambda _: True, reject=lambda _: False): return files -def only_corr_planner(options: dict) -> list[Task]: +def planner(options: dict) -> list[Task]: sources = list_files( accept=lambda x: x.endswith(".tex"), reject=lambda x: x.startswith("tpl_"), ) options["sources"] = sources - return only_corr_tasks_builder(options) + return tasks_builder(options) -def only_corr_tasks_builder( +def tasks_builder( options: dict, ) -> list[Task]: opt = { diff --git a/bopytex/planner/default_planner.py b/bopytex/planner/generate_compile_join_planner.py similarity index 92% rename from bopytex/planner/default_planner.py rename to bopytex/planner/generate_compile_join_planner.py index 6151061..28e30cb 100644 --- a/bopytex/planner/default_planner.py +++ b/bopytex/planner/generate_compile_join_planner.py @@ -20,7 +20,7 @@ def build_subject_list_from_qty(qty: int) -> list[dict]: return subjects -def default_planner(options: dict) -> list[Task]: +def planner(options: dict) -> list[Task]: try: students_csv = options["students_csv"] @@ -37,10 +37,10 @@ def default_planner(options: dict) -> list[Task]: infos = csv.DictReader(csv_file) options["subjects"] = build_subject_list_from_infos(infos) - return default_tasks_builder(options) + return tasks_builder(options) -def default_tasks_builder( +def tasks_builder( options: dict, ) -> list[Task]: @@ -67,8 +67,9 @@ def default_tasks_builder( for subject in subjects: source = naming.template2source(template, subject) + args = {**subject, **options} - tasks.append(generate(template, {**subject, **opt}, source)) + tasks.append(generate(template, args, source)) if not no_pdf: pdf = naming.source2pdf(source) diff --git a/test/test_e2e.py b/test/test_e2e.py index a921289..d7487b3 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -2,7 +2,7 @@ import os import jinja2 from pathlib import Path from bopytex.message import Message -from bopytex.planner.default_planner import default_planner +from bopytex.planner.generate_compile_join_planner import planner from bopytex.service import orcherstrator from bopytex.worker import Dispatcher from bopytex.worker.clean import clean @@ -52,7 +52,7 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): dispatcher = Dispatcher( {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} ) - orcherstre = orcherstrator(options, planner=default_planner, dispatcher=dispatcher) + orcherstre = orcherstrator(options, planner=planner, dispatcher=dispatcher) messages = [] for message in orcherstre: diff --git a/test/test_planner.py b/test/test_planner.py index a1c1dbd..6a73463 100644 --- a/test/test_planner.py +++ b/test/test_planner.py @@ -1,10 +1,10 @@ -from bopytex.planner.default_planner import default_tasks_builder -from bopytex.planner.only_corr_planner import only_corr_tasks_builder +from bopytex.planner.generate_compile_join_planner import tasks_builder as gcj_task_builder +from bopytex.planner.activate_corr_compile_join_planner import tasks_builder as accj_task_builder from bopytex.tasks import Task def test_tasks_builder_generate(): - tasks = default_tasks_builder( + tasks = gcj_task_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -16,11 +16,9 @@ def test_tasks_builder_generate(): action="GENERATE", args={ "number": "01", - "corr": False, - "no_join": False, - "no_pdf": True, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "no_pdf": True, }, deps=["tpl_source.tex"], output="01_source.tex", @@ -29,11 +27,9 @@ def test_tasks_builder_generate(): action="GENERATE", args={ "number": "02", - "corr": False, - "no_join": False, - "no_pdf": True, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "no_pdf": True, }, deps=["tpl_source.tex"], output="02_source.tex", @@ -42,7 +38,7 @@ def test_tasks_builder_generate(): def test_tasks_builder_generate_compile(): - tasks = default_tasks_builder( + tasks = gcj_task_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -54,11 +50,9 @@ def test_tasks_builder_generate_compile(): action="GENERATE", args={ "number": "01", - "corr": False, - "no_join": True, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "no_join": True, }, deps=["tpl_source.tex"], output="01_source.tex", @@ -73,11 +67,9 @@ def test_tasks_builder_generate_compile(): action="GENERATE", args={ "number": "02", - "corr": False, - "no_join": True, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "no_join": True, }, deps=["tpl_source.tex"], output="02_source.tex", @@ -92,7 +84,7 @@ def test_tasks_builder_generate_compile(): def test_tasks_builder_generate_compile_join(): - tasks = default_tasks_builder( + tasks = gcj_task_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -103,9 +95,6 @@ def test_tasks_builder_generate_compile_join(): action="GENERATE", args={ "number": "01", - "corr": False, - "no_join": False, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], }, @@ -122,9 +111,6 @@ def test_tasks_builder_generate_compile_join(): action="GENERATE", args={ "number": "02", - "corr": False, - "no_join": False, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], }, @@ -147,7 +133,7 @@ def test_tasks_builder_generate_compile_join(): def test_tasks_builder_generate_compile_corr(): - tasks = default_tasks_builder( + tasks = gcj_task_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -160,11 +146,10 @@ def test_tasks_builder_generate_compile_corr(): action="GENERATE", args={ "number": "01", - "corr": True, - "no_join": True, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "corr": True, + "no_join": True, }, deps=["tpl_source.tex"], output="01_source.tex", @@ -191,11 +176,10 @@ def test_tasks_builder_generate_compile_corr(): action="GENERATE", args={ "number": "02", - "corr": True, - "no_join": True, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "corr": True, + "no_join": True, }, deps=["tpl_source.tex"], output="02_source.tex", @@ -222,7 +206,7 @@ def test_tasks_builder_generate_compile_corr(): def test_tasks_builder_generate_compile_corr_joined(): - tasks = default_tasks_builder( + tasks = gcj_task_builder( options={ "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], @@ -237,7 +221,6 @@ def test_tasks_builder_generate_compile_corr_joined(): "number": "01", "corr": True, "no_join": False, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], }, @@ -266,11 +249,10 @@ def test_tasks_builder_generate_compile_corr_joined(): action="GENERATE", args={ "number": "02", - "corr": True, - "no_join": False, - "no_pdf": False, "template": "tpl_source.tex", "subjects": [{"number": "01"}, {"number": "02"}], + "corr": True, + "no_join": False, }, deps=["tpl_source.tex"], output="02_source.tex", @@ -309,7 +291,7 @@ def test_tasks_builder_generate_compile_corr_joined(): def test_only_corr_tasks_builder(): - tasks = only_corr_tasks_builder( + tasks = accj_task_builder( options={ "sources": ["01_source.tex", "02_source.tex"], } From 25b5bde82324365e5f76d724e99f53e04200dd0d Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 11:03:16 +0200 Subject: [PATCH 51/76] Feat: Create tex jinja2 environment --- bopytex/jinja2_env/texenv.py | 31 +++++++++++++++++++++++++++++++ test/test_texenv.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 bopytex/jinja2_env/texenv.py create mode 100644 test/test_texenv.py diff --git a/bopytex/jinja2_env/texenv.py b/bopytex/jinja2_env/texenv.py new file mode 100644 index 0000000..d1fb124 --- /dev/null +++ b/bopytex/jinja2_env/texenv.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import jinja2 + +__all__ = [ + "texenv", +] + +# Definition of jinja syntax for latex +texenv = jinja2.Environment( + block_start_string=r"\Block{", + block_end_string="}", + variable_start_string=r"\Var{", + variable_end_string="}", + comment_start_string=r"\#{", + comment_end_string="}", + line_statement_prefix="%-", + line_comment_prefix="%#", + loader=jinja2.ChoiceLoader( + [ + jinja2.FileSystemLoader(["./"]), + ] + ), + extensions=["jinja2.ext.do"], +) + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/test/test_texenv.py b/test/test_texenv.py new file mode 100644 index 0000000..dc0d353 --- /dev/null +++ b/test/test_texenv.py @@ -0,0 +1,31 @@ +from bopytex.jinja2_env.texenv import texenv + + +def test_variable_block(): + base_template = r"\Var{a}" + jinja2_template = texenv.from_string(base_template) + output = jinja2_template.render(a=2) + assert output == "2" + + +def test_block_string(): + base_template = r"\Block{set a = 2}\Var{a}" + jinja2_template = texenv.from_string(base_template) + output = jinja2_template.render() + assert output == "2" + + +def test_block_line_statement(): + base_template = r"""%-set a = 2 +\Var{a}""" + jinja2_template = texenv.from_string(base_template) + output = jinja2_template.render() + assert output == "2" + +def test_block_line_statement_with_comment(): + base_template = r"""%-set a = 2 +%# comment +\Var{a}""" + jinja2_template = texenv.from_string(base_template) + output = jinja2_template.render() + assert output == "\n2" From 87ebb4c28441a78d9f9000fca807de9e8ee85986 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 11:11:39 +0200 Subject: [PATCH 52/76] Feat: add test on add_filter --- test/test_texenv.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/test_texenv.py b/test/test_texenv.py index dc0d353..e68c5b4 100644 --- a/test/test_texenv.py +++ b/test/test_texenv.py @@ -29,3 +29,12 @@ def test_block_line_statement_with_comment(): jinja2_template = texenv.from_string(base_template) output = jinja2_template.render() assert output == "\n2" + +def test_add_filter(): + texenv.filters["count_caracters"] = lambda x: len(x) + base_template = r"""\Var{a} has \Var{a | count_caracters}""" + jinja2_template = texenv.from_string(base_template) + output = jinja2_template.render(a="coucou") + assert output == "coucou has 6" + + From 69b2e1c82e0652cac4151808b122c8cbfb83671c Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 11:39:28 +0200 Subject: [PATCH 53/76] Refact: move config and workflow to bopytex --- bopytex/bopytex.py | 16 ++++++++++++++++ bopytex/default_config.py | 13 +++++++++++++ test/test_e2e.py | 18 +++--------------- 3 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 bopytex/bopytex.py create mode 100644 bopytex/default_config.py diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py new file mode 100644 index 0000000..bfa345e --- /dev/null +++ b/bopytex/bopytex.py @@ -0,0 +1,16 @@ +import bopytex.default_config as DEFAULT +from bopytex.service import orcherstrator + + +def bopytex(**options): + + config = options.copy() + config["jinja2"] = {} + config["jinja2"]["environment"] = DEFAULT.jinja2_env + + orcherstre = orcherstrator( + options, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher + ) + for message in orcherstre: + print(message) + assert message.status == 0 diff --git a/bopytex/default_config.py b/bopytex/default_config.py new file mode 100644 index 0000000..864d804 --- /dev/null +++ b/bopytex/default_config.py @@ -0,0 +1,13 @@ +import jinja2 +from bopytex.planner.generate_compile_join_planner import planner +from bopytex.worker import Dispatcher +from bopytex.worker.clean import clean +from bopytex.worker.compile import latexmk +from bopytex.worker.generate import generate +from bopytex.worker.join_pdf import pdfjam + +jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader("./")) + +dispatcher = Dispatcher( + {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} +) diff --git a/test/test_e2e.py b/test/test_e2e.py index d7487b3..3368fa6 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -1,14 +1,7 @@ import os import jinja2 from pathlib import Path -from bopytex.message import Message -from bopytex.planner.generate_compile_join_planner import planner -from bopytex.service import orcherstrator -from bopytex.worker import Dispatcher -from bopytex.worker.clean import clean -from bopytex.worker.compile import latexmk -from bopytex.worker.generate import generate -from bopytex.worker.join_pdf import pdfjam +from bopytex.bopytex import bopytex import pytest @@ -49,13 +42,8 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): "environment": jinja2_env, }, } - dispatcher = Dispatcher( - {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} - ) - orcherstre = orcherstrator(options, planner=planner, dispatcher=dispatcher) - messages = [] - for message in orcherstre: - assert message.status == 0 + bopytex(**options) assert Path("joined_source.pdf").exists() + From 9d7f779f0722c33d45522c2f671e3d0060f7520c Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 17:04:16 +0200 Subject: [PATCH 54/76] Fix: Fix planer when students_csv is empty --- bopytex/planner/generate_compile_join_planner.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bopytex/planner/generate_compile_join_planner.py b/bopytex/planner/generate_compile_join_planner.py index 28e30cb..8fd2ad2 100644 --- a/bopytex/planner/generate_compile_join_planner.py +++ b/bopytex/planner/generate_compile_join_planner.py @@ -23,11 +23,13 @@ def build_subject_list_from_qty(qty: int) -> list[dict]: def planner(options: dict) -> list[Task]: try: students_csv = options["students_csv"] + assert options["students_csv"] != "" - except KeyError: + except (KeyError, AssertionError): try: quantity_subjects = options["quantity_subjects"] - except KeyError: + assert options["quantity_subjects"] != 0 + except (KeyError, AssertionError): raise PlannerMissingOption("students_csv or quantity_subjects is required") else: options["subjects"] = build_subject_list_from_qty(qty=quantity_subjects) From d9bd4ca5a1ff37b610b4f3adae109aef5e1ff5d1 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 18:00:54 +0200 Subject: [PATCH 55/76] Feat: Organize script to work in CLI --- bopytex/bopytex.py | 5 ++--- bopytex/script.py | 33 ++++++++++++++++----------------- bopytex/worker/compile.py | 3 +-- example/tpl_example.tex | 12 ++++++++++++ test/test_e2e.py | 5 +++-- 5 files changed, 34 insertions(+), 24 deletions(-) create mode 100644 example/tpl_example.tex diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index bfa345e..3776568 100644 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -9,8 +9,7 @@ def bopytex(**options): config["jinja2"]["environment"] = DEFAULT.jinja2_env orcherstre = orcherstrator( - options, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher + config, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher ) for message in orcherstre: - print(message) - assert message.status == 0 + yield message diff --git a/bopytex/script.py b/bopytex/script.py index ee50405..3d1bdaf 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -5,7 +5,7 @@ import click import logging -from bopytex.service import bopytex +from bopytex.bopytex import bopytex formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -21,7 +21,6 @@ logger.addHandler(steam_handler) "template", type=click.Path(exists=True), nargs=1, - # help="File with the template. The name should have the following form tpl_... .", ) @click.option( "-w", @@ -36,13 +35,6 @@ logger.addHandler(steam_handler) 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", @@ -50,6 +42,13 @@ logger.addHandler(steam_handler) default=False, help="Do not compile source code", ) +@click.option( + "-d", + "--dirty", + is_flag=True, + default=False, + help="Do not clean after compilation", +) @click.option( "-q", "--quantity_subjects", @@ -78,15 +77,15 @@ logger.addHandler(steam_handler) 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(**options) + for message in bopytex(**options): + try: + assert message.status == 0 + except AssertionError: + logger.warning(message) + break + else: + logger.info(message.out) if __name__ == "__main__": diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py index f5ea32f..1c1b68d 100644 --- a/bopytex/worker/compile.py +++ b/bopytex/worker/compile.py @@ -6,11 +6,10 @@ from ..message import SubprocessMessage def latexmk(args: dict, deps, output): compile_process = subprocess.Popen( - ["latexmk", deps[0]], + ["latexmk", "-f", deps[0]], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) - #return SubprocessMessage(compile_process) return Message(compile_process.wait(), list(compile_process.stdout), list(compile_process.stderr)) diff --git a/example/tpl_example.tex b/example/tpl_example.tex new file mode 100644 index 0000000..d5245f8 --- /dev/null +++ b/example/tpl_example.tex @@ -0,0 +1,12 @@ +% vim:ft=tex: +% +\documentclass[12pt]{article} + +\title{Bopytex example -- {{ number }}} + +\begin{document} + +\maketitle + + +\end{document} diff --git a/test/test_e2e.py b/test/test_e2e.py index 3368fa6..0a0a609 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -16,7 +16,7 @@ def template_path(tmp_path): \\begin{document} First document. -Subject {{ subject }} +Subject {{ number }} \\end{document} """ ) @@ -43,7 +43,8 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): }, } - bopytex(**options) + for message in bopytex(**options): + pass assert Path("joined_source.pdf").exists() From 467135abc6d13acc56d6c0404ff5ddcbbfb4ee2a Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 18:03:58 +0200 Subject: [PATCH 56/76] Feat: Create dockerfile to test in new env --- Dockerfile | 15 +++++++++++++++ requirements.txt | 33 --------------------------------- setup.py | 3 +-- 3 files changed, 16 insertions(+), 35 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dd0b1b8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM python:3.11-rc-alpine3.14 + +RUN apk add --no-cache --virtual python3-dev +COPY requirements.txt /tmp/ +RUN pip install -r /tmp/requirements.txt + +RUN mkdir -p /src/bopytex +COPY bopytex/ /src/bopytex +COPY setup.py /src/ +RUN pip install -e /src +COPY example /example + +WORKDIR /example +CMD bopytex -n 2 tpl_example.tex + diff --git a/requirements.txt b/requirements.txt index 6c00717..e69de29 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,33 +0,0 @@ -alabaster==0.7.10 -appdirs==1.4.3 -Babel==2.4.0 -decorator==4.0.11 -docutils==0.13.1 -imagesize==0.7.1 -ipython==5.3.0 -ipython-genutils==0.2.0 -Jinja2==2.9.6 -location==0.0.7 -MarkupSafe==1.0 -mpmath==0.19 -packaging==16.8 -pexpect==4.2.1 -pickleshare==0.7.4 -prompt-toolkit==1.0.14 -ptyprocess==0.5.1 -Pygments==2.2.0 -pyparsing==2.2.0 -## !! Could not determine repository location -pytz==2017.2 -requests==2.13.0 -simplegeneric==0.8.1 -six==1.10.0 -snowballstemmer==1.2.1 -Sphinx==1.5.5 -sphinx-rtd-theme==0.2.4 -sympy==1.0 -traitlets==4.3.2 -wcwidth==0.1.7 -mapytex==2.0.8 -mypytex==0.2 -path.py==12.1 diff --git a/setup.py b/setup.py index b1cddb8..0de5bf0 100644 --- a/setup.py +++ b/setup.py @@ -11,9 +11,8 @@ setup( author_email='programming@opytex.org', packages=['bopytex'], install_requires=[ - 'mapytex', - 'mypytex', 'click', + 'jinja2', ], entry_points={ "console_scripts": ['bopytex=bopytex.script:new'] From 0d614465f033fb896668d0fbda96f3b11de87955 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 21:16:27 +0200 Subject: [PATCH 57/76] Refact: move bopytex.bopytex to service.main --- bopytex/script.py | 4 ++-- bopytex/service.py | 12 ++++++++++++ test/test_e2e.py | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bopytex/script.py b/bopytex/script.py index 3d1bdaf..afad745 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -5,7 +5,7 @@ import click import logging -from bopytex.bopytex import bopytex +from bopytex.service import main formatter = logging.Formatter("%(name)s :: %(levelname)s :: %(message)s") steam_handler = logging.StreamHandler() @@ -78,7 +78,7 @@ logger.addHandler(steam_handler) help="Create and compile correction while making subjects", ) def new(**options): - for message in bopytex(**options): + for message in main(**options): try: assert message.status == 0 except AssertionError: diff --git a/bopytex/service.py b/bopytex/service.py index 4e1a429..9446fd7 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -6,6 +6,7 @@ Producing then compiling templates """ from bopytex.scheduler import Scheduler +import bopytex.default_config as DEFAULT def orcherstrator( @@ -21,6 +22,17 @@ def orcherstrator( for message in scheduler.backlog(): yield message +def main(**options): + + config = options.copy() + config["jinja2"] = {} + config["jinja2"]["environment"] = DEFAULT.jinja2_env + + orcherstre = orcherstrator( + config, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher + ) + for message in orcherstre: + yield message # ----------------------------- # Reglages pour 'vim' diff --git a/test/test_e2e.py b/test/test_e2e.py index 0a0a609..2945c63 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -1,7 +1,7 @@ import os import jinja2 from pathlib import Path -from bopytex.bopytex import bopytex +from bopytex.service import main import pytest @@ -43,7 +43,7 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): }, } - for message in bopytex(**options): + for message in main(**options): pass assert Path("joined_source.pdf").exists() From e4f234d241f25d93ebed364c46066826bfc88cbc Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Wed, 4 May 2022 21:21:26 +0200 Subject: [PATCH 58/76] Feat: add e2e test with failed compilation --- test/test_e2e.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/test_e2e.py b/test/test_e2e.py index 2945c63..53c703b 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -22,6 +22,22 @@ Subject {{ number }} ) return template +@pytest.fixture +def bad_template_path(tmp_path): + template = tmp_path / "tpl_source.tex" + with open(template, "w") as tpl: + tpl.write( + """ +\\documentclass{article} + +\\begin{document} +First document. + +Subject {{ number }} + """ + ) + return template + @pytest.fixture def jinja2_env(tmp_path): @@ -48,3 +64,22 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): assert Path("joined_source.pdf").exists() +def test_with_default_planner_bad_template(bad_template_path, jinja2_env, tmp_path): + os.chdir(tmp_path) + + options = { + "template": str(bad_template_path.name), + "quantity_subjects": 3, + "corr": False, + "no_join": False, + "no_pdf": False, + "jinja2": { + "environment": jinja2_env, + }, + } + + for message in main(**options): + pass + + assert not Path("joined_source.pdf").exists() + From 90ae3e936e85039eb4636c6db04679adae63f833 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Thu, 5 May 2022 14:35:01 +0200 Subject: [PATCH 59/76] Feat: Enable configfile loading --- bopytex/bopytex.py | 11 -------- bopytex/default_config.py | 6 +++-- bopytex/script.py | 11 ++++++-- bopytex/service.py | 57 +++++++++++++++++++++++++++++++++++---- test/test_service.py | 45 ++++++++++++++++++++++++++++++- 5 files changed, 109 insertions(+), 21 deletions(-) diff --git a/bopytex/bopytex.py b/bopytex/bopytex.py index 3776568..c7b5260 100644 --- a/bopytex/bopytex.py +++ b/bopytex/bopytex.py @@ -2,14 +2,3 @@ import bopytex.default_config as DEFAULT from bopytex.service import orcherstrator -def bopytex(**options): - - config = options.copy() - config["jinja2"] = {} - config["jinja2"]["environment"] = DEFAULT.jinja2_env - - orcherstre = orcherstrator( - config, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher - ) - for message in orcherstre: - yield message diff --git a/bopytex/default_config.py b/bopytex/default_config.py index 864d804..2f8b19d 100644 --- a/bopytex/default_config.py +++ b/bopytex/default_config.py @@ -1,4 +1,4 @@ -import jinja2 +import jinja2 as j2 from bopytex.planner.generate_compile_join_planner import planner from bopytex.worker import Dispatcher from bopytex.worker.clean import clean @@ -6,7 +6,9 @@ from bopytex.worker.compile import latexmk from bopytex.worker.generate import generate from bopytex.worker.join_pdf import pdfjam -jinja2_env = jinja2.Environment(loader=jinja2.FileSystemLoader("./")) +jinja2 = { + "environment": j2.Environment(loader=j2.FileSystemLoader("./")), +} dispatcher = Dispatcher( {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} diff --git a/bopytex/script.py b/bopytex/script.py index afad745..f3e3153 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -71,14 +71,21 @@ logger.addHandler(steam_handler) help="Activate correction and compile only from existing subjects", ) @click.option( - "-c", + "-C", "--corr", is_flag=True, default=False, help="Create and compile correction while making subjects", ) +@click.option( + "-c", + "--configfile", + type=str, + default="bopyptex_config.py", + help="Config file path", +) def new(**options): - for message in main(**options): + for message in main(options): try: assert message.status == 0 except AssertionError: diff --git a/bopytex/service.py b/bopytex/service.py index 9446fd7..b7243ba 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -5,8 +5,10 @@ Producing then compiling templates """ +import importlib.util +from pathlib import Path from bopytex.scheduler import Scheduler -import bopytex.default_config as DEFAULT +from bopytex import default_config def orcherstrator( @@ -22,18 +24,63 @@ def orcherstrator( for message in scheduler.backlog(): yield message + +def load_module(modulefile: str): + spec = importlib.util.spec_from_file_location("module.name", modulefile) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def clean_vars_keys( + vars: dict, + keys: list[str] = [ + "__name__", + "__doc__", + "__package__", + "__loader__", + "__spec__", + "__file__", + "__cached__", + "__builtins__", + ], +) -> dict: + new_dict = vars.copy() + for k in keys: + del new_dict[k] + return new_dict + + +def get_config(options: dict) -> dict: + """ Look for options["configfile"] to load it with default_config and options """ + try: + options["configfile"] + except KeyError: + local_config = {} + else: + if Path(options["configfile"]).exists(): + local_config = vars(load_module(options["configfile"])) + local_config = clean_vars_keys(local_config) + else: + local_config = {} + + config = clean_vars_keys(vars(default_config)) + config.update(local_config) + config.update(options) + return config + + def main(**options): - config = options.copy() - config["jinja2"] = {} - config["jinja2"]["environment"] = DEFAULT.jinja2_env + config = get_config(options) orcherstre = orcherstrator( - config, planner=DEFAULT.planner, dispatcher=DEFAULT.dispatcher + config, planner=default_config.planner, dispatcher=default_config.dispatcher ) for message in orcherstre: yield message + # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: diff --git a/test/test_service.py b/test/test_service.py index 80138f3..3828f18 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -1,5 +1,8 @@ +import os +import pytest + from bopytex.planner import fake_planner -from bopytex.service import orcherstrator +from bopytex.service import get_config, orcherstrator from bopytex.tasks import Task from bopytex.worker import Dispatcher from .fakes.workers import fake_worker @@ -14,3 +17,43 @@ def test_service(): for i, message in enumerate(service): assert message.status == 0 assert message.out == [f"FAKE - {{'number': {i}}} - [] - {i}"] + + +def test_get_config_no_configfile(): + pass + + +@pytest.fixture +def config_file(tmp_path): + config_file = tmp_path / "bopytex_config.py" + with open(config_file, "w") as f: + f.write( + """ +from bopytex.jinja2_env.texenv import texenv +jinja2 = { + "environment": texenv, +} + """ + ) + return config_file + + +def test_get_config_with_configfile(config_file, tmp_path): + os.chdir(tmp_path) + config = get_config({"from_option": "config", "configfile": str(config_file)}) + assert type(config) == dict + assert set(config.keys()) == { + "generate", + "configfile", + "Dispatcher", + "planner", + "latexmk", + "clean", + "j2", + "dispatcher", + "pdfjam", + "jinja2", + "texenv", + "from_option", + } + assert list(config["jinja2"].keys()) == ["environment"] From 8f1d9cb4d4a6e16634b5870f058d34acefca38db Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sat, 7 May 2022 16:55:33 +0200 Subject: [PATCH 60/76] Feat: four un peu tout --- bopytex/default_config.py | 6 ++++-- bopytex/script.py | 2 +- bopytex/worker/compile.py | 26 ++++++++++++++++++-------- bopytex/worker/join_pdf.py | 9 +++++++-- test/test_service.py | 2 +- 5 files changed, 31 insertions(+), 14 deletions(-) diff --git a/bopytex/default_config.py b/bopytex/default_config.py index 2f8b19d..1325331 100644 --- a/bopytex/default_config.py +++ b/bopytex/default_config.py @@ -2,7 +2,7 @@ import jinja2 as j2 from bopytex.planner.generate_compile_join_planner import planner from bopytex.worker import Dispatcher from bopytex.worker.clean import clean -from bopytex.worker.compile import latexmk +from bopytex.worker.compile import pdflatex from bopytex.worker.generate import generate from bopytex.worker.join_pdf import pdfjam @@ -11,5 +11,7 @@ jinja2 = { } dispatcher = Dispatcher( - {"GENERATE": generate, "COMPILE": latexmk, "JOIN": pdfjam, "CLEAN": clean} + {"GENERATE": generate, "COMPILE": pdflatex, "JOIN": pdfjam, "CLEAN": clean} ) + + diff --git a/bopytex/script.py b/bopytex/script.py index f3e3153..15706a2 100644 --- a/bopytex/script.py +++ b/bopytex/script.py @@ -85,7 +85,7 @@ logger.addHandler(steam_handler) help="Config file path", ) def new(**options): - for message in main(options): + for message in main(**options): try: assert message.status == 0 except AssertionError: diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py index 1c1b68d..641122c 100644 --- a/bopytex/worker/compile.py +++ b/bopytex/worker/compile.py @@ -4,12 +4,22 @@ from bopytex.message import Message from ..message import SubprocessMessage -def latexmk(args: dict, deps, output): - compile_process = subprocess.Popen( - ["latexmk", "-f", deps[0]], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True - ) - return Message(compile_process.wait(), list(compile_process.stdout), list(compile_process.stderr)) +def curstomtex(command: str, options: str): + def func(args: dict, deps, output) -> Message: + compile_process = subprocess.Popen( + [command, options, deps[0]], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + return Message( + compile_process.wait(), + list(compile_process.stdout), + list(compile_process.stderr), + ) + return func + + +latexmk = curstomtex("latexmk", "-f") +pdflatex = curstomtex("pdflatex", "--inteactions=nonstopmode") diff --git a/bopytex/worker/join_pdf.py b/bopytex/worker/join_pdf.py index 2a44f04..120df39 100644 --- a/bopytex/worker/join_pdf.py +++ b/bopytex/worker/join_pdf.py @@ -1,6 +1,6 @@ import subprocess -from bopytex.message import SubprocessMessage +from bopytex.message import Message, SubprocessMessage def pdfjam(args: dict, deps, output): joining_process = subprocess.Popen( @@ -9,5 +9,10 @@ def pdfjam(args: dict, deps, output): stderr=subprocess.PIPE, universal_newlines=True, ) - return SubprocessMessage(joining_process) + #return SubprocessMessage(joining_process) + return Message( + joining_process.wait(), + list(joining_process.stdout), + list(joining_process.stderr), + ) diff --git a/test/test_service.py b/test/test_service.py index 3828f18..a495180 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -47,7 +47,7 @@ def test_get_config_with_configfile(config_file, tmp_path): "configfile", "Dispatcher", "planner", - "latexmk", + "pdflatex", "clean", "j2", "dispatcher", From 78f6ddc813506048eb5ac310c0b671248ce61057 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 8 May 2022 09:15:13 +0200 Subject: [PATCH 61/76] Feat: dockerfile to test bopytex in new environment --- Dockerfile | 11 ++++++----- bopytex/worker/join_pdf.py | 21 ++++++++++++++++++--- setup.py | 24 +++++++++++------------- test/test_e2e.py | 2 +- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Dockerfile b/Dockerfile index dd0b1b8..c76e34c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,9 @@ -FROM python:3.11-rc-alpine3.14 +FROM python:3.11-rc-slim -RUN apk add --no-cache --virtual python3-dev +RUN apt-get update +RUN apt-get install -y texlive texlive-extra-utils + +RUN apt-get install -y python3-dev COPY requirements.txt /tmp/ RUN pip install -r /tmp/requirements.txt @@ -8,8 +11,6 @@ RUN mkdir -p /src/bopytex COPY bopytex/ /src/bopytex COPY setup.py /src/ RUN pip install -e /src + COPY example /example - WORKDIR /example -CMD bopytex -n 2 tpl_example.tex - diff --git a/bopytex/worker/join_pdf.py b/bopytex/worker/join_pdf.py index 120df39..a89155c 100644 --- a/bopytex/worker/join_pdf.py +++ b/bopytex/worker/join_pdf.py @@ -2,17 +2,32 @@ import subprocess from bopytex.message import Message, SubprocessMessage + def pdfjam(args: dict, deps, output): joining_process = subprocess.Popen( - ["pdfjam"] + deps + ["-o", output], + ["pdfjam"] + deps + ["-o", output], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) - #return SubprocessMessage(joining_process) return Message( joining_process.wait(), list(joining_process.stdout), list(joining_process.stderr), - ) + ) + +def gs(args: dict, deps, output): + """ Not working. The command works in terminal but not here """ + joining_process = subprocess.Popen( + ["gs", f"-dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile={output}"] + + deps, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + return Message( + joining_process.wait(), + list(joining_process.stdout), + list(joining_process.stderr), + ) diff --git a/setup.py b/setup.py index 0de5bf0..4e1e194 100644 --- a/setup.py +++ b/setup.py @@ -4,20 +4,18 @@ from setuptools import setup setup( - name='Bopytex', - version='0.2', - description='Command line tool for compiling latex with python command embedded', - author='Benjamin Bertrand', - author_email='programming@opytex.org', - packages=['bopytex'], + name="Bopytex", + version="0.9", + description="Command line tool for compiling latex with python command embedded", + author="Benjamin Bertrand", + author_email="benjamin.bertrand@opytex.org", + packages=["bopytex"], install_requires=[ - 'click', - 'jinja2', - ], - entry_points={ - "console_scripts": ['bopytex=bopytex.script:new'] - }, - ) + "click", + "jinja2", + ], + entry_points={"console_scripts": ["bopytex=bopytex.script:new"]}, +) # ----------------------------- # Reglages pour 'vim' diff --git a/test/test_e2e.py b/test/test_e2e.py index 53c703b..66ca212 100644 --- a/test/test_e2e.py +++ b/test/test_e2e.py @@ -60,7 +60,7 @@ def test_with_default_planner(template_path, jinja2_env, tmp_path): } for message in main(**options): - pass + assert message.status == 0 assert Path("joined_source.pdf").exists() From b1353bb6c76ed123dced371f98e51066f6077540 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Sun, 8 May 2022 09:45:45 +0200 Subject: [PATCH 62/76] Feat: add test with a template using random --- test/worker/test_generate.py | 47 ++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/test/worker/test_generate.py b/test/worker/test_generate.py index fbd3baf..015f322 100644 --- a/test/worker/test_generate.py +++ b/test/worker/test_generate.py @@ -1,7 +1,6 @@ import os import jinja2 -from pathlib import Path -from bopytex.worker.generate import tpl2tex, generate +from bopytex.worker.generate import generate, tpl2tex import pytest @@ -12,6 +11,12 @@ def test_tpl2tex(): assert fed == "Plop 1" +@pytest.fixture +def jinja2_env(tmp_path): + templateEnv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmp_path)) + return templateEnv + + @pytest.fixture def template_path(tmp_path): template = tmp_path / "template.j2" @@ -20,12 +25,6 @@ def template_path(tmp_path): return template -@pytest.fixture -def jinja2_env(tmp_path): - templateEnv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmp_path)) - return templateEnv - - def test_generate(template_path, jinja2_env): tmp_path = template_path.parent os.chdir(tmp_path) @@ -46,3 +45,35 @@ def test_generate(template_path, jinja2_env): with open(output, "r") as out: lines = out.readlines() assert lines == ["Plop 2"] + + +@pytest.fixture +def template_path_with_random(tmp_path): + template = tmp_path / "template.j2" + with open(template, "w") as tpl: + tpl.write("Plop {{ random.randint(0, 10) }}") + return template + + +def test_generate_with_random(template_path_with_random, jinja2_env): + tmp_path = template_path_with_random.parent + os.chdir(tmp_path) + + assert template_path_with_random.exists + template = str(template_path_with_random.name) + output = "output" + + import random + + message = generate( + args={"random": random, "jinja2": {"environment": jinja2_env}}, + deps=[template], + output=output, + ) + print(message.err) + assert message.status == 0 + + with open(output, "r") as out: + lines = out.readlines() + assert int(lines[0][-1]) > 0 + assert int(lines[0][-1]) < 10 From fae2afa76cce86cb5a26e5ff23d585ece5e0e0f3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Mon, 9 May 2022 09:09:38 +0200 Subject: [PATCH 63/76] Feat: make dockerfile to test my workflow --- Dockerfile | 2 +- Dockerfile.mapytex | 17 +++++++++++++++++ bopytex/worker/compile.py | 2 +- bopytex/worker/generate.py | 9 +++------ example/mapytex/bopytex_config.py | 7 +++++++ example/mapytex/tpl_example.tex | 17 +++++++++++++++++ example/{ => simple}/tpl_example.tex | 0 test/worker/test_generate.py | 9 +-------- 8 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 Dockerfile.mapytex create mode 100644 example/mapytex/bopytex_config.py create mode 100644 example/mapytex/tpl_example.tex rename example/{ => simple}/tpl_example.tex (100%) diff --git a/Dockerfile b/Dockerfile index c76e34c..343f5a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,5 +12,5 @@ COPY bopytex/ /src/bopytex COPY setup.py /src/ RUN pip install -e /src -COPY example /example +COPY example/simple /example WORKDIR /example diff --git a/Dockerfile.mapytex b/Dockerfile.mapytex new file mode 100644 index 0000000..5a5a910 --- /dev/null +++ b/Dockerfile.mapytex @@ -0,0 +1,17 @@ +FROM python:3.11-rc-slim + +RUN apt-get update +RUN apt-get install -y texlive texlive-extra-utils + +RUN apt-get install -y python3-dev +COPY requirements.txt /tmp/ +RUN pip install -r /tmp/requirements.txt +RUN pip install mapytex + +RUN mkdir -p /src/bopytex +COPY bopytex/ /src/bopytex +COPY setup.py /src/ +RUN pip install -e /src + +COPY example/mapytex /example +WORKDIR /example diff --git a/bopytex/worker/compile.py b/bopytex/worker/compile.py index 641122c..5813337 100644 --- a/bopytex/worker/compile.py +++ b/bopytex/worker/compile.py @@ -22,4 +22,4 @@ def curstomtex(command: str, options: str): latexmk = curstomtex("latexmk", "-f") -pdflatex = curstomtex("pdflatex", "--inteactions=nonstopmode") +pdflatex = curstomtex("pdflatex", "--interaction=nonstopmode") diff --git a/bopytex/worker/generate.py b/bopytex/worker/generate.py index b6a9cf3..02a12b0 100644 --- a/bopytex/worker/generate.py +++ b/bopytex/worker/generate.py @@ -9,13 +9,10 @@ def generate(args, deps, output): try: with open(output, "w") as out: - out.write(tpl2tex(template, metas=args)) + fed = template.render(args) + out.write(fed) return Message(0, [f"GENERATE - {deps[0]} to {output}"], []) except Exception as e: - return Message(0, [], [e]) - - -def tpl2tex(template: Template, metas: dict = {}) -> str: - return template.render(metas) + return Message(1, [], [e]) diff --git a/example/mapytex/bopytex_config.py b/example/mapytex/bopytex_config.py new file mode 100644 index 0000000..1fa0584 --- /dev/null +++ b/example/mapytex/bopytex_config.py @@ -0,0 +1,7 @@ +from mapytex import Expression + +from bopytex.jinja2_env.texenv import texenv + +jinja2 = { + "environment": texenv, +} diff --git a/example/mapytex/tpl_example.tex b/example/mapytex/tpl_example.tex new file mode 100644 index 0000000..48b3339 --- /dev/null +++ b/example/mapytex/tpl_example.tex @@ -0,0 +1,17 @@ +% vim:ft=tex: +% +\documentclass[12pt]{article} + +\title{Bopytex with Mapytex example -- \Var{ number }} + +\begin{document} + +\maketitle + +%- set e = Expression.random("{a} + {b}") +\Var{e} + +\Var{e.simplify()} + + +\end{document} diff --git a/example/tpl_example.tex b/example/simple/tpl_example.tex similarity index 100% rename from example/tpl_example.tex rename to example/simple/tpl_example.tex diff --git a/test/worker/test_generate.py b/test/worker/test_generate.py index 015f322..bcdd319 100644 --- a/test/worker/test_generate.py +++ b/test/worker/test_generate.py @@ -1,16 +1,9 @@ import os import jinja2 -from bopytex.worker.generate import generate, tpl2tex +from bopytex.worker.generate import generate import pytest -def test_tpl2tex(): - tpl = "Plop {{a}}" - jinja2_tpl = jinja2.Template(tpl) - fed = tpl2tex(jinja2_tpl, metas={"a": 1}) - assert fed == "Plop 1" - - @pytest.fixture def jinja2_env(tmp_path): templateEnv = jinja2.Environment(loader=jinja2.FileSystemLoader(tmp_path)) From 01a0f9db173ed07dae8a77847180906f634d4457 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Mon, 9 May 2022 09:11:56 +0200 Subject: [PATCH 64/76] Fix: Fix test on randint returned value --- test/worker/test_generate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/worker/test_generate.py b/test/worker/test_generate.py index bcdd319..220c79c 100644 --- a/test/worker/test_generate.py +++ b/test/worker/test_generate.py @@ -68,5 +68,5 @@ def test_generate_with_random(template_path_with_random, jinja2_env): with open(output, "r") as out: lines = out.readlines() - assert int(lines[0][-1]) > 0 + assert int(lines[0][-1]) >= 0 assert int(lines[0][-1]) < 10 From 8c20ff8e4a88698440f5f1dff70ea1d92fe970f3 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 19 Jul 2022 15:15:06 +0200 Subject: [PATCH 65/76] Feat: rename get_config --- bopytex/service.py | 34 ++++++++++++++++++++++------------ test/test_service.py | 4 ++-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/bopytex/service.py b/bopytex/service.py index b7243ba..808c5a1 100755 --- a/bopytex/service.py +++ b/bopytex/service.py @@ -6,6 +6,7 @@ Producing then compiling templates """ import importlib.util +import os from pathlib import Path from bopytex.scheduler import Scheduler from bopytex import default_config @@ -51,18 +52,27 @@ def clean_vars_keys( return new_dict -def get_config(options: dict) -> dict: - """ Look for options["configfile"] to load it with default_config and options """ - try: - options["configfile"] - except KeyError: - local_config = {} +def config_from_file(filename: str) -> dict: + if Path(filename).exists(): + local_config = vars(load_module(filename)) + return clean_vars_keys(local_config) else: - if Path(options["configfile"]).exists(): - local_config = vars(load_module(options["configfile"])) - local_config = clean_vars_keys(local_config) - else: - local_config = {} + return {} + + +def build_config(options: dict) -> dict: + """Look for options["configfile"] to load it with default_config and options""" + configfile = "" + try: + configfile = options["configfile"] + except KeyError: + pass + try: + configfile = os.environ["BOPYTEXCONFIG"] + except KeyError: + pass + + local_config = config_from_file(configfile) config = clean_vars_keys(vars(default_config)) config.update(local_config) @@ -72,7 +82,7 @@ def get_config(options: dict) -> dict: def main(**options): - config = get_config(options) + config = build_config(options) orcherstre = orcherstrator( config, planner=default_config.planner, dispatcher=default_config.dispatcher diff --git a/test/test_service.py b/test/test_service.py index a495180..fc1bc1d 100644 --- a/test/test_service.py +++ b/test/test_service.py @@ -2,7 +2,7 @@ import os import pytest from bopytex.planner import fake_planner -from bopytex.service import get_config, orcherstrator +from bopytex.service import build_config, orcherstrator from bopytex.tasks import Task from bopytex.worker import Dispatcher from .fakes.workers import fake_worker @@ -40,7 +40,7 @@ jinja2 = { def test_get_config_with_configfile(config_file, tmp_path): os.chdir(tmp_path) - config = get_config({"from_option": "config", "configfile": str(config_file)}) + config = build_config({"from_option": "config", "configfile": str(config_file)}) assert type(config) == dict assert set(config.keys()) == { "generate", From 1fd5ca9c964e73c93030e5aaef761cf9172f20a2 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 19 Jul 2022 16:33:27 +0200 Subject: [PATCH 66/76] Feat: activate texenv by default --- bopytex/default_config.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bopytex/default_config.py b/bopytex/default_config.py index 1325331..4619b82 100644 --- a/bopytex/default_config.py +++ b/bopytex/default_config.py @@ -1,13 +1,15 @@ -import jinja2 as j2 +#import jinja2 as j2 from bopytex.planner.generate_compile_join_planner import planner from bopytex.worker import Dispatcher from bopytex.worker.clean import clean from bopytex.worker.compile import pdflatex from bopytex.worker.generate import generate from bopytex.worker.join_pdf import pdfjam +from bopytex.jinja2_env.texenv import texenv jinja2 = { - "environment": j2.Environment(loader=j2.FileSystemLoader("./")), + # "environment": j2.Environment(loader=j2.FileSystemLoader("./")), + "environment": texenv } dispatcher = Dispatcher( From 473f554ebe23cfb3ec65654e4220e8b1eaab82b2 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 19 Jul 2022 16:33:51 +0200 Subject: [PATCH 67/76] Doc: update README with docker examples --- README.md | 125 +++++++++++++++++++++++++----------------------------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 4a5a481..8f15937 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://drone.opytex.org/api/badges/lafrite/Bopytex/status.svg)](https://drone.opytex.org/lafrite/Bopytex) -Bopytex is a command line tool for producing random math exercises with their correction. It embeds [mapytex](https://git.opytex.org/lafrite/Mapytex) and [python](python.org) into [latex](latex-project.org) through [jinja](jinja.pocoo.org). +Bopytex is a command line tool which embed python into latex. It uses jinja2 to do so with a modified environnement to match with latex syntax. ## Installing @@ -12,84 +12,75 @@ Install and update using [pip](https://pip.pypa.io/en/stable/quickstart/) ## Simple example -Let's say I want an exercise on adding 2 fractions (files are in `examples`). +``` latex +% save this as tpl_simple.tex +\documentclass[12pt]{article} -The *latex* template called `tpl_add_fraction.tex` +\title{Bopytex example -- {{ number }}} + +\begin{document} + +\maketitle +%- set a = 10 +%- set n = 2 +We have two variables +\begin{itemize} + \item a: \Var{a} + \item n: \Var{n} +\end{itemize} + +%# We can use blocks +\begin{itemize} +%- for i in n + \item \Var{a} +%- endfor +\end{itemize} + + +\end{document} + +``` + +To create a version a this document type this + +``` bash +$ bopytex tpl_simple.tex +``` + +## How I use it + +I build this program to produce individual exams subjects for each of my student with the correction associated. I write a template, and bopytex build subject and correction. + +To produce formulas and values, I use an another tool I an developing: `mapytex 1", "k>1"]) -\[ - A = \Var{e} -\] -Solution -\[ - \Var{e.simplify().explain() | join('=')} -\] -\end{document} -``` +%- set e = Expression.random("{a} + {b}") +\Var{e} -Generate latex files and compile those for 2 different subjects. - -``` -bopytex -t tpl_add_fractions.tex -N 2 -``` - -It produces 2 sources files - -- `01_add_fractions.tex` - -```latex -\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} -\] +\Var{e.simplify()} \end{document} ``` -- `02_add_fractions.tex` +Then I can produce multiple documents (3 here) -```latex -\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} +``` bash +$ bopytex tpl_simple.tex -q 3 -c bopytex_config.py ``` - -And a ready to print pdf. - -- [ all_add_fraction.pdf ]( ./examples/all_add_fraction.pdf ) - - - - - From 3d54cce718030fdb073c9e575a562aad5f6f6320 Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Tue, 19 Jul 2022 16:37:14 +0200 Subject: [PATCH 68/76] Refact: move mapytex to usecase and build example --- example/mapytex/bopytex_config.py | 7 ------- example/simple/joined_example.pdf | Bin 0 -> 31657 bytes example/simple/tpl_example.tex | 16 +++++++++++++++- example/usecase/bopytex_config.py | 1 + example/usecase/joined_example.pdf | Bin 0 -> 28646 bytes example/{mapytex => usecase}/tpl_example.tex | 0 6 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 example/mapytex/bopytex_config.py create mode 100644 example/simple/joined_example.pdf create mode 100644 example/usecase/bopytex_config.py create mode 100644 example/usecase/joined_example.pdf rename example/{mapytex => usecase}/tpl_example.tex (100%) diff --git a/example/mapytex/bopytex_config.py b/example/mapytex/bopytex_config.py deleted file mode 100644 index 1fa0584..0000000 --- a/example/mapytex/bopytex_config.py +++ /dev/null @@ -1,7 +0,0 @@ -from mapytex import Expression - -from bopytex.jinja2_env.texenv import texenv - -jinja2 = { - "environment": texenv, -} diff --git a/example/simple/joined_example.pdf b/example/simple/joined_example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a1c4cac88d1db1e2dc07fa9ca66dd846e35667d2 GIT binary patch literal 31657 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 literal 0 HcmV?d00001 diff --git a/example/simple/tpl_example.tex b/example/simple/tpl_example.tex index d5245f8..6519eb3 100644 --- a/example/simple/tpl_example.tex +++ b/example/simple/tpl_example.tex @@ -2,11 +2,25 @@ % \documentclass[12pt]{article} -\title{Bopytex example -- {{ number }}} +\title{Bopytex example -- \Var{ number }} \begin{document} \maketitle +%- set a = 10 +%- set n = 2 +We have two variables +\begin{itemize} + \item a: \Var{a} + \item n: \Var{n} +\end{itemize} + +%# We can use blocks +\begin{itemize} +%- for i in range(n) + \item \Var{a} +%- endfor +\end{itemize} \end{document} diff --git a/example/usecase/bopytex_config.py b/example/usecase/bopytex_config.py new file mode 100644 index 0000000..03a19e3 --- /dev/null +++ b/example/usecase/bopytex_config.py @@ -0,0 +1 @@ +from mapytex import Expression diff --git a/example/usecase/joined_example.pdf b/example/usecase/joined_example.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1200dcde7bc7079775aa670b9e2fb6398c334558 GIT binary patch literal 28646 zcmeFYW0Yjgw)a~$yVzx0S+;H4wr!)!wz`Ziqsz8!+qP~$=bZiCeb3%woITz<&bPfj zN zU4ih zHIX&2F(II!cQtUNceb&ow>LJmG_awSH?_0#w4!&ib8$2>`Sb99Zd3l(AbA5b6ImA< z!#~gXw|OLNP3?p&jGPGotbdGAgrOG{v~wrW`pYsF4i-8_HdX?5W&j;KhYk$AqRD?w z>O}CD|3vLy}YBHk&=lsfffP%pOqk>S2l5X zCeZoIr+;%lfP?9O_Pb6jk5xYdTNptc{Wxg#d8f)2Qf!>I%t9|`T&*r=2*=n?`p#N&h| z*zdQhbMbd**YNoFj&AOvc+%SNtJl)2Ry(poVzW9{@_C+$vWxiKbnM+y8xLNLGjCQ_ zM^)#}ulsf1n$}n2H%cPLL15-B5-R# z4gBJ&3kUAsU#@QV5kS%H_`+s#SSRw~6M`qU*7q46b9Hxw82nDC*On$A+qx7EO`DFY zxHRQ^+-r2}NAa|Ujy^ox>a0Jrd#DdS>g%OnYxz}7v{v%hlAE?z@L9##_38^L76WX` zzYbBWmhs8T9hEzStL4Sc2;%em7(+qameKSXnbp->1=ZPc40?75B0~Bk-vavte(Eb0 zO^3rC=jVs%>bNN#Mwc>v5#7Q zYB@h&3Y4WRyow;(#pGp>wP+7?5Zyx^eBpbKP+DK~CvS&D?$v<0-j-DaP8kOG>Gj$+ z1zz@wD?4&WWCZ;MQZ3L&2pi%8?;c^rGvu|Gezc9dzb)c!M+EW(;)9kGo1-{25>E_V zfY?=y>b{nzC)qv-S0f15DRf>IfMPg>#7>wK56m+|3Sv)&>`1gN%<&2iuMp6gXEq*> zh@~9rvQ52jHZ7Jf>zA5QAj>XCOTnh8XzMKAG6Opr9Yev!S-fs8(lV3bSOB2Lx<8I| zo0_7K5;O>yW@Bd5h!bi_|O zxaS7?H?MchX?^7zE&>^KCz;vY@E;{g&q&gGudDYmZ#|cGNh|3OFYfEurTX0JbQ(6U zP5BR>?Hrh_ACue3x1S!&(yg@wDk^H3a}G-BzH`251P9(Dvvt}K#lOBXrOXFh*%VQv z4xGG0s5BwU;Am3-Fzbd|-NvP549y`m4eUPy2L!NSBT)`u&><}XM@WQ->86G9!!)(s zYFkCYN4%AlKUVHBF1WSzzl#}Bdce_?A%Js}R0fy@!)}MWZg&F=mX7M`wP3Rq#g`JL z`vJU|X1?v+>`!}ed%O6b!<2Sb+t5b>!R)ZWh>s*F`V!j5D3hQ|O!`LxrZIcm!#JAY zIzhdJk-?`h?y^?6LmqoAd(*h6*F;%t$UR*IdPRGQCF2e-yW;w{JCDhMw%D≶wJM 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