7. Number Lines booklet pythonο
The python below requires 3 .tex files:
number_lines_booklet_template.tex
number_lines_booklet_ans_template.tex
number_lines_worksheet_diagram_template.tex
7.1. Python to create a 2 page booklet of number lines worksheetsο
The python file, number_lines_booklet_maker.py, when run, will ask for these inputs:
Choose the arithmetic process:
"Enter 1,2,3,4,5 or 6 for plus,minus_neg,minus,minus_pos,plus_neg,random"
.Choose the number of questions from 1 to 100:
"Enter the number of questions from 1 to 80"
Choose the file name base:
""Enter the base filename to be added to the prefix nlBk:"
.The filename will have β_qβ added for the question booklet and β_ansβ for the answer booklet.
1from pathlib import Path
2import subprocess
3import time
4import random
5import magick_pdf_to_png
6
7currfile_dir = Path(__file__).parent
8tex_template_path = currfile_dir / "number_lines_booklet_template.tex"
9texans_template_path = currfile_dir / "number_lines_booklet_ans_template.tex"
10tex_diagram_template_path = currfile_dir / "number_lines_booklet_diagram_template.tex"
11
12
13def convert_to_pdf(tex_path, currfile_dir, aux_path):
14 """
15 Converts a TeX file to PDF format using pdfLaTeX.
16
17 Args:
18 tex_path (str): The path to the TeX file.
19 currfile_dir (str): The path to the directory where the TeX file is located.
20 aux_path (str): The path to the directory where auxiliary files will be stored.
21
22 Returns:
23 subprocess.CompletedProcess: A subprocess.CompletedProcess object containing information about the completed process.
24
25 Raises:
26 FileNotFoundError: If the TeX file does not exist.
27 subprocess.CalledProcessError: If pdfLaTeX returns a non-zero exit code.
28 """
29 result = subprocess.run(
30 [
31 "pdfLaTeX",
32 tex_path,
33 "-output-directory",
34 currfile_dir,
35 "-aux-directory",
36 aux_path,
37 ],
38 stdout=subprocess.PIPE,
39 )
40
41
42def getprocess_dict(num):
43 if num is None or num == 6:
44 num = random.randint(1, 5)
45 match num:
46 case 1:
47 return go_right_dict("plus")
48 case 2:
49 return go_right_dict("minus_neg")
50 case 3:
51 return go_left_dict("minus")
52 case 4:
53 return go_left_dict("minus_pos")
54 case 5:
55 return go_left_dict("plus_neg")
56
57
58def val_in_list_exclude_zero(low, high):
59 vals = list(range(low, high + 1))
60 if 0 in vals:
61 vals.remove(0)
62 return random.choice(vals)
63
64
65def go_right_dict(add_style):
66 # set points
67 endval = val_in_list_exclude_zero(-7, 9)
68 startval = val_in_list_exclude_zero(-9, endval - 2)
69 changevaltxt = endval - startval
70 kv = dict()
71 kv["endval"] = f"{endval}"
72 kv["startval"] = f"{startval}"
73 # answers
74 kv["endvaltxt"] = f"{endval}"
75 kv["startvaltxt"] = f"{startval}"
76 if add_style == "plus":
77 kv["changevaltxt"] = r"+" + str(changevaltxt)
78 else: # minus_neg
79 kv["changevaltxt"] = r"-(" + str(-changevaltxt) + ")"
80 kv["equtxt"] = f"{startval}{kv['changevaltxt']} = {endval}"
81 # _question
82 kv["endvaltxt_q"] = f"\qgap"
83 kv["startvaltxt_q"] = f"\qgap"
84 if add_style == "plus":
85 kv["changevaltxt_q"] = f"+\qgap"
86 kv["equtxt_q"] = f"\qgap + \qgap = \qgap"
87 else: # minus_neg
88 kv["changevaltxt_q"] = r"-(\qgap)"
89 kv["equtxt_q"] = r"\qgap - (\qgap) = \qgap"
90 return kv
91
92
93def go_left_dict(sub_style):
94 # set points
95 endval = val_in_list_exclude_zero(-9, 7)
96 startval = val_in_list_exclude_zero(endval + 2, 9)
97 changevaltxt = endval - startval
98 kv = dict()
99 kv["endval"] = f"{endval}"
100 kv["startval"] = f"{startval}"
101 # answers
102 kv["endvaltxt"] = f"{endval}"
103 kv["startvaltxt"] = f"{startval}"
104 if sub_style == "minus":
105 kv["changevaltxt"] = r"-" + str(-changevaltxt)
106 elif sub_style == "minus_pos":
107 kv["changevaltxt"] = r"-(+" + str(-changevaltxt) + ")"
108 else: # plus_neg
109 kv["changevaltxt"] = r"+(" + str(changevaltxt) + ")"
110 kv["equtxt"] = f"{startval}{kv['changevaltxt']} = {endval}"
111 # _question
112 kv["endvaltxt_q"] = f"\qgap"
113 kv["startvaltxt_q"] = f"\qgap"
114 if sub_style == "minus":
115 kv["changevaltxt_q"] = r"-\qgap"
116 kv["equtxt_q"] = r"\qgap - \qgap = \qgap"
117 elif sub_style == "minus_pos":
118 kv["changevaltxt_q"] = r"-(+\qgap)"
119 kv["equtxt_q"] = r"\qgap - (+\qgap) = \qgap"
120 else: # plus_neg
121 kv["changevaltxt_q"] = r"+(\qgap)"
122 kv["equtxt_q"] = r"\qgap + (\qgap) = \qgap"
123 return kv
124
125
126kv_keys_ans = [
127 "startval",
128 "endval",
129 "startvaltxt",
130 "endvaltxt",
131 "changevaltxt",
132 "equtxt",
133]
134kv_keys_q = [
135 "startval",
136 "endval",
137 "startvaltxt_q",
138 "endvaltxt_q",
139 "changevaltxt_q",
140 "equtxt_q",
141]
142
143
144def trimkey(key):
145 key = key.replace("_q", "")
146 return key
147
148
149def make1_diagram(tex_diagram_template_txt, num):
150 posttext = r"\vspace{-2pt}"
151 tex_diagram_template_txt_ans = tex_diagram_template_txt
152 kv = getprocess_dict(num)
153 for key, value in kv.items():
154 # show answers
155 if key in kv_keys_ans:
156 tex_diagram_template_txt_ans = tex_diagram_template_txt_ans.replace(
157 "<<" + key + ">>", value
158 )
159 for key, value in kv.items():
160 # don't show answers, use ___ for gaps
161 if key in kv_keys_q:
162 tex_diagram_template_txt = tex_diagram_template_txt.replace(
163 "<<" + trimkey(key) + ">>", value
164 )
165 return tex_diagram_template_txt + posttext, tex_diagram_template_txt_ans + posttext
166
167
168def main():
169 num = input(
170 "Enter 1,2,3,4,5 or 6 for plus,minus_neg,minus,minus_pos,plus_neg,random \n"
171 )
172 if num.strip().isdigit():
173 num = int(num)
174 if not num in [1, 2, 3, 4, 5, 6]:
175 num = 6 # random by default
176 else:
177 num = 6 # random by default
178 #
179 numq = input("Enter the number of questions from 1 to 80 \n")
180 if numq.strip().isdigit():
181 numq = int(numq)
182 if not numq in range(1,81):
183 numq = 16 # random by default
184 else:
185 numq = 16 # random by default
186 #
187 filename = input("Enter the base filename to be added to the prefix nlBk_: \n")
188 if not filename:
189 filename = "1" # "nlBk_1_q and nlBk_1_ans as default file"
190 # set names of files that are made
191 tex_output_path = currfile_dir / f"nlBk_{filename}_q.tex"
192 pdf_path = currfile_dir / f"nlBk_{filename}_q.pdf"
193 aux_path = currfile_dir / "temp"
194 # answers
195 tex_output_path_ans = currfile_dir / f"nlBk_{filename}_ans.tex"
196 pdf_path_ans = currfile_dir / f"nlBk_{filename}_ans.pdf"
197
198 # Read in the LaTeX template file
199 with open(tex_template_path, "r") as infile:
200 tex_template_txt = infile.read()
201 # Read in the LaTeX template file for answers
202 with open(texans_template_path, "r") as infile:
203 tex_template_txt_ans = infile.read()
204 # Read in the LaTeX diagram template file
205 with open(tex_diagram_template_path, "r") as infile:
206 tex_diagram_template_txt = infile.read()
207
208 # <<diagrams>>
209 # generate diagrams text and text for answers
210 diagrams_text = ""
211 diagrams_text_ans = ""
212 # add the headtext
213 headtext = r"\pagebreak ~ \newline ~ \newline"
214 rmax = numq + 1
215 for i in range(1, rmax):
216 img_tex, img_tex_ans = make1_diagram(tex_diagram_template_txt, num)
217 if i > 8 and i % 8 == 1:
218 diagrams_text += headtext
219 diagrams_text_ans += headtext
220 diagrams_text += img_tex
221 diagrams_text_ans += img_tex_ans
222
223 # Replace the <<diagrams>> placeholder in the LaTeX template with the generated diagrams
224 tex_template_txt = tex_template_txt.replace("<<diagrams>>", diagrams_text)
225 tex_template_txt_ans = tex_template_txt_ans.replace(
226 "<<diagrams>>", diagrams_text_ans
227 )
228 # Write the question tex to an output file
229 with open(tex_output_path, "w") as outfile:
230 outfile.write(tex_template_txt)
231
232 # Write the answer tex to an output file
233 with open(tex_output_path_ans, "w") as outfile:
234 outfile.write(tex_template_txt_ans)
235
236 # Wait for the file to be created
237 time.sleep(2)
238 # Convert the LaTeX files to PDFs
239 convert_to_pdf(tex_output_path, currfile_dir, aux_path)
240 convert_to_pdf(tex_output_path_ans, currfile_dir, aux_path)
241
242 # # don't convert to images
243 # time.sleep(1)
244 # # Convert the PDFs to PNGs
245 # magick_pdf_to_png.convert_pdf_to_png(pdf_path,png_path)
246 # magick_pdf_to_png.convert_pdf_to_png(pdf_path_ans,png_path_ans)
247
248
249if __name__ == "__main__":
250 print("starting")
251 main()
252 print("finished")