diff --git a/README.md b/README.md new file mode 100644 index 0000000..1edf5e2 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Maths Worksheet Generator + +![sample worksheet](sample.png) + +## Background +One of my best friends is training his 5 years old with basic addition questions from store-bought material. Good for one time usage as kid is smart enough to memorize the answers. He wants to give him more practice. + +Two solutions: +1. keep buying more one time usage materials (less beer budget); or +2. make question sets with the number pairs and calculate the answer for each question manually (less beer time) + +Not ideal. + +That's the reason for me to look into an automate way to get the job done. + +## Benefit of the Maths Worksheet Generator +You can generate as many non duplicated questions as you need with the answer sheet in a fraction of second. + +There are four choices: +1. Addition +2. Subtraction +3. Multiplication +4. Mixed + +## Requirements +1. [python3](https://www.python.org/downloads/) +2. [fpdf](https://pypi.org/project/fpdf/) + +## How to Use +1. Generate the worksheet in pdf format with the following command: +``` +python3 run.py --type [+|-|x|mix] +``` +For example, for addition only worksheet, use the following command: +``` +python3 run.py --type + +``` +3. Print out the generated file `worksheet.pdf` + +## Sample +[sample worksheet](sample-worksheet.pdf) + +## Code Overview +Everything is written in python in `run.py`. You can play with the font and gird size with the variables under the `# Basic settings` section. + +## Contributing +Any suggestion will be appreciated. You can simply create a pull request with your idea. Feel free to fork the project too. + +## Special Thanks +My long time friend San for the inspiration of this project and lovely sons Tim and Hin. \ No newline at end of file diff --git a/run.py b/run.py new file mode 100644 index 0000000..06167c9 --- /dev/null +++ b/run.py @@ -0,0 +1,156 @@ +import argparse +import random +from fpdf import FPDF + + +class maths_worksheet_generator(): + pdf = FPDF() + # Basic settings + small_font_size = 10 + middle_font_size = 15 + large_font_size = 30 + size = 21 + tiny_pad_size = 2 + pad_size = 10 + large_pad_size = 30 + num_x_cell = 4 + num_y_cell = 2 + total_question = 80 # Must be a multiple of 40 + font_1 = 'Times' + font_2 = 'Arial' + + def generate_question(self, type): + # Generate each question and calculate the answer depending on the type in a list + # To keep it simple, number is generated randomly within the range of 0 to 100 + num_1 = random.randint(0, 100) + num_2 = random.randint(0, 100) + if type == 'mix': + type = random.choice(['+', '-', 'x']) + if type == '+': + answer = num_1 + num_2 + elif type == '-': + answer = num_1 - num_2 + elif type == 'x': + answer = num_1 * num_2 + else: + raise RuntimeError('Question type {} not supported'.format(type)) + return [num_1, type, num_2, answer] + + def get_list_of_questions(self, type): + # Generate all the questions for the worksheet in a list + questions = [] + while len(questions) < self.total_question: + new_question = self.generate_question(type) + if new_question not in questions: + questions.append(new_question) + return questions + + def make_question_page(self, data): + # Prepare a single page of question + total_page = int(self.total_question / (self.num_x_cell * self.num_y_cell)) + for page in range(0, total_page): + self.pdf.add_page(orientation='L') + self.print_question_row(data, (page) * (2 * self.num_x_cell)) + self.print_horizontal_seperator() + self.print_question_row(data, (page) * (2 * self.num_x_cell) + self.num_x_cell) + + def print_top_row(self, question_num): + # Helper function to print first row of a question + self.pdf.set_font(self.font_1, size=self.middle_font_size) + self.pdf.cell(self.pad_size, self.pad_size, txt=question_num, border='LT', ln=0, align='C') + self.pdf.cell(self.size, self.pad_size, border='T', ln=0, align='C') + self.pdf.cell(self.size, self.pad_size, border='T', ln=0, align='C') + self.pdf.cell(self.pad_size, self.pad_size, border='TR', ln=0, align='C') + + def print_second_row(self, num): + # Helper function to print second row of a question + self.pdf.set_font(self.font_2, size=self.large_font_size) + self.pdf.cell(self.pad_size, self.size, border='L', ln=0, align='C') + self.pdf.cell(self.size, self.size, border=0, ln=0, align='C') + self.pdf.cell(self.size, self.size, txt=str(num), border=0, ln=0, align='R') + self.pdf.cell(self.pad_size, self.size, border='R', ln=0, align='C') + + def print_third_row(self, num, type): + # Helper function to print third row of a question + self.pdf.set_font(self.font_2, size=self.large_font_size) + self.pdf.cell(self.pad_size, self.size, border='L', ln=0, align='C') + self.pdf.cell(self.size, self.size, txt=type, border=0, ln=0, align='L') + self.pdf.cell(self.size, self.size, txt=str(num), border=0, ln=0, align='R') + self.pdf.cell(self.pad_size, self.size, border='R', ln=0, align='C') + + def print_bottom_row(self): + # Helper function to print bottom row of question + self.pdf.set_font(self.font_2, size=self.large_font_size) + self.pdf.cell(self.pad_size, self.size, border='LB', ln=0, align='C') + self.pdf.cell(self.size, self.size, border='TB', ln=0, align='C') + self.pdf.cell(self.size, self.size, border='TB', ln=0, align='R') + self.pdf.cell(self.pad_size, self.size, border='BR', ln=0, align='C') + + def print_edge_vertical_seperator(self): + # Print space between question for the top or bottom row + self.pdf.cell(self.pad_size, self.pad_size, border=0, ln=0) + + def print_middle_vertical_seperator(self): + # Print space betwen question for the second or third row + self.pdf.cell(self.pad_size, self.size, border=0, ln=0) + + def print_horizontal_seperator(self): + # Print line breaker between two rows of questions + self.pdf.cell(self.size, self.size, border=0, ln=0, align='C') + self.pdf.ln() + + def print_question_row(self, data, offset): + # Print a single row of questions (total question in a row is set by num_x_cell) + for x in range(0, self.num_x_cell): + self.print_top_row(str(x + 1 + offset)) + self.print_edge_vertical_seperator() + self.pdf.ln() + for x in range(0, self.num_x_cell): + self.print_second_row(data[x + offset][0]) + self.print_middle_vertical_seperator() + self.pdf.ln() + for x in range(0, self.num_x_cell): + self.print_third_row(data[x + offset][2], data[x + offset][1]) + self.print_middle_vertical_seperator() + self.pdf.ln() + for _ in range(0, self.num_x_cell): + self.print_bottom_row() + self.print_edge_vertical_seperator() + self.pdf.ln() + + def make_answer_page(self, data): + # Print answer sheet + self.pdf.add_page(orientation='L') + self.pdf.set_font(self.font_1, size=self.large_font_size) + self.pdf.cell(self.large_pad_size, self.large_pad_size, txt='Answers', border=0, ln=1, align='C') + + for i in range(len(data)): + self.pdf.set_font(self.font_1, size=self.small_font_size) + self.pdf.cell(self.pad_size, self.pad_size, txt='{}:'.format(i+1), border='TLB', ln=0, align='R') + self.pdf.set_font(self.font_2, size=self.small_font_size) + self.pdf.cell(self.pad_size, self.pad_size, txt=str(data[i][3]), border='TB', ln=0, align='R') + self.pdf.cell(self.tiny_pad_size, self.pad_size, border='TRB', ln=0, align='R') + self.pdf.cell(self.tiny_pad_size, self.pad_size, border=0, ln=0, align='C') + if (i+1) >= 10 and (i+1) % 10 == 0: + self.pdf.ln() + + +def main(type): + new_pdf = maths_worksheet_generator() + seed_question = new_pdf.get_list_of_questions(type) + new_pdf.make_question_page(seed_question) + new_pdf.make_answer_page(seed_question) + new_pdf.pdf.output("worksheet.pdf") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Generate Maths Addition/Subtraction/Multiplication Exercise Worksheet') + parser.add_argument('--type', default='+', choices=['+', '-', 'x', 'mix'], + help='type of calculation: ' + '+: Addition; ' + '-: Substration; ' + 'x: Multipication; ' + 'mix: Mixed; ' + '(default: +)') + args = parser.parse_args() + main(args.type) diff --git a/sample-worksheet.pdf b/sample-worksheet.pdf new file mode 100644 index 0000000..b0993e7 Binary files /dev/null and b/sample-worksheet.pdf differ diff --git a/sample.png b/sample.png new file mode 100644 index 0000000..9a0883b Binary files /dev/null and b/sample.png differ