Neue Generierung von moodle-formulas Fragen schon weit entwickelt. Dynamischer Aufgabenaufbau.
This commit is contained in:
parent
dd5e8c7286
commit
8f0c9ab93c
@ -1,19 +1,23 @@
|
|||||||
import random
|
import random
|
||||||
|
import operator
|
||||||
|
import re
|
||||||
from math import gcd, lcm
|
from math import gcd, lcm
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
|
# Define operators and their corresponding functions
|
||||||
|
ops = {
|
||||||
|
'+': operator.add,
|
||||||
|
'-': operator.sub,
|
||||||
|
'*': operator.mul,
|
||||||
|
'/': operator.truediv
|
||||||
|
}
|
||||||
|
|
||||||
# Konstanten
|
# Konstanten
|
||||||
# Anzahl der zu generierenden Fragen.
|
# Anzahl der zu generierenden Fragen.
|
||||||
NUM_QUESTIONS = 1
|
NUM_QUESTIONS = 1
|
||||||
# Algebraische Struktur der Aufgaben
|
# Algebraische Struktur der Aufgaben
|
||||||
STRUCTURE = "-(+)"
|
STRUCTURE = "-(+)-"
|
||||||
|
|
||||||
# TODO
|
|
||||||
# Platzhalter hinzufügen
|
|
||||||
|
|
||||||
# Anzahl der benötigten Brüche
|
|
||||||
NUM_FRACTIONS = 1 + STRUCTURE.count("+") + STRUCTURE.count("+") + STRUCTURE.count("*") + STRUCTURE.count("/")
|
|
||||||
# Größter Zähler
|
# Größter Zähler
|
||||||
MAX_NUMERATOR = 20
|
MAX_NUMERATOR = 20
|
||||||
# Größter Nenner
|
# Größter Nenner
|
||||||
@ -23,11 +27,99 @@ MAX_COMMON_DENOMINATOR = 100
|
|||||||
# Dateiname für Ausgabe
|
# Dateiname für Ausgabe
|
||||||
FILENAME = 'formulas_new_quiz.xml'
|
FILENAME = 'formulas_new_quiz.xml'
|
||||||
|
|
||||||
|
def parse_expression(expression):
|
||||||
|
"""Parse an algebraic expression and determine the required number of numbers"""
|
||||||
|
op_count = 0
|
||||||
|
|
||||||
|
# Count operators in parentheses
|
||||||
|
paren_count = 1
|
||||||
|
max_depth = 0
|
||||||
|
|
||||||
|
for i, char in enumerate(expression):
|
||||||
|
if char == '(':
|
||||||
|
paren_count += 1
|
||||||
|
elif char == ')':
|
||||||
|
paren_count -= 1
|
||||||
|
|
||||||
|
# Update max depth when a closed parenthesis is encountered
|
||||||
|
max_depth = max(max_depth, paren_count)
|
||||||
|
|
||||||
|
if char in ops.keys():
|
||||||
|
op_count += 1
|
||||||
|
|
||||||
|
return max_depth + op_count
|
||||||
|
|
||||||
|
# Anzahl der benötigten Brüche
|
||||||
|
NUM_FRACTIONS = parse_expression(STRUCTURE)
|
||||||
|
|
||||||
|
|
||||||
|
# Platzhalter hinzufügen
|
||||||
|
def add_placeholders(expression):
|
||||||
|
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 output[0] != f'n{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()
|
||||||
|
return final_expression
|
||||||
|
|
||||||
|
|
||||||
def create_formula_question(n):
|
# Algebraische Struktur mit Platzhaltern
|
||||||
|
STRUCTURE_PH = add_placeholders(STRUCTURE)
|
||||||
|
|
||||||
|
def calculate(expression, numbers):
|
||||||
|
"""Calculate the result of the expression using n[0], n[1], ..."""
|
||||||
|
# numbers = [random.randint(1, 10) for _ in range(num)]
|
||||||
|
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 create_formula_question(ex_number: int):
|
||||||
numerators = []
|
numerators = []
|
||||||
denominators = []
|
denominators = []
|
||||||
for i in range(NUM_FRACTIONS):
|
for i in range(NUM_FRACTIONS):
|
||||||
@ -46,8 +138,11 @@ def create_formula_question(n):
|
|||||||
print(numerators)
|
print(numerators)
|
||||||
print(denominators)
|
print(denominators)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
|
|
||||||
|
# Hauptnenner bestimmen
|
||||||
*denoms_integers, = denominators
|
*denoms_integers, = denominators
|
||||||
common_denominator = lcm(*denoms_integers)
|
common_denominator = lcm(*denoms_integers)
|
||||||
|
cd = common_denominator
|
||||||
|
|
||||||
fractions = []
|
fractions = []
|
||||||
for i in range(NUM_FRACTIONS):
|
for i in range(NUM_FRACTIONS):
|
||||||
@ -57,7 +152,6 @@ def create_formula_question(n):
|
|||||||
print(fractions)
|
print(fractions)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
|
|
||||||
cd = common_denominator
|
|
||||||
expanded_numerators = [frac["n"] * cd // frac["d"] for frac in fractions]
|
expanded_numerators = [frac["n"] * cd // frac["d"] for frac in fractions]
|
||||||
|
|
||||||
expanded_fractions = []
|
expanded_fractions = []
|
||||||
@ -68,25 +162,127 @@ def create_formula_question(n):
|
|||||||
print(expanded_fractions)
|
print(expanded_fractions)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
|
|
||||||
eval_string = f"{expanded_numerators[0]}"
|
result_numerator = calculate(STRUCTURE_PH, expanded_numerators)
|
||||||
cnt_next_fraction = 1
|
|
||||||
for c in STRUCTURE:
|
|
||||||
if c == "(" or c ==")":
|
|
||||||
eval_string += c
|
|
||||||
elif c == "+" or c == "-" or c == "*" or c == "/":
|
|
||||||
eval_string += c
|
|
||||||
eval_string += f"{expanded_numerators[cnt_next_fraction]}"
|
|
||||||
cnt_next_fraction += 1
|
|
||||||
|
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
print(eval_string)
|
print(result_numerator)
|
||||||
# [DEBUG]
|
# [DEBUG]
|
||||||
result_numerator = eval(eval_string)
|
|
||||||
|
|
||||||
|
gcd_result = gcd(result_numerator, cd)
|
||||||
|
if gcd_result > 1:
|
||||||
|
# print("kürzbar")
|
||||||
|
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'})
|
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"
|
||||||
|
exercise_text = "Berechne den Term"
|
||||||
|
elif has_plus:
|
||||||
|
expression_type = "Addition"
|
||||||
|
exercise_text = "Addiere die Brüche"
|
||||||
|
elif has_minus:
|
||||||
|
expression_type = "Subtraktion"
|
||||||
|
exercise_text = "Subtrahiere die 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} 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"<![CDATA[ <p>{exercise_text} und kürze das Ergebnis so weit wie möglich.</p>" + "<p>{#A} </p> ]]>"
|
||||||
|
else:
|
||||||
|
text_elem.text = f"<![CDATA[ <p>{exercise_text}.</p>"+"<p>{#A} </p> ]]>"
|
||||||
|
|
||||||
|
# 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 = '''<![CDATA[ <p>Die Antwort ist richtig.</p> ]]> '''
|
||||||
|
|
||||||
|
partiallycorrectfeedback_elem = ET.SubElement(question, 'partiallycorrectfeedback', attrib={'format': 'html'})
|
||||||
|
text_elem = ET.SubElement(partiallycorrectfeedback_elem, 'text')
|
||||||
|
text_elem.text = '''<![CDATA[ <p>Die Antwort ist teilweise richtig.</p> ]]> '''
|
||||||
|
|
||||||
|
incorrectfeedback_elem = ET.SubElement(question, 'incorrectfeedback', attrib={'format': 'html'})
|
||||||
|
text_elem = ET.SubElement(incorrectfeedback_elem, 'text')
|
||||||
|
text_elem.text = '''<![CDATA[ <p>Die Antwort ist falsch.</p> ]]>'''
|
||||||
|
|
||||||
|
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'
|
||||||
return question
|
return question
|
||||||
|
|
||||||
|
# Berechnungsvariablen
|
||||||
|
vars1_elem = ET.SubElement(answers_elem, 'vars1')
|
||||||
|
if shortable:
|
||||||
|
vars1_elem_text = "<![CDATA[ g = gcd(d[0],d[1]);\n cd = abs(d[0] * d[1]) / g;\n e = [cd / d[0], cd / d[1]];\n nums = [n[0]*e[0], n[1]*e[1]];\n thesumnum = nums[0] - nums[1];\n gcdres = gcd(thesumnum,cd);\n snum = thesumnum / gcdres;\n sdenom = cd / gcdres;]]>"
|
||||||
|
else:
|
||||||
|
vars1_elem_text = "<![CDATA[ g = gcd(d[0],d[1]);\ncd = abs(d[0] * d[1]) / g;\ne = [cd / d[0], cd / d[1]];\nnums = [n[0]*e[0], n[1]*e[1]];\nthesumnum = nums[0] - nums[1];]]>"
|
||||||
|
ET.SubElement(vars1_elem, 'text').text = vars1_elem_text
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_questions(num_questions):
|
def generate_questions(num_questions):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user