import random import operator import re from math import gcd, lcm import xml.etree.ElementTree as ET from collections import Counter # Define operators and their corresponding functions ops = { '+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv } # Konstanten NUM_QUESTIONS = 5 STRUCTURE = "(+)-((+)-(+))" SAME_ALLOWED = True MAX_WHOLE = 5 # Größte ganze Zahl für gemischte Brüche MAX_NUMERATOR = 10 # Größter Zähler MAX_DENOMINATOR = 8 # Größter Nenner MAX_COMMON_DENOMINATOR = 100 FILENAME = 'formulas_new_quiz.xml' def parse_expression(expression): op_count = 0 for char in expression: if char in ops.keys(): op_count += 1 return op_count + 1 NUM_FRACTIONS = parse_expression(STRUCTURE) def add_placeholders(expression): placeholder_counter = 0 def placeholder_generator(): nonlocal placeholder_counter placeholder = f'n{placeholder_counter}' placeholder_counter += 1 return placeholder output = [] need_placeholder = True for char in expression: if char in '+-*/': if need_placeholder: output.append(placeholder_generator()) output.append(char) need_placeholder = True elif char == '(': output.append(char) need_placeholder = True elif char == ')': if need_placeholder: output.append(placeholder_generator()) output.append(char) need_placeholder = False else: if need_placeholder: output.append(placeholder_generator()) output.append(char) need_placeholder = False if output and not (output[0] == f'n{0}' or output[0] == '('): result = [placeholder_generator()] + output else: result = output if output[-1] in '+-*/': result = result + [placeholder_generator()] final_expression = ''.join(result).strip() return final_expression STRUCTURE_PH = add_placeholders(STRUCTURE) def calculate(expression, numbers): for i, num in enumerate(numbers): placeholder = f'n{i}' expression = re.sub(rf'\{placeholder}\s*([+-/*])', rf'{num} \1', expression) expression = re.sub(rf'({placeholder})\s*', rf'{num}', expression) return eval(expression) def create_mixed_fraction(): """Generates a random mixed fraction represented as a dictionary.""" whole = random.randint(1, MAX_WHOLE) numerator = random.randint(1, MAX_NUMERATOR) denominator = random.randint(2, MAX_DENOMINATOR) return { "whole": whole, "numerator": numerator, "denominator": denominator } def create_formula_question(ex_number: int): numerators = [] denominators = [] mixed_fractions = [] for i in range(NUM_FRACTIONS): mixed_fraction = create_mixed_fraction() mixed_fractions.append(mixed_fraction) # Convert mixed fraction to improper fraction for calculation improper_numerator = mixed_fraction["whole"] * mixed_fraction["denominator"] + mixed_fraction["numerator"] numerators.append(improper_numerator) denominators.append(mixed_fraction["denominator"]) if not SAME_ALLOWED: same_denominators = [k for k, v in Counter(denominators).items() if v > 1] while len(same_denominators) > 1: new_denom = random.randint(2, MAX_DENOMINATOR) try: index = denominators.index(same_denominators[0]) denominators[index] = new_denom except ValueError: pass same_denominators = [k for k, v in Counter(denominators).items() if v > 1] *denoms_integers, = denominators common_denominator = lcm(*denoms_integers) expanded_numerators = [(frac["whole"] * frac["denominator"] + frac["numerator"]) * common_denominator // frac["denominator"] for frac in mixed_fractions] result_numerator = calculate(STRUCTURE_PH, expanded_numerators) gcd_result = gcd(result_numerator, common_denominator) shortable = gcd_result > 1 shortened_numerator = result_numerator // gcd_result if shortable else result_numerator shortened_denominator = common_denominator // gcd_result if shortable else common_denominator question = ET.Element('question', attrib={'type': 'formulas'}) has_plus = '+' in STRUCTURE has_minus = '-' in STRUCTURE if has_plus and has_minus: expression_type = "Addition/Subtraktion" exercise_text = "Berechne den Term" elif has_plus: expression_type = "Addition" exercise_text = "Addiere die gemischten Brüche" elif has_minus: expression_type = "Subtraktion" exercise_text = "Subtrahiere die gemischten Brüche" else: expression_type = "Rechnung" exercise_text = "Berechne den Term" name_elem = ET.SubElement(question, 'name') text_elem = ET.SubElement(name_elem, 'text') text_elem.text = f'{expression_type} von {NUM_FRACTIONS} gemischten Brüchen {ex_number}' questiontext_elem = ET.SubElement(question, 'questiontext', attrib={'format': 'html'}) text_elem = ET.SubElement(questiontext_elem, 'text') text_elem.text = f"{exercise_text} und kürze das Ergebnis so weit wie möglich.

