Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
- allow student to flag inappropriate explanation
  • Loading branch information
kitsook committed Sep 10, 2018
1 parent 371afad commit f744a51
Show file tree
Hide file tree
Showing 23 changed files with 1,560 additions and 54 deletions.
40 changes: 33 additions & 7 deletions ubcpi/answer_pool.py
Expand Up @@ -13,6 +13,8 @@
# items in the pool. For variable length item, specify the max length
POOL_ITEM_LENGTH_SIMPLE = POOL_ITEM_LENGTH_RANDOM = 35

# dummy id to identify a seed explanation
SEED_EXPLANATION_ID = "-1"

class UnknownChooseAnswerAlgorithm(Exception):
pass
Expand Down Expand Up @@ -173,7 +175,7 @@ def validate_seeded_answers(answers, options, algo):
raise UnknownChooseAnswerAlgorithm()


def get_other_answers(pool, seeded_answers, get_student_item_dict, algo, options):
def get_other_answers(pool, seeded_answers, get_student_item_dict, algo, options, inappropriate_answers):
"""
Select other student's answers from answer pool or seeded answers based on the selection algorithm
Expand All @@ -195,6 +197,12 @@ def get_other_answers(pool, seeded_answers, get_student_item_dict, algo, options
get_student_item_dict (callable): get student item dict function to return student item dict
algo (str): selection algorithm
options (dict): answer options for the question
inappropriate_answers (set): inappropriate answers that shouldn't be showing. Format:
{
submission_uuid_1, # Submission UUID of inappropriate answer
submission_uuid_2,
...
}
Returns:
dict: answers based on the selection algorithm
Expand All @@ -204,15 +212,24 @@ def get_other_answers(pool, seeded_answers, get_student_item_dict, algo, options
if 'num_responses' not in algo or algo['num_responses'] == "#" \
else int(algo['num_responses'])

# clean up the pool
for option in pool.keys():
for student in pool[option].keys():
student_item = get_student_item_dict(student)
submission = sas_api.get_answers_for_student(student_item)
# remove deleted submission or new submission that doesn't match recorded option
if not submission.has_revision(0) or (submission.has_revision(0) and submission.get_vote(0) != option):
del pool[option][student]

if algo['name'] == 'simple':
return get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_responses)
return get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_responses, inappropriate_answers)
elif algo['name'] == 'random':
return get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_responses)
return get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_responses, inappropriate_answers)
else:
raise UnknownChooseAnswerAlgorithm()


def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_responses):
def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_responses, inappropriate_answers):
"""
Get answers from others with simple algorithm, which picks one answer for each option.
Expand Down Expand Up @@ -240,6 +257,7 @@ def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_re
while len(ret) < num_responses and merged_pool:
for option, students in merged_pool.items():
rationale = None
unique_id = SEED_EXPLANATION_ID
while students:
student = random.choice(students.keys())
# remove the chosen answer from pool
Expand All @@ -251,14 +269,17 @@ def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_re
else:
student_item = get_student_item_dict(student)
submission = sas_api.get_answers_for_student(student_item)
if submission.get_submission_uuid() in inappropriate_answers:
continue
# Make sure the answer is still the one we want.
# It may have changed (e.g. instructor deleted the student state
# and the student re-submitted a diff answer)
if submission.has_revision(0) and submission.get_vote(0) == option:
rationale = submission.get_rationale(0)
unique_id = submission.get_submission_uuid()

if rationale:
ret.append({'option': option, 'rationale': rationale})
ret.append({'option': option, 'rationale': rationale, 'id': unique_id})
break

if not students:
Expand All @@ -271,7 +292,7 @@ def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_re
return {"answers": ret}


def get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_responses):
def get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_responses, inappropriate_answers):
"""
Get answers from others with random algorithm, which randomly select answer from the pool.
Expand Down Expand Up @@ -307,18 +328,23 @@ def get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_re
# this is the student's answer so don't return
continue

unique_id = SEED_EXPLANATION_ID
if student.startswith('seeded'):
option = seeded[student]['answer']
rationale = seeded[student]['rationale']
else:
student_item = get_student_item_dict(student)
submission = sas_api.get_answers_for_student(student_item)
if submission.get_submission_uuid() in inappropriate_answers:
continue

if submission.has_revision(0):
rationale = submission.get_rationale(0)
option = submission.get_vote(0)
unique_id = submission.get_submission_uuid()
else:
continue
ret.append({'option': option, 'rationale': rationale})
ret.append({'option': option, 'rationale': rationale, 'id': unique_id})

return {"answers": ret}

Expand Down
15 changes: 13 additions & 2 deletions ubcpi/persistence.py
Expand Up @@ -16,6 +16,7 @@
}
"""

SUBMISSION_UUID = 'uuid'
ANSWER_LIST_KEY = 'answers'
DELETE_INDICATOR = 'deleted'
REQUEST_USER_ID_KEY = 'requesting_user_id'
Expand Down Expand Up @@ -43,7 +44,7 @@ def get_answers_for_student(student_item):
latest_answer_item = latest_submission.get('answer', {})
if latest_answer_item.get(DELETE_INDICATOR, False):
return Answers()
return Answers(latest_answer_item.get(ANSWER_LIST_KEY, []))
return Answers(latest_answer_item.get(ANSWER_LIST_KEY, []), latest_submission.get(SUBMISSION_UUID, None))


def add_answer_for_student(student_item, vote, rationale):
Expand Down Expand Up @@ -85,11 +86,12 @@ class Answers:
in the future if this xblock supports more than one round of revision.
"""

def __init__(self, answers=None):
def __init__(self, answers=None, uuid=None):
if not answers:
self.raw_answers = []
else:
self.raw_answers = answers
self.submission_uuid = uuid

def _safe_get(self, revision, key):
"""
Expand All @@ -109,6 +111,15 @@ def _safe_get(self, revision, key):
else:
return None

def get_submission_uuid(self):
"""
Get the UUID of the submission
Returns:
the uuid of the submission. None if there is no submission
"""
return self.submission_uuid

def has_revision(self, revision):
"""
Check if the answer has a revision
Expand Down
8 changes: 7 additions & 1 deletion ubcpi/serialize.py
Expand Up @@ -240,6 +240,8 @@ def parse_from_xml(root):
algo = unicode(root.attrib['algorithm']) if 'algorithm' in root.attrib else None
num_responses = unicode(root.attrib['num_responses']) if 'num_responses' in root.attrib else None

flag_inappropriate_threshold = int(root.attrib['flag_inappropriate_threshold']) if 'flag_inappropriate_threshold' in root.attrib else None

return {
'display_name': display_name,
'question_text': question,
Expand All @@ -248,7 +250,8 @@ def parse_from_xml(root):
'correct_answer': correct_answer,
'correct_rationale': correct_rationale,
'seeds': seeds,
'algo': {"name": algo, 'num_responses': num_responses}
'algo': {"name": algo, 'num_responses': num_responses},
'flag_inappropriate_threshold': flag_inappropriate_threshold
}


Expand Down Expand Up @@ -346,3 +349,6 @@ def serialize_to_xml(root, block):

seeds = etree.SubElement(root, 'seeds')
serialize_seeds(seeds, block)

if block.flag_inappropriate_threshold:
root.set('flag_inappropriate_threshold', unicode(block.flag_inappropriate_threshold))

0 comments on commit f744a51

Please sign in to comment.