From 1fb7dd0c3df83bbaf788b4807f3afa54254293ae Mon Sep 17 00:00:00 2001 From: Martin Putzlocher Date: Thu, 5 Dec 2024 18:58:09 +0100 Subject: [PATCH] =?UTF-8?q?Vorbereitung=20f=C3=BCr=20Skript=20f=C3=BCr=20g?= =?UTF-8?q?emischte=20Br=C3=BCche?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aufgabengenerator_formulas_fraction_new_c.py | 594 ++++++++++++++++++ ...abengenerator_formulas_gem_fraction_new.py | 252 ++++++++ 2 files changed, 846 insertions(+) create mode 100644 aufgabengenerator_formulas_fraction_new_c.py create mode 100644 aufgabengenerator_formulas_gem_fraction_new.py diff --git a/aufgabengenerator_formulas_fraction_new_c.py b/aufgabengenerator_formulas_fraction_new_c.py new file mode 100644 index 0000000..0e4037f --- /dev/null +++ b/aufgabengenerator_formulas_fraction_new_c.py @@ -0,0 +1,594 @@ +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 = "" + else: + table_row = "" + + for cell in row: + table_row += f"{cell}" + + table_row += "" + table_rows.append(table_row) + + # Tabelle zusammenbauen + table_html = f"\n" + "\n".join(table_rows) + "
" + + return table_html + + +def create_formula_question(ex_number: int): + """Create a formula question based on a random set of fractions. + + Args: + ex_number (int): The index number of the exercise being created. + + Returns: + Element: The XML element representing the question. + """ + numerators = [] + denominators = [] + for i in range(NUM_FRACTIONS): + n = random.randint(1, MAX_NUMERATOR) + d = random.randint(2, MAX_DENOMINATOR) + numerators.append(n) + denominators.append(d) + + # Müssen Nenner unterschiedlich sein? + if not SAME_ALLOWED: + # Sicherstellen, dass die Nenner unterschiedlich sind + 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] + else: + pass + + # Hauptnenner bestimmen + *denoms_integers, = denominators # Entpacken der Liste + common_denominator = lcm(*denoms_integers) + cd = common_denominator + + fractions = [] + for i in range(NUM_FRACTIONS): + fraction = {"n": numerators[i], "d": denominators[i]} + fractions.append(fraction) + + expanded_numerators = [frac["n"] * cd // frac["d"] for frac in fractions] + + expanded_fractions = [] + for i in range(NUM_FRACTIONS): + fraction = {"n": expanded_numerators[i], "d": cd} + expanded_fractions.append(fraction) + + result_numerator = calculate(STRUCTURE_PH, expanded_numerators) + + gcd_result = gcd(result_numerator, cd) + if gcd_result > 1: + shortable = True + shortened_numerator = result_numerator // gcd_result + shortened_denominator = common_denominator // gcd_result + else: + shortable = False + + # Start der Aufgabe + question = ET.Element('question', attrib={'type': 'formulas'}) + + # Name der Aufgabe + has_plus = '+' in STRUCTURE + has_minus = '-' in STRUCTURE + has_mult = '*' in STRUCTURE + has_div = '/' in STRUCTURE + + if has_plus and has_minus: + expression_type = "Addition/Subtraktion" + f" ({STRUCTURE})" + exercise_text = "Berechne den Term" + elif has_plus: + expression_type = "Addition" + f" ({STRUCTURE})" + exercise_text = "Addiere die Brüche" + elif has_minus: + expression_type = "Subtraktion" + f" ({STRUCTURE})" + exercise_text = "Subtrahiere die Brüche" + else: + expression_type = "Rechnung" + f" ({STRUCTURE})" + 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} ungleichnamigen Brüchen {ex_number}' + + # Allgemeiner Aufgabentext + questiontext_elem = ET.SubElement(question, 'questiontext', attrib={'format': 'html'}) + text_elem = ET.SubElement(questiontext_elem, 'text') + if shortable: + text_elem.text = f"{exercise_text} und kürze das Ergebnis so weit wie möglich.

" + "

{#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 = '
=' + if shortable: + sub_q_t_input += create_html_table_for_fracture_input(rows_list) + + rows_list_s = [["Gekürzter Zähler:", "{_2}"], + ["Gekürzter Nenner:", "{_3}"]] + sub_q_t_input += " = " + sub_q_t_input += create_html_table_for_fracture_input(rows_list_s) + + else: + sub_q_t_input += create_html_table_for_fracture_input(rows_list) + sub_q_t_input += '
' + sub_q_t_end = "]]>" + + sub_question_text = sub_q_t_begin + sub_q_t_math + sub_q_t_input + sub_q_t_end + ET.SubElement(subqtext_elem, 'text').text = sub_question_text + + expanded_fractions_latex_strings = [] + for i in range(NUM_FRACTIONS): + l_str = "\\\\frac{{"+ f"ens[{i}]" +"}}{{"+ f"cd" +"}}" + expanded_fractions_latex_strings.append(l_str) + expanded_latex_calculation = gen_latex_calculate_string(STRUCTURE_PH, expanded_fractions_latex_strings) + + # Lösungshinweis + feedback_elem = ET.SubElement(answers_elem, 'feedback', attrib={'format': 'html'}) + if shortable: + feedback_text = '''Lösungshinweis +

Die korrekte Lösung ist:

+

\$\\displaystyle''' + f"{latex_calculation}" +" = " + f"{expanded_latex_calculation}" + ''' = \\frac{{resnum}}{{cd}} = \\frac{{sresnum}}{{sresden}} \$

]]>''' + else: + feedback_text = '''Lösungshinweis +

Die 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.") + diff --git a/aufgabengenerator_formulas_gem_fraction_new.py b/aufgabengenerator_formulas_gem_fraction_new.py new file mode 100644 index 0000000..69ec8fa --- /dev/null +++ b/aufgabengenerator_formulas_gem_fraction_new.py @@ -0,0 +1,252 @@ +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.")