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 # Anzahl der zu generierenden Fragen. NUM_QUESTIONS = 5 # Algebraische Struktur der Aufgaben STRUCTURE = "(+)-((+)-(+))" # Gleiche Nenner erlauben SAME_ALLOWED = True # Größter Zähler MAX_NUMERATOR = 10 # Größter Nenner MAX_DENOMINATOR = 8 # Größter erlaubter gemeinsamer Nenner MAX_COMMON_DENOMINATOR = 100 # Dateiname für Ausgabe FILENAME = 'formulas_new_quiz.xml' def parse_expression(expression): """Parse an algebraic expression and determine the required number of numbers. Args: expression (str): The algebraic expression to parse. Returns: int: The total number of numbers required for the expression. """ op_count = 0 # Count operators in parentheses paren_count = 1 max_depth = 0 for i, char in enumerate(expression): if char in ops.keys(): op_count += 1 return max_depth + op_count + 1 # Anzahl der benötigten Brüche NUM_FRACTIONS = parse_expression(STRUCTURE) def add_placeholders(expression): """Add placeholders to the expression for calculation. Args: expression (str): The algebraic expression to modify. Returns: str: The modified expression with placeholders. """ placeholder_counter = 0 # Zähler für die Platzhalter def placeholder_generator(): nonlocal placeholder_counter placeholder = f'n{placeholder_counter}' placeholder_counter += 1 return placeholder output = [] # Ausgabe als Liste zum späteren Zusammensetzen need_placeholder = True # Flag, um zu tracken, ob ein Platzhalter hinzugefügt werden soll for char in expression: if char in '+-*/': # Operator gefunden if need_placeholder: # Wenn wir einen Platzhalter brauchen output.append(placeholder_generator()) output.append(char) # Fügen Sie den Operator hinzu need_placeholder = True # Nach einem Operator müssen wir wieder einen Platzhalter hinzufügen elif char == '(': # Eine neue Sub-Expression beginnt output.append(char) # Fügen Sie die öffnende Klammer hinzu need_placeholder = True elif char == ')': # Eine Sub-Expression endet if need_placeholder: # Wenn wir einen Platzhalter brauchen output.append(placeholder_generator()) output.append(char) # Fügen Sie die schließende Klammer hinzu need_placeholder = False # Nach einer schließenden Klammer erwarten wir keine Platzhalter mehr else: # Bei anderen Zeichen if need_placeholder: # Wenn wir einen Platzhalter brauchen output.append(placeholder_generator()) output.append(char) # Fügen Sie das Zeichen hinzu need_placeholder = False # Nach einem Platzhalter sollten wir keinen weiteren setzen # Platzhalter am Beginn hinzufügen if output and not (output[0] == f'n{0}' or output[0] == '('): result = [placeholder_generator()] + output else: result = output # Platzhalter am Ende hinzufügen, wenn nötig if output[-1] in '+-*/': result = result + [placeholder_generator()] else: pass # Zusammensetzen des finalen Ausdrucks final_expression = ''.join(result).strip() print(final_expression) return final_expression # Algebraische Struktur mit Platzhaltern STRUCTURE_PH = add_placeholders(STRUCTURE) def calculate(expression, numbers): """Calculate the result of the expression using provided numbers. Args: expression (str): The algebraic expression with placeholders. numbers (list): A list of numbers to replace the placeholders. Returns: float: The calculated result of the expression. """ print(numbers) # Replace placeholders with actual number values 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) print(expression) # Evaluate the result return eval(expression) def gen_calculate_string(expression: str, liststring: str): """Generate a calculation string for the expression with actual number values. Args: expression (str): The algebraic expression with placeholders. liststring (str): The list of values to replace the placeholders. Returns: str: The modified expression with the list values. """ for i in range(NUM_FRACTIONS): placeholder = f'n{i}' expression = re.sub(rf'\{placeholder}\s*([+-/*])', rf'{liststring}[{i}] \1', expression) expression = re.sub(rf'({placeholder})\s*', rf'{liststring}[{i}]', expression) print(expression) return expression def gen_latex_calculate_string(expression: str, liststring: str): """Generate a LaTeX-compatible calculation string for the expression. Args: expression (str): The algebraic expression with placeholders. liststring (str): A list of LaTeX strings to replace the placeholders. Returns: str: The modified expression formatted for LaTeX. """ for i in range(NUM_FRACTIONS): placeholder = f'n{i}' replacement = liststring[i] print(replacement) expression = re.sub(rf'\{placeholder}\s*([+-/*])', replacement, expression) expression = re.sub(rf'({placeholder})\s*', replacement, expression) # Klammern anpassen expression = re.sub(rf'\(', u'\\\\left(', expression) expression = re.sub(rf'\)', u'\\\\right)', expression) return expression def create_html_table_for_fracture_input(rows: list): """Create an HTML table for displaying fraction inputs. Args: rows (list): A list of rows to include in the table. Returns: str: The generated HTML table as a string. """ table_rows = [] for i, row in enumerate(rows): if (i % 2) == 0: table_row = "
{#A}
]]>" else: text_elem.text = f"{exercise_text}."+"{#A}
]]>" # Allgemeines Feedback generalfeedback_elem = ET.SubElement(question, 'generalfeedback', attrib={'format': 'html'}) text_elem = ET.SubElement(generalfeedback_elem, 'text') text_elem.text = ' ' # Grundlegende Bewertungsinformationen ET.SubElement(question, 'defaultgrade').text = '1.0000000' ET.SubElement(question, 'penalty').text = '0.3333333' ET.SubElement(question, 'hidden').text = '0' ET.SubElement(question, 'idnumber').text = ' ' # Feedback für korrekte und inkorrekte Antworten 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. ]]>''' ET.SubElement(question, 'shownumcorrect') varsrandom_elem = ET.SubElement(question, 'varsrandom') text_elem = ET.SubElement(varsrandom_elem, 'text') text_elem.text = f' ' # Globale Variablen definieren 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}];' # Durchnummerierung der Antworten answernumbering_elem = ET.SubElement(question, 'answernumbering') text_elem = ET.SubElement(answernumbering_elem, 'text') text_elem.text = 'ABCD' # Antworten definieren answers_elem = ET.SubElement(question, 'answers') # Indizes für die Antworten 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' # Punktzahl auf Teilaufgabe answermark_elem = ET.SubElement(answers_elem, 'answermark') if shortable: ET.SubElement(answermark_elem, 'text').text = '2' else: ET.SubElement(answermark_elem, 'text').text = '1' # Antworttyp answertype_elem = ET.SubElement(answers_elem, 'answertype') ET.SubElement(answertype_elem, 'text').text = '0' numbox_elem = ET.SubElement(answers_elem, 'numbox') ET.SubElement(numbox_elem, 'text').text = '2' # Berechnungsvariablen # common divisor cdtext = "" for i in range(NUM_FRACTIONS): if i == 0: pass elif i == 1: cdtext += f"cd{i} = lcm(d[{i}],d[{i-1}]);" else: cdtext += f"cd{i} = lcm(cd{i-1}, d[{i}]);" cdtext += f"cd = cd{NUM_FRACTIONS-1};" # expansions extext = "e = [" for i in range(NUM_FRACTIONS): extext += f"cd / d[{i}]," extext = extext[:-1] extext += "];" # expanded_numerators entext = "ens = [" for i in range(NUM_FRACTIONS): entext += f"n[{i}]*e[{i}]," entext = entext[:-1] entext += "];" # result numerator resnum = "resnum = " + gen_calculate_string(STRUCTURE_PH, "ens") + ";" # gcd of result numerator and result (common) denominator resgcd = "resgcd = gcd(resnum, cd);" # shortened result numerator sresnum = "sresnum = resnum / resgcd;" # shortened result denominator sresden = "sresden = cd / resgcd;" vars1_elem = ET.SubElement(answers_elem, 'vars1') if shortable: vars1_elem_text = f"" else: vars1_elem_text = f"" ET.SubElement(vars1_elem, 'text').text = vars1_elem_text # Antwort answer_elem = ET.SubElement(answers_elem, 'answer') if shortable: answer_text = '[resnum, cd, sresnum, sresden]' else: answer_text = '[resnum, cd]' ET.SubElement(answer_elem, 'text').text = answer_text answernotunique_elem = ET.SubElement(answers_elem, 'answernotunique') ET.SubElement(answernotunique_elem, 'text').text = '0' vars2_elem = ET.SubElement(answers_elem, 'vars2') if shortable: vars2_elem_text = "" else: vars2_elem_text = "" ET.SubElement(vars2_elem, 'text').text = vars2_elem_text correctness_elem = ET.SubElement(answers_elem, 'correctness') if shortable: ET.SubElement(correctness_elem, 'text').text = '0.25*ga + 0.25*gb + 0.25*gsa + 0.25*gsb' else: ET.SubElement(correctness_elem, 'text').text = '0.5*ga + 0.5*gb' unitpenalty_elem = ET.SubElement(answers_elem, 'unitpenalty') ET.SubElement(unitpenalty_elem, 'text').text = '1' postunit_elem = ET.SubElement(answers_elem, 'postunit') ET.SubElement(postunit_elem, 'text').text = ' ' ruleid_elem = ET.SubElement(answers_elem, 'ruleid') ET.SubElement(ruleid_elem, 'text').text = '1' otherrule_elem = ET.SubElement(answers_elem, 'otherrule') ET.SubElement(otherrule_elem, 'text').text = ' ' # Unterfragen-Text subqtext_elem = ET.SubElement(answers_elem, 'subqtext', attrib={'format': 'html'}) fractions_latex_strings = [] for i in range(NUM_FRACTIONS): l_str = "\\\\frac{{"+ f"n[{i}]" +"}}{{"+ f"d[{i}]" +"}}" fractions_latex_strings.append(l_str) latex_calculation = gen_latex_calculate_string(STRUCTURE_PH, fractions_latex_strings) sub_q_t_begin = "Berechne:" sub_q_t_math = "\$\\Large \\displaystyle " + f"{latex_calculation}" + "=\$
" rows_list = [ ["Zähler:", "{_0}"], ["Nenner:", "{_1}"] ] sub_q_t_input = 'Die korrekte Lösung ist:
\$\\displaystyle''' + f"{latex_calculation}" +" = " + f"{expanded_latex_calculation}" + ''' = \\frac{{resnum}}{{cd}} = \\frac{{sresnum}}{{sresden}} \$
]]>''' else: feedback_text = '''LösungshinweisDie korrekte Lösung ist:
\$ \\displaystyle ''' + f"{latex_calculation}" +" = " + f"{expanded_latex_calculation}" + ''' = \\frac{{resnum}}{{cd}} \$
]]>''' ET.SubElement(feedback_elem, 'text').text = feedback_text # correctfeedback correctfeedback_elem = ET.SubElement(answers_elem, 'correctfeedback', attrib={'format': 'html'}) correctfeedback_text = '''Richtig!]]>''' ET.SubElement(correctfeedback_elem, 'text').text = correctfeedback_text # partially correct feedback pcorrectfeedback_elem = ET.SubElement(answers_elem, 'partiallycorrectfeedback', attrib={'format': 'html'}) pcorrectfeedback_text = '''Teilweise richtig.]]>''' ET.SubElement(pcorrectfeedback_elem, 'text').text = pcorrectfeedback_text # incorrect feedback incorrectfeedback_elem = ET.SubElement(answers_elem, 'incorrectfeedback', attrib={'format': 'html'}) incorrectfeedback_text = '''Leider nicht richtig.]]>''' ET.SubElement(incorrectfeedback_elem, 'text').text = incorrectfeedback_text return question def generate_questions(num_questions): """Generates a list of questions. Args: num_questions (int): The number of questions to generate. Returns: list: A list of question elements. """ questions = [] for i in range(num_questions): questions.append(create_formula_question(i)) return questions def create_quiz(questions): """Creates the overall quiz element. Args: questions (list): A list of question elements. Returns: Element: The complete quiz element. """ quiz = ET.Element('quiz') for q in questions: quiz.append(q) return quiz def save_to_file(xml_element, filename): """Saves the XML document to a file. Args: xml_element (Element): The XML element to save. filename (str): The name of the target file. Returns: None """ 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): """Replaces special characters in the XML file. Args: xml_file (str): The name of the XML file to modify. Returns: None """ # XML-Datei lesen 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.") # Hauptprogramm 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.")