Commit 7ab13256 by Alexander Kryklia

extended grader

parent 744b7947
...@@ -28,9 +28,88 @@ values are (x,y) coordinates of centers of dragged images. ...@@ -28,9 +28,88 @@ values are (x,y) coordinates of centers of dragged images.
""" """
import json import json
from collections import OrderedDict
def grade(user_input, correct_answer): class PositionsCompare(list):
"""Inputs are: "abc" - target
[10, 20] - list of integers
[[10,20], 200] list of list and integer
"""
def __eq__(self, other):
# Default lists behaviour is convers "abc" to ["a", "b", "c"].
# We will use that.
# import ipdb; ipdb.set_trace()
#check if self or other is not empty list (empty lists = false)
if not self or not other:
return False
# check correct input types
if (not isinstance(self[0], (str, unicode, list, int)) or
not isinstance(other[0], (str, unicode, list, int))):
print 'Incorrect input type'
return False
if (isinstance(self[0], (list, int)) and
isinstance(other[0], (list, int))):
print 'Numerical position compare'
return self.coordinate_positions_compare(other)
elif (isinstance(self[0], (unicode, str)) and
isinstance(other[0], (unicode, str))):
print 'Targets compare'
return ''.join(self) == ''.join(other)
else:
# we do not have ints or lists of lists or two string/unicode lists
# on both sides
print type(self[0]), type(other[0]), "not correct"
return False
def __ne__(self, other):
return not self.__eq__(other)
def coordinate_positions_compare(self, other, r=10):
""" Checks if pos1 is equal to pos2 inside radius
of forgiveness (default 10 px).
Args:
self, other: [x, y] or [[x, y], r], where
r is radius of forgiveness;
x, y, r: int
Returns: bool.
"""
print 'I am called', self, other
# get max radius of forgiveness
if isinstance(self[0], list): # [(x, y), r] case
r = max(self[1], r)
x1, y1 = self[0]
else:
x1, y1 = self
if isinstance(other[0], list): # [(x, y), r] case
r = max(other[1], r)
x2, y2 = other[0]
else:
x2, y2 = other
if (x2 - x1) ** 2 + (y2 - y1) ** 2 > r * r:
return False
return True
class DragAndDrop(object):
def __init__(self):
self.correct_groups = OrderedDict() # groups
self.correct_positions = OrderedDict() # positions of comparing
self.user_groups = OrderedDict()
self.user_positions = OrderedDict()
def grade(self):
''' '''
Grade drag and drop problem. Grade drag and drop problem.
If use_targets is True - checks if image placed on proper target. If use_targets is True - checks if image placed on proper target.
...@@ -51,51 +130,108 @@ def grade(user_input, correct_answer): ...@@ -51,51 +130,108 @@ def grade(user_input, correct_answer):
Returns: Returns:
True or False. True or False.
''' '''
if sorted(self.correct_groups.keys()) != sorted(self.user_groups.keys()):
return False
user_answer = json.loads(user_input) for groupname, draggable_ids in self.correct_groups.items():
if sorted(draggable_ids) != sorted(self.user_groups[groupname]):
if len(correct_answer.keys()) != len(user_answer['draggables']):
return False return False
def is_equal(user_answer, correct_answer): # from now self.groups and self.user_groups are equal
""" Checks if user_answer is equal to correct_answer inside radius assert self.correct_groups == self.user_groups
of forgiveness (default 10 px).
Args: # Check fo every group that positions of every group element are equal
user_answer: [x, y] - list of floats; # with positions
correct_answer: [x, y] or [[x, y], r], where
r is radius of forgiveness;
Returns: bool. # 'denied' rule
""" # import ipdb; ipdb.set_trace()
if not isinstance(correct_answer, list) or isinstance(user_answer, list): denied_positions = [self.correct_positions[g].get('denied', [])
for g in self.correct_groups.keys()]
all_user_positions = [self.user_positions[g]['user']
for g in self.correct_groups.keys()]
if not self.compare_positions(denied_positions,
all_user_positions, flag='denied'):
return False return False
r = 10 no_exact, no_allowed = False, False
if isinstance(correct_answer[0], list): # [(x, y), r] case # 'exact' rule
r = correct_answer[1] for groupname in self.correct_groups:
corr_x = correct_answer[0][0] if self.correct_positions[groupname].get('exact', []):
corr_y = correct_answer[0][1] if not self.compare_positions(
else: # (x, y) case self.correct_positions[groupname]['exact'],
corr_x = correct_answer[0] self.user_positions[groupname]['user'], flag='exact'):
corr_y = correct_answer[1] return False
else:
if ((user_answer[0] - corr_x) ** 2 + no_exact = True
(user_answer[1] - corr_y) ** 2) > r * r:
# 'allowed' rule
for groupname in self.correct_groups:
if self.correct_positions[groupname].get('allowed', []):
if not self.compare_positions(
self.correct_positions[groupname]['allowed'],
self.user_positions[groupname]['user'], flag='allowed'):
return False
else:
no_allowed = True
if no_allowed and no_exact:
return False return False
return True return True
if user_answer["use_targets"]: def compare_positions(self, list1, list2, flag):
is_equal = lambda user, correct: user == correct if ( # import ipdb; ipdb.set_trace()
isinstance(user, unicode) and isinstance(correct, str)) else False if flag == 'denied':
for el1 in list1:
for el2 in list2:
if PositionsCompare(el1) == PositionsCompare(el2):
return False
for draggable in user_answer['draggables']: if flag == 'allowed':
user_img_location = draggable.values()[0] for el1, el2 in zip(sorted(list1), sorted(list2)):
corr_img_location = correct_answer.get(draggable.keys()[0], None) if PositionsCompare(el1) != PositionsCompare(el2):
if not corr_img_location:
return False return False
if not is_equal(user_img_location, corr_img_location):
if flag == 'exact':
for el1, el2 in zip(list1, list2):
if PositionsCompare(el1) != PositionsCompare(el2):
return False return False
return True return True
def populate(self, correct_answer, user_answer):
""" """
if isinstance(correct_answer, dict):
for key, value in correct_answer.items():
self.correct_groups[key] = [key]
self.correct_positions[key] = {'exact': [value]}
user_answer = json.loads(user_answer)
self.use_targets = user_answer.get('use_targets')
# create identical data structures
# user groups must mirror correct_groups
# and positions must reflect order in groups
for groupname in self.correct_groups:
self.user_groups[groupname] = []
self.user_positions[groupname] = {'user': []}
for draggable_dict in user_answer['draggables']:
# draggable_dict is 1-to-1 {draggable_name: position}
draggable_name = draggable_dict.keys()[0]
if draggable_name in self.correct_groups[groupname]:
self.user_groups[groupname].append(draggable_name)
self.user_positions[groupname]['user'].append(
draggable_dict[draggable_name])
# import ipdb; ipdb.set_trace()
def grade(user_input, correct_answer):
""" Support 2 interfaces"""
if isinstance(correct_answer, dict):
dnd = DragAndDrop()
dnd.populate(correct_answer=correct_answer, user_answer=user_input)
return dnd.grade()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment