5. Number Lines worksheet python๏
The python below requires 3 .tex files:
number_lines_worksheet_template.tex
number_lines_worksheet_ans_template.tex
number_lines_worksheet_diagram_template.tex
5.1. Python to create a number lines worksheet๏
The Python code that creates a number lines worksheet, with 8 number line diagrams on one page, is below.
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_worksheet_template.tex"
9texans_template_path = currfile_dir / "number_lines_worksheet_ans_template.tex"
10tex_diagram_template_path = currfile_dir / "number_lines_worksheet_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("Enter 1,2,3,4,5 or 6 for plus,minus_neg,minus,minus_pos,plus_neg,random \n")
170 if num.strip().isdigit():
171 num = int(num)
172 if not num in [1, 2, 3, 4, 5, 6]:
173 num = 6 # random by default
174 else:
175 num = 6 # random by default
176 filename = input("Enter the base filename to be added to the prefix nlWS_: \n")
177 if not filename:
178 filename = "1" # "nlWS_1_q and nlWS_1_ans as default file"
179 # set names of files that are made
180 # questions
181 # questions
182 tex_output_path = currfile_dir / f"nlWS_{filename}_q.tex"
183 pdf_path = currfile_dir / f"nlWS_{filename}_q.pdf"
184 png_path = currfile_dir / f"nlWS_{filename}_q.png"
185 aux_path = currfile_dir / "temp"
186 # answers
187 tex_output_path_ans = currfile_dir / f"nlWS_{filename}_ans.tex"
188 pdf_path_ans = currfile_dir / f"nlWS_{filename}_ans.pdf"
189 png_path_ans = currfile_dir / f"nlWS_{filename}_ans.png"
190 # Read in the LaTeX template file
191 with open(tex_template_path, "r") as infile:
192 tex_template_txt = infile.read()
193 # Read in the LaTeX template file for answers
194 with open(texans_template_path, "r") as infile:
195 tex_template_txt_ans = infile.read()
196 # Read in the LaTeX diagram template file
197 with open(tex_diagram_template_path, "r") as infile:
198 tex_diagram_template_txt = infile.read()
199
200 # <<diagrams>>
201 # generate diagrams text and text for answers
202 diagrams_text = ""
203 diagrams_text_ans = ""
204 for i in range(1, 9):
205 img_tex, img_tex_ans = make1_diagram(tex_diagram_template_txt, num)
206 diagrams_text += img_tex
207 diagrams_text_ans += img_tex_ans
208
209 # Replace the <<diagrams>> placeholder in the LaTeX template with the generated diagrams
210 tex_template_txt = tex_template_txt.replace("<<diagrams>>", diagrams_text)
211 tex_template_txt_ans = tex_template_txt_ans.replace("<<diagrams>>", diagrams_text_ans)
212
213 # Write the question tex to an output file
214 with open(tex_output_path, "w") as outfile:
215 outfile.write(tex_template_txt)
216
217 # Write the answer tex to an output file
218 with open(tex_output_path_ans, "w") as outfile:
219 outfile.write(tex_template_txt_ans)
220
221 # Wait for the files to be created
222 time.sleep(1)
223 # Convert the LaTeX files to PDFs
224 convert_to_pdf(tex_output_path, currfile_dir, aux_path)
225 convert_to_pdf(tex_output_path_ans, currfile_dir, aux_path)
226
227 # Wait for the files to be created
228 time.sleep(1)
229 # Convert the PDFs to PNGs
230 magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
231 magick_pdf_to_png.convert_pdf_to_png(pdf_path_ans, png_path_ans)
232
233
234if __name__ == "__main__":
235 print("starting")
236 main()
237 print("finished")