4. Decimals python

The 2 custom python modules required are:
 1"""
 2Module of functions to return decimals dictionary for LaTeX
 3"""
 4
 5import random
 6
 7
 8def get_dec_dict(nump, numip, numdp):
 9    if nump is None or nump == 3:
10        nump = random.randint(1, 2)
11    match nump:
12        case 1:
13            return add_dict(numip, numdp)
14        case 2:
15            return sub_dict(numip, numdp)
16
17
18def add_dict(numip, numdp):
19    # na + nb = nc
20    # Generate a random decimal number between 0.00001 and 9999
21    na = random.uniform(10**-numdp, (10**numip) - 0.1)
22    nb = random.uniform(10**-numdp, (10**numip) - 0.1 - na)
23    # Format the result to 0 to 5 decimal places
24    format_string = "{:." + str(numdp) + "f}"
25    na = format_string.format(na)
26    nb = format_string.format(nb)
27    nc = float(na) + float(nb)
28    nc = format_string.format(nc)
29    kv = dict()
30    kv["num1"] = f"{na}"
31    kv["num2"] = f"{nb}"
32    kv["process"] = "+"
33    kv["answer"] = f"{nc}"
34    kv["numip"] = f"{str(numip)}"
35    kv["numdp"] = f"{str(numdp)}"
36    return kv
37
38
39def sub_dict(numip, numdp):
40    # na + nb = nc
41    # Generate a random decimal number between 0.01 and 100
42    nc = random.uniform(10**-numdp, (10**numip) - 0.1)
43    nb = random.uniform(10**-numdp, (10**numip) - 0.1 - nc)
44    # Format the result to 0 to 5 decimal places
45    format_string = "{:." + str(numdp) + "f}"
46    nc = format_string.format(nc)
47    nb = format_string.format(nb)
48    na = float(nc) + float(nb)
49    na = format_string.format(na)
50    #
51    kv = dict()
52    kv["num1"] = f"{na}"
53    kv["num2"] = f"{nb}"
54    kv["process"] = "-"
55    kv["answer"] = f"{nc}"
56    kv["numip"] = f"{str(numip)}"
57    kv["numdp"] = f"{str(numdp)}"
58    return kv
59
The Python to create booklets of questions involving the addition and subtraction of decimals, is below.
  1from pathlib import Path
  2import subprocess
  3import os
  4import time
  5import decimals_functions as decf
  6
  7# import magick_pdf_to_png
  8
  9
 10currfile_dir = Path(__file__).parent
 11tex_template_path = currfile_dir / "decimals_booklet_template.tex"
 12texans_template_path = currfile_dir / "decimals_booklet_ans_template.tex"
 13tex_diagram_template_path = (currfile_dir /
 14                             "decimals_booklet_diagram_template.tex")
 15
 16colbreak = "\columnbreak  % Break column after 30 questions \n"
 17
 18
 19def convert_to_pdf(tex_path, outputdir):
 20    tex_path = Path(tex_path).resolve()
 21    outputdir = Path(outputdir).resolve()
 22    # for testing
 23    # print(f"tex_path: {tex_path}")
 24    # print(f"outputdir: {outputdir}")
 25    try:
 26        # Generate the PDF
 27        subprocess.run(["latexmk", "-pdf", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 28        # # Clean auxiliary files after successful PDF generation
 29        subprocess.run(["latexmk", "-c", "-outdir=" + str(outputdir), str(tex_path)], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
 30        # for hosted remove stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL for debugging any errors
 31        # Remove the .tex file manually
 32        if tex_path.exists():
 33            os.remove(tex_path)
 34    except subprocess.CalledProcessError as e:
 35        print(f"Error: {e}")
 36
 37
 38# tex_keys = ["num1", "num2", "process", "numip", "numdp"]
 39tex_keys_q = ["answer"]
 40
 41def make1_diagram(tex_diagram_template_txt, nump, numip, numdp):
 42    tex_diagram_template_txt_ans = tex_diagram_template_txt
 43    kv = decf.get_dec_dict(nump, numip, numdp)
 44    posttext = r"\vspace{-1pt}"
 45
 46    for key, value in kv.items():
 47        tex_diagram_template_txt_ans = tex_diagram_template_txt_ans.replace(
 48            "<<" + key + ">>", value
 49        )
 50
 51    for key, value in kv.items():
 52        if key in tex_keys_q:
 53            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 54                "<<" + key + ">>", ""
 55            )
 56        else:
 57            tex_diagram_template_txt = tex_diagram_template_txt.replace(
 58                "<<" + key + ">>", value
 59            )
 60    # return tex_diagram_template_txt
 61    return tex_diagram_template_txt + posttext, tex_diagram_template_txt_ans + posttext
 62
 63
 64def get_title(nump):
 65    match nump:
 66        case 1:
 67            return "Addition"
 68        case 2:
 69            return "Subtraction"
 70        case 3:
 71            return "Multiplication"
 72        case 4:
 73            return "Addition and subtraction"
 74        case 5:
 75            return "Addition, subtraction and multiplication"
 76
 77
 78def main():
 79    nump = input("Enter 1, 2, or 3 for +, -, random for the process \n")
 80    if nump.strip().isdigit():
 81        nump = int(nump)
 82        if not nump in [1, 2]:
 83            nump = 3  # random by default
 84    else:
 85        nump = 3  # random by default
 86    # get title for part of heading indicating which process/es
 87    title = get_title(nump)
 88    #
 89    numip = input(
 90        "Enter 0, 1, 2, 3, or 4 for the number of places before the decimal point: \n"
 91    )
 92    if numip.strip().isdigit():
 93        numip = int(numip)
 94        if not numip in [0, 1, 2, 3, 4]:
 95            numip = 1  # 1 by default
 96    else:
 97        numip = 1  # 1 by default
 98    #
 99    numdp = input(
100        "Enter 1, 2, 3, 4, or 5 for the number of decimal places: \n")
101    if numdp.strip().isdigit():
102        numdp = int(numdp)
103        if not numdp in [1, 2, 3, 4, 5]:
104            numdp = 1  # 1 by default
105    else:
106        numdp = 1  # 1 by default
107    #
108    #
109    numq = input(
110        "Enter the number of questions from 1 to 108, with 27 per page: \n")
111    if numq.strip().isdigit():
112        numq = int(numq)
113        if not numq in range(1, 109):
114            numq = 27  # 27 by default
115    else:
116        numq = 27  # 27 by default
117    #
118    filename = input(
119        "Enter the base filename to be added to the prefix asd_: \n")
120    if not filename:
121        filename = "1"  # "asd_1_q and asd_1_ans as default file"
122    # set names of files that are made
123    # questions
124    tex_output_path = currfile_dir / f"asdBk_{filename}_q.tex"
125
126    # answers
127    tex_output_path_ans = currfile_dir / f"asdBk_{filename}_ans.tex"
128
129    # Read in the LaTeX template file
130    with open(tex_template_path, "r") as infile:
131        tex_template_txt = infile.read()
132    # Read in the LaTeX template file for answers
133    with open(texans_template_path, "r") as infile:
134        tex_template_txt_ans = infile.read()
135    # Read in the LaTeX diagram template file
136    with open(tex_diagram_template_path, "r") as infile:
137        tex_diagram_template_txt = infile.read()
138
139    # <<diagrams>>
140    # generate column text and column text for answers
141    diagram_text = ""
142    diagram_text_ans = ""
143    for i in range(1, numq + 1):
144        img_tex, img_tex_ans = make1_diagram(tex_diagram_template_txt, nump,
145                                             numip, numdp)
146        diagram_text += img_tex
147        diagram_text_ans += img_tex_ans
148        if i % 27 == 0 and i > 0 and i != numq:
149            diagram_text += colbreak
150            diagram_text_ans += colbreak
151
152    # Replace the <<title>> placeholder in the LaTeX template
153    tex_template_txt = tex_template_txt.replace("<<title>>", title)
154    tex_template_txt_ans = tex_template_txt_ans.replace("<<title>>", title)
155    # Replace the <<diagrams>> placeholder in the LaTeX template with the generated diagrams
156    tex_template_txt = tex_template_txt.replace("<<diagrams>>", diagram_text)
157    tex_template_txt_ans = tex_template_txt_ans.replace(
158        "<<diagrams>>", diagram_text_ans)
159
160    # Write the question tex to an output file
161    with open(tex_output_path, "w") as outfile:
162        outfile.write(tex_template_txt)
163
164    # Write the answer tex to an output file
165    with open(tex_output_path_ans, "w") as outfile:
166        outfile.write(tex_template_txt_ans)
167
168    # Wait for the file to be created
169    time.sleep(1)
170    # Convert the LaTeX files to PDFs
171    convert_to_pdf(tex_output_path, currfile_dir)
172    convert_to_pdf(tex_output_path_ans, currfile_dir)
173
174
175if __name__ == "__main__":
176    print("starting")
177    main()
178    print("finished")