3. Number Lines - automated

The LaTeX in this section is for one number line diagram.
The values in the diagram can be randomly generated by python.

3.1. A number line diagram with answers

nl_plus_q

nl_plus_ans

nl_minus_neg_q

nl_minus_neg_ans

nl_minus_q

nl_minus_ans

nl_minus_pos_q

nl_minus_pos_ans

nl_plus_neg_q

nl_plus_neg_ans

nl_random_q

nl_random_ans


3.2. Splitting the LaTeX and modifying it to be built by python

The LaTeX from a single number lines equation is used as a starting point, split into 2 and modified.
The document template contains the preamble and the scaffold for the document.
The Diagram template contains the backtacking diagram LaTeX.

3.2.1. document template

The LaTeX number lines worksheet template is below.
 1\documentclass[preview,12pt]{standalone}
 2\usepackage{tikz}
 3\usetikzlibrary{positioning}
 4\usetikzlibrary {arrows.meta}
 5\usetikzlibrary{bending}
 6\usepackage[a4paper, portrait, margin=1cm]{geometry}
 7
 8\def\jumpheight{10}
 9\def\qgap{\rule[-1pt]{1.0em}{.25pt}}
10
11\begin{document}
12    <<diagram>>
13\end{document}
<<diagram>> is placeholder text for the text that python will use to add the LaTeX for the number line diagram.

3.2.2. diagram template

The LaTeX number line diagram template is below.
 1\begin{tikzpicture}[scale=0.75]
 2    % axis, arrow style to-to
 3    \draw[{To[scale=1.3]}-{To[scale=1.3]}, line width=1pt] (-10.4, 0) -- (10.4, 0);  
 4    % tick marks
 5    \foreach \x in {-10,-9,...,10}
 6        \draw[shift={(\x,0)},color=black, line width=1pt] (0pt,-14pt) -- (0pt,0pt);
 7    % numbers along each axis
 8    \foreach \x in {-10,0,10}
 9        \draw[shift={(\x,-0.8)},color=black] node[font=\large] {$\x$};
10    \draw[shift={(<<startval>>,-0.8)},color=black] node[font=\large,text height=12pt] {$<<startvaltxt>>$};
11    \draw[shift={(<<endval>>,-0.8)},color=black] node[font=\large,text height=12pt] {$<<endvaltxt>>$};
12    % dots
13    \filldraw[black] (<<startval>>,0) circle (4pt) node[above,yshift=-2pt] (a) {};
14    \filldraw[black] (<<endval>>,0) circle (4pt) node[above,yshift=-2pt] (b) {}; 
15    % arrow
16    \draw[-{To[scale=1.3, bend]},line width=1pt, color=black] (a.north)  .. controls  +(north:\jumpheight mm) and +(north:\jumpheight mm) .. node[above=2pt,font=\large,text height=10pt] {$<<changevaltxt>>$} (b.north); % for addition
17    % equation at right end
18    \node [font=\large, minimum width=30mm] at (11.0,1.2) {$<<equtxt>>$};
19\end{tikzpicture}
The line \draw[shift={(<<startval>>,-0.8)},color=black] node[font=\large,text height=12pt] {$<<startvaltxt>>$}; has a placeholder <<startval>> which is replaced by python.
Other placeholders are tagged by <<   >>.

3.2.3. Python to create a number line diagram

The Python to create a number line diagram is below.
This Python code is a script that generates a PDF file and PNG file with a number line diagram based on some random parameters. Here is an outline of the code:
  • It opens three template files for creating a tex file with a number line diagram and reads their contents.

  • It calls a function called make1_diagram that takes the diagram template text as an argument and returns two strings: one for the question and one for the answer. The function calls the btf.get_1step_process_dict function to get a dictionary with some keys and values for creating the diagram. The function then replaces the placeholders in the template text with the corresponding values from the dictionary, depending on whether it is for the answer or question version. The function uses a loop to iterate over the keys and values of the dictionary and perform the replacement.

  • It replaces a placeholder in the LaTeX template texts with the diagram texts and writes them to two output files: one for the question and one for the answer.

  • It waits for two seconds and then calls another function called convert_to_pdf that takes the output tex files and converts them to PDF files using the pdfLaTeX command.

  • It waits for another second and then calls another function called convert_pdf_to_png from the magick_pdf_to_png module that takes the output PDF files and converts them to PNG files using the magick command.

  • It prints β€œstarting” before calling the main function and β€œfinished” after it.

