5. Number Lines worksheet python๏ƒ

The python below requires 3 .tex files:

  1. number_lines_worksheet_template.tex

  2. number_lines_worksheet_ans_template.tex

  3. 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.

1 page worksheet python

  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")