mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
experimental type answer refactor
- when answer not correct, show both the given and correct string in separate markup. we use red/green for the given string to indicate what was correct, but we use grey rather than red on the correct string to indicate what was missing, as red is misleading - colours can now be customized in css with .typeGood, .typeBad and .typeMissing - answer now shown in monospace so given/correct lines up; can be customized with code#typeans - do away with 'correct answer was' text that people didn't like
This commit is contained in:
parent
f10b336fb9
commit
c3b0ae37aa
1 changed files with 55 additions and 60 deletions
115
aqt/reviewer.py
115
aqt/reviewer.py
|
@ -320,6 +320,9 @@ body { margin:1.5em; }
|
|||
img { max-width: 95%; max-height: 95%; }
|
||||
.marked { position:absolute; right: 7px; top: 7px; display: none; }
|
||||
#typeans { width: 100%; }
|
||||
.typeGood { background: #0f0; }
|
||||
.typeBad { background: #f00; }
|
||||
.typeMissed { background: #ccc; }
|
||||
"""
|
||||
|
||||
def _styles(self):
|
||||
|
@ -328,8 +331,6 @@ img { max-width: 95%; max-height: 95%; }
|
|||
# Type in the answer
|
||||
##########################################################################
|
||||
|
||||
failedCharColour = "#FF0000"
|
||||
passedCharColour = "#00FF00"
|
||||
typeAnsPat = "\[\[type:(.+?)\]\]"
|
||||
|
||||
def typeAnsFilter(self, buf):
|
||||
|
@ -390,11 +391,7 @@ Please run Tools>Empty Cards""")
|
|||
cor = parser.unescape(cor)
|
||||
given = self.typedAnswer
|
||||
# compare with typed answer
|
||||
res = self.correct(cor, given)
|
||||
if cor != given:
|
||||
# Wrap the extra text in an id-ed span.
|
||||
res += u"<span id=rightanswer><br> {0} <br> {1} </span>".format(
|
||||
_(u"Correct answer was:"), cor)
|
||||
res = self.correct(given, cor, showBad=False)
|
||||
# and update the type answer area
|
||||
def repl(match):
|
||||
# can't pass a string in directly, and can't use re.escape as it
|
||||
|
@ -419,62 +416,60 @@ Please run Tools>Empty Cards""")
|
|||
txt = matches[0]
|
||||
return txt
|
||||
|
||||
# following type answer functions thanks to Bernhard
|
||||
def calculateOkBadStyle(self):
|
||||
"Precalculates styles for correct and incorrect part of answer"
|
||||
st = "background: %s; color: #000;"
|
||||
self.styleOk = st % self.passedCharColour
|
||||
self.styleBad = st % self.failedCharColour
|
||||
def tokenizeComparison(self, given, correct):
|
||||
s = difflib.SequenceMatcher(None, given, correct)
|
||||
givenElems = []
|
||||
correctElems = []
|
||||
givenPoint = 0
|
||||
correctPoint = 0
|
||||
offby = 0
|
||||
def logBad(old, new, str, array):
|
||||
if old != new:
|
||||
array.append((False, str[old:new]))
|
||||
def logGood(start, cnt, str, array):
|
||||
if cnt:
|
||||
array.append((True, str[start:start+cnt]))
|
||||
for x, y, cnt in s.get_matching_blocks():
|
||||
# if anything was missed in correct, pad given
|
||||
if cnt and y-offby > x:
|
||||
givenElems.append((False, "-"*(y-x-offby)))
|
||||
offby = y-x
|
||||
# log any proceeding bad elems
|
||||
logBad(givenPoint, x, given, givenElems)
|
||||
logBad(correctPoint, y, correct, correctElems)
|
||||
givenPoint = x+cnt
|
||||
correctPoint = y+cnt
|
||||
# log the match
|
||||
logGood(x, cnt, given, givenElems)
|
||||
logGood(y, cnt, correct, correctElems)
|
||||
return givenElems, correctElems
|
||||
|
||||
def ok(self, a):
|
||||
"returns given sring in style correct (green)"
|
||||
if len(a) == 0:
|
||||
return ""
|
||||
return "<span style='%s'>%s</span>" % (self.styleOk, cgi.escape(a))
|
||||
|
||||
def bad(self, a):
|
||||
"returns given sring in style incorrect (red)"
|
||||
if len(a) == 0:
|
||||
return ""
|
||||
return "<span style='%s'>%s</span>" % (self.styleBad, cgi.escape(a))
|
||||
|
||||
def applyStyle(self, testChar, correct, wrong):
|
||||
"Calculates answer fragment depending on testChar's unicode category"
|
||||
ZERO_SIZE = 'Mn'
|
||||
def head(a):
|
||||
return a[:len(a) - 1]
|
||||
def tail(a):
|
||||
return a[len(a) - 1:]
|
||||
if ucd.category(testChar) == ZERO_SIZE:
|
||||
return self.ok(head(correct)) + self.bad(tail(correct) + wrong)
|
||||
return self.ok(correct) + self.bad(wrong)
|
||||
|
||||
def correct(self, a, b):
|
||||
def correct(self, given, correct, showBad=True):
|
||||
"Diff-corrects the typed-in answer."
|
||||
if b == "":
|
||||
return "";
|
||||
self.calculateOkBadStyle()
|
||||
ret = ""
|
||||
lastEqual = ""
|
||||
s = difflib.SequenceMatcher(None, b, a)
|
||||
for tag, i1, i2, j1, j2 in s.get_opcodes():
|
||||
if tag == "equal":
|
||||
lastEqual = b[i1:i2]
|
||||
elif tag == "replace":
|
||||
ret += self.applyStyle(b[i1], lastEqual,
|
||||
b[i1:i2] + ("-" * ((j2 - j1) - (i2 - i1))))
|
||||
lastEqual = ""
|
||||
elif tag == "delete":
|
||||
ret += self.applyStyle(b[i1], lastEqual, b[i1:i2])
|
||||
lastEqual = ""
|
||||
elif tag == "insert":
|
||||
if ucd.category(a[j1]) != 'Mn':
|
||||
dashNum = (j2 - j1)
|
||||
givenElems, correctElems = self.tokenizeComparison(given, correct)
|
||||
def good(s):
|
||||
return "<span class=typeGood>"+s+"</span>"
|
||||
def bad(s):
|
||||
return "<span class=typeBad>"+s+"</span>"
|
||||
def missed(s):
|
||||
return "<span class=typeMissed>"+s+"</span>"
|
||||
if given == correct:
|
||||
res = good(given)
|
||||
else:
|
||||
dashNum = ((j2 - j1) - 1)
|
||||
ret += self.applyStyle(a[j1], lastEqual, "-" * dashNum)
|
||||
lastEqual = ""
|
||||
return ret + self.ok(lastEqual)
|
||||
res = ""
|
||||
for ok, txt in givenElems:
|
||||
if ok:
|
||||
res += good(txt)
|
||||
else:
|
||||
res += bad(txt)
|
||||
res += "<br>↓<br>"
|
||||
for ok, txt in correctElems:
|
||||
if ok:
|
||||
res += good(txt)
|
||||
else:
|
||||
res += missed(txt)
|
||||
res = "<div><code id=typeans>" + res + "</code></div>"
|
||||
return res
|
||||
|
||||
# Bottom bar
|
||||
##########################################################################
|
||||
|
|
Loading…
Reference in a new issue