Further details:
  • The code imports the modules pathlib, subprocess, time, random, and the file in the currectn directory magick_pdf_to_png.

  • The code defines the paths for the template and output files using the Path class from pathlib.

  • The code defines the function called btf.get_1step_process_dict that returns a dictionary with some keys and values for creating the number line diagram. The function uses a random integer to choose one of five possible cases, each corresponding to a different style of adding or subtracting numbers on the number line. The function calls another helper function called val_in_list_exclude_zero that returns a random integer from a range excluding zero.

  • The code defines the function called go_right_dict that takes an argument called add_style and returns a dictionary with some keys and values for creating a number line diagram where the arrow goes to the right. The function sets the end value, start value, and change value based on the argument and uses some formatting to display them as text.

  • The code defines a function called go_left_dict that takes an argument called sub_style and returns a dictionary with some keys and values for creating a number line diagram where the arrow goes to the left. The function sets the end value, start value, and change value based on the argument and uses some formatting to display them as text.

  • The code defines two lists called kv_keys_ans and kv_keys_q that contain the keys of the dictionary that are relevant for the answer and question versions of the diagram, respectively.

  • The code defines the function called trimkey that takes a key and removes the _q suffix if it has one.

number_lines_diagram_maker

  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_template.tex"
  9texans_template_path = currfile_dir / "number_lines_template.tex"
 10tex_diagram_template_path = currfile_dir / "number_lines_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    tex_diagram_template_txt_ans = tex_diagram_template_txt
151    kv = getprocess_dict(num)
152    for key, value in kv.items():
153        # show answers
154        if key in kv_keys_ans:
155            tex_diagram_template_txt_ans = tex_diagram_template_txt_ans.replace(
156                "<<" + key + ">>", value
157            )
158    for key, value in kv.items():
159        # don't show answers, use ___ for gaps
160        if key in kv_keys_q:
161            tex_diagram_template_txt = tex_diagram_template_txt.replace(
162                "<<" + trimkey(key) + ">>", value
163            )
164    return tex_diagram_template_txt, tex_diagram_template_txt_ans
165
166
167def main():
168    num = input("Enter 1,2,3,4,5 or 6 for plus,minus_neg,minus,minus_pos,plus_neg,random \n")
169    if num.strip().isdigit():
170        num = int(num)
171        if not num in [1, 2, 3, 4, 5, 6]:
172            num = 6  # random by default
173    else:
174        num = 6  # random by default
175    filename = input("Enter the base filename to be added to the prefix nl_: \n")
176    if not filename:
177        filename = "1"  # "nl_1_q and nl_1_ans as default file"
178    # set names of files that are made
179    # questions
180      # questions
181    tex_output_path = currfile_dir / f"nl_{filename}_q.tex"
182    pdf_path = currfile_dir / f"nl_{filename}_q.pdf"
183    png_path = currfile_dir / f"nl_{filename}_q.png"
184    aux_path = currfile_dir / "temp"
185    # answers
186    tex_output_path_ans = currfile_dir / f"nl_{filename}_ans.tex"
187    pdf_path_ans = currfile_dir / f"nl_{filename}_ans.pdf"
188    png_path_ans = currfile_dir / f"nl_{filename}_ans.png"
189    # Read in the LaTeX template file
190    with open(tex_template_path, "r") as infile:
191        tex_template_txt = infile.read()
192    # Read in the LaTeX template file for answers
193    with open(texans_template_path, "r") as infile:
194        tex_template_txt_ans = infile.read()
195    # Read in the LaTeX diagram template file
196    with open(tex_diagram_template_path, "r") as infile:
197        tex_diagram_template_txt = infile.read()
198
199    # Generate the <<diagram>> replacement tex
200    diagram_text, diagram_text_ans = make1_diagram(tex_diagram_template_txt, num)
201    # Replace the <<diagram>> placeholder in the LaTeX template
202    tex_template_txt = tex_template_txt.replace("<<diagram>>", diagram_text)
203    tex_template_txt_ans = tex_template_txt_ans.replace("<<diagram>>", diagram_text_ans)
204    # Write the question diagram tex to an output file
205    with open(tex_output_path, "w") as outfile:
206        outfile.write(tex_template_txt)
207    # Write the answer diagram tex to an output file
208    with open(tex_output_path_ans, "w") as outfile:
209        outfile.write(tex_template_txt_ans)
210
211    # # Wait for the file to be created
212    time.sleep(2)
213
214    # convert to pdf
215    convert_to_pdf(tex_output_path_ans, currfile_dir, aux_path)
216    convert_to_pdf(tex_output_path, currfile_dir, aux_path)
217
218    time.sleep(1)
219    # convert to png
220    magick_pdf_to_png.convert_pdf_to_png(pdf_path, png_path)
221    magick_pdf_to_png.convert_pdf_to_png(pdf_path_ans, png_path_ans)
222
223
224if __name__ == "__main__":
225    print("starting")
226    main()
227    print("finished")