from .Variable import Variable
|
import re
|
from . import Exceptions
|
from math import *
|
from random import shuffle
|
|
|
class Problem:
|
def __init__(
|
self,
|
source=None,
|
shuffle=True,
|
MaxShuffleAttempts=40,
|
MaxRegenerateAttempts=100,
|
AnsDiff=0.05,
|
):
|
self.source = source
|
self.MaxShuffleAttempts = MaxShuffleAttempts
|
self.MaxRegenerateAttempts = MaxRegenerateAttempts
|
self.AnsDiff = AnsDiff
|
self.source.generateVariables()
|
self.source.generateSolutions()
|
self.problem = self.source.generateProblem()
|
if shuffle:
|
for i in range(0, self.MaxRegenerateAttempts):
|
try:
|
self.problem["solutions"] = self.shuffleAnswers(
|
self.problem["solutions"]
|
)
|
except Exceptions.AnswerProximityError:
|
self.regenerate()
|
continue
|
else:
|
break
|
if i == self.MaxRegenerateAttempts - 1:
|
raise Exceptions.AnswerProximityError(
|
"Could not shuffle answers and get answers that are not too close together :: "
|
+ self.problem["introduction"]
|
)
|
|
def regenerate(self):
|
self.source.generateVariables()
|
self.source.generateSolutions()
|
self.problem = self.source.generateProblem()
|
|
def shuffleAnswers(self, solList):
|
shuffList = []
|
for cont in solList:
|
for i in range(0, self.MaxShuffleAttempts):
|
wrong = cont["wrong"]
|
shuffle(wrong)
|
ans = [
|
*zip(cont["correct"], list("1" * len(cont["correct"]))),
|
*zip(wrong, list("0" * len(wrong))),
|
]
|
ans = ans[0:4]
|
if self.checkAnsProximity(ans) == False:
|
break
|
if i == self.MaxShuffleAttempts - 1:
|
raise Exceptions.AnswerProximityError(
|
"Could not shuffle answers and get answers that are not too close together"
|
)
|
shuffle(ans)
|
cont["shuffled"] = ans
|
shuffList.append(cont)
|
return solList
|
|
def checkAnsProximity(self, ans):
|
for idx1, (val1, cor) in enumerate(ans):
|
for idx2, (val2, cor) in enumerate(ans):
|
|
if idx1 == idx2:
|
continue
|
|
if (val1.get_formatted_value()==val2.get_formatted_value()):
|
return True
|
if not val1.is_float() or not val2.is_float():
|
if val1.get_formatted_value() == val2.get_formatted_value():
|
return True
|
else:
|
if (
|
abs(val1.get_formatted_value() - val2.get_formatted_value())
|
< self.AnsDiff * val1.get_formatted_value()
|
):
|
return True
|
return False
|
|
|
class ProblemSource:
|
def __init__(self, parser=None):
|
self.introduction = None
|
self.subproblems = None
|
self.picture= None
|
self.parsedSolutions = None
|
self.parsedVariables = None
|
self.variableGenerator = None
|
self.varDict = {}
|
if parser is not None:
|
self.introduction, self.picture, self.subproblems, self.parsedVariables, self.parsedSolutions, self.variableGenerator = (
|
parser.get_parsed_sections()
|
)
|
# self.generateVariables()
|
|
def generateVariables(self):
|
for key in self.parsedVariables:
|
self.varDict[key] = Variable(
|
next(self.variableGenerator[key]), self.parsedVariables[key]["type"]
|
)
|
|
def generateSolutions(self):
|
# a dirty one but it has to be like this ;)
|
__retsol = []
|
|
# define variables
|
for __varname, __var in self.varDict.items():
|
exec(__varname + "=" + str(__var.get_formatted_value()))
|
for __s in self.parsedSolutions:
|
__ps = {}
|
__ps["correct"] = []
|
__ps["wrong"] = []
|
for __corr in __s["correct"]:
|
for __corrsplit in __corr.split(";"):
|
__result = None
|
if __corrsplit.find("=") >= 0:
|
try:
|
exec(self.substitute_octave(__corrsplit))
|
except:
|
print("Error while evaluating {}".format(__corrsplit))
|
else:
|
__result = eval(self.substitute_octave(__corrsplit))
|
if __result is None:
|
raise Exceptions.NoResult(
|
"Result cannot be calculated. Be sure to specify last term without = sign!"
|
)
|
__ps["correct"].append(Variable(__result, formatting=__s["type"]))
|
for __corr in __s["wrong"]:
|
for __corrsplit in __corr.split(";"):
|
__result = None
|
if __corrsplit.find("=") >= 0:
|
exec(self.substitute_octave(__corrsplit))
|
else:
|
__result = eval(self.substitute_octave(__corrsplit))
|
if __result is None:
|
raise Exceptions.NoResult(
|
"Result cannot be calculated. Be sure to specify last term without = sign!"
|
)
|
__ps["wrong"].append(Variable(__result, formatting=__s["type"]))
|
__ps["glyph"] = __s["glyph"]
|
__ps["unit"] = __s["unit"]
|
__retsol.append(__ps)
|
return __retsol
|
|
def isMultiProblem(self):
|
if len(self.subproblems) > 0:
|
return True
|
else:
|
return False
|
|
def substitute_variables(self, text):
|
for key, var in self.varDict.items():
|
text = re.sub("\/\*\/" + key + "\/\*\/", var.format_as_tex(dollar=""), text)
|
return text
|
|
def substitute_octave(self, text):
|
text = re.sub("\^", "**", text)
|
text = re.sub(";", "\n", text)
|
return text
|
|
def generateProblem(self):
|
intro = self.substitute_variables(self.introduction)
|
sp = []
|
for p in self.subproblems:
|
sp.append(self.substitute_variables(p))
|
sol = self.generateSolutions()
|
return {"introduction": intro, "subproblems": sp, "picture": self.picture, "solutions": sol}
|