" + "

{#A}

]]>" generalfeedback_elem = ET.SubElement(question, 'generalfeedback', attrib={'format': 'html'}) text_elem = ET.SubElement(generalfeedback_elem, 'text') text_elem.text = ' ' ET.SubElement(question, 'defaultgrade').text = '1.0000000' ET.SubElement(question, 'penalty').text = '0.3333333' ET.SubElement(question, 'hidden').text = '0' correctfeedback_elem = ET.SubElement(question, 'correctfeedback', attrib={'format': 'html'}) text_elem = ET.SubElement(correctfeedback_elem, 'text') text_elem.text = '''Die Antwort ist richtig.

]]> ''' partiallycorrectfeedback_elem = ET.SubElement(question, 'partiallycorrectfeedback', attrib={'format': 'html'}) text_elem = ET.SubElement(partiallycorrectfeedback_elem, 'text') text_elem.text = '''Die Antwort ist teilweise richtig.

]]> ''' incorrectfeedback_elem = ET.SubElement(question, 'incorrectfeedback', attrib={'format': 'html'}) text_elem = ET.SubElement(incorrectfeedback_elem, 'text') text_elem.text = '''Die Antwort ist falsch.

]]>''' varsrandom_elem = ET.SubElement(question, 'varsrandom') text_elem = ET.SubElement(varsrandom_elem, 'text') text_elem.text = f' ' varsglobal_elem = ET.SubElement(question, 'varsglobal') text_elem = ET.SubElement(varsglobal_elem, 'text') numerators_string = ",".join([str(n) for n in numerators]) denominators_string = ",".join([str(d) for d in denominators]) text_elem.text = f'n = [{numerators_string}];\n d = [{denominators_string}];' answers_elem = ET.SubElement(question, 'answers') partindex_elem = ET.SubElement(answers_elem, 'partindex') ET.SubElement(partindex_elem, 'text').text = '0' placeholder_elem = ET.SubElement(answers_elem, 'placeholder') ET.SubElement(placeholder_elem, 'text').text = '#A' answermark_elem = ET.SubElement(answers_elem, 'answermark') ET.SubElement(answermark_elem, 'text').text = '2' if shortable else '1' answer_elem = ET.SubElement(answers_elem, 'answer') answer_text = '[resnum, cd, sresnum, sresden]' if shortable else '[resnum, cd]' ET.SubElement(answer_elem, 'text').text = answer_text vars1_elem = ET.SubElement(answers_elem, 'vars1') vars1_elem_text = f"" ET.SubElement(vars1_elem, 'text').text = vars1_elem_text vars2_elem = ET.SubElement(answers_elem, 'vars2') vars2_elem_text = "" ET.SubElement(vars2_elem, 'text').text = vars2_elem_text correctness_elem = ET.SubElement(answers_elem, 'correctness') ET.SubElement(correctness_elem, 'text').text = '0.5*ga + 0.5*gb' feedback_elem = ET.SubElement(answers_elem, 'feedback', attrib={'format': 'html'}) feedback_text = '''Lösungshinweis

Die korrekte Lösung ist:

\\(\\displaystyle''' + f"{result_numerator}/{common_denominator}" + '''\\)

]]>''' ET.SubElement(feedback_elem, 'text').text = feedback_text return question def generate_questions(num_questions): questions = [] for i in range(num_questions): questions.append(create_formula_question(i)) return questions def create_quiz(questions): quiz = ET.Element('quiz') for q in questions: quiz.append(q) return quiz def save_to_file(xml_element, filename): tree = ET.ElementTree(xml_element) ET.indent(tree, space="\t", level=0) with open(filename, 'wb') as f: tree.write(f, encoding='utf_8', xml_declaration=True) return 0 def replace_vars2(xml_file): with open(xml_file, 'r') as f: text = f.read() text = text.replace("&", "&") text = text.replace("<", "<") text = text.replace(">", ">") with open(xml_file, 'w') as f: f.write(text) print("Ersetzungen abgeschlossen.") if __name__ == "__main__": questions = generate_questions(NUM_QUESTIONS) quiz = create_quiz(questions) save_to_file(quiz, FILENAME) replace_vars2(FILENAME) print("Moodle-XMLDatei erfolgreich erstellt.")