Skip to content

Commit

Permalink
Delete learner's state in staff debug
Browse files Browse the repository at this point in the history
WIP
- implement the callback clear_student_state
- add a new submission to indicate existing answers are deleted
- add (but commented out) code to partially support revising score
manually.  But full support will require adding logic to persist the
score within xblock
- fix issue with same explanation displayed multiple times in Step 2
  • Loading branch information
kitsook committed Jun 15, 2018
1 parent 38dc217 commit 51d4a75
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 37 deletions.
71 changes: 36 additions & 35 deletions ubcpi/answer_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,48 +227,46 @@ def get_other_answers_simple(pool, seeded_answers, get_student_item_dict, num_re
ret = []
# clean up answers so that all keys are int
pool = {int(k): v for k, v in pool.items()}
total_in_pool = len(seeded_answers)
merged_pool = convert_seeded_answers(seeded_answers)
student_id = get_student_item_dict()['student_id']
# merge the dictionaries in the answer dictionary
for key in pool:
total_in_pool += len(pool[key])
# if student_id has value, we assume the student just submitted an answer. So removing it
# from total number in the pool
if student_id in pool[key].keys():
total_in_pool -= 1
if key in merged_pool:
merged_pool[key].update(pool[key].items())
else:
merged_pool[key] = pool[key]

# remember which option+student_id is selected, so that we don't have duplicates in the result
selected = []
merged_pool.setdefault(key, {})
merged_pool[key].update(pool[key].items())
# Pop student's own answer, if exists
merged_pool[key].pop(student_id, None)

# loop until we have enough answers to return
while len(ret) < min(num_responses, total_in_pool):
# loop until we have enough answers to return or when there is nothing more to return
while len(ret) < num_responses and merged_pool:
for option, students in merged_pool.items():
student = student_id
i = 0
while (student == student_id or i > 100) and (str(option) + student) not in selected:
# retry until we got a different one or after 100 retries
# we are suppose to get a different student answer or a seeded one in a few tries
# as we have at least one seeded answer for each option in the algo. And it is not
# suppose to overflow i order to break the loop
rationale = None
while students:
student = random.choice(students.keys())
i += 1
selected.append(str(option)+student)
if student.startswith('seeded'):
# seeded answer, get the rationale from local
rationale = students[student]
else:
student_item = get_student_item_dict(student)
submission = sas_api.get_answers_for_student(student_item)
rationale = submission.get_rationale(0)
ret.append({'option': option, 'rationale': rationale})
content = students[student]
# remove the choosen answer from pool
students.pop(student, None)

if student.startswith('seeded'):
# seeded answer, get the rationale from local
rationale = content
else:
student_item = get_student_item_dict(student)
submission = sas_api.get_answers_for_student(student_item)
# Make sure the option 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)

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

if not students:
merged_pool.pop(option, None)

# check if we have enough answers
if len(ret) >= min(num_responses, total_in_pool):
if len(ret) >= num_responses:
break

return {"answers": ret}
Expand Down Expand Up @@ -316,8 +314,11 @@ def get_other_answers_random(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)
rationale = submission.get_rationale(0)
option = submission.get_vote(0)
if submission.has_revision(0):
rationale = submission.get_rationale(0)
option = submission.get_vote(0)
else:
continue
ret.append({'option': option, 'rationale': rationale})

return {"answers": ret}
Expand Down
17 changes: 17 additions & 0 deletions ubcpi/persistence.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"""

ANSWER_LIST_KEY = 'answers'
DELETE_INDICATOR = 'deleted'
REQUEST_USER_ID_KEY = 'requesting_user_id'

VOTE_KEY = 'vote'
RATIONALE_KEY = 'rationale'
Expand All @@ -39,6 +41,8 @@ def get_answers_for_student(student_item):

latest_submission = submissions[0]
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, []))


Expand All @@ -59,6 +63,19 @@ def add_answer_for_student(student_item, vote, rationale):
ANSWER_LIST_KEY: answers.get_answers_as_list()
})

def delete_answer_for_student(student_item, requesting_user_id):
"""
Create a new submission to indicate student's answer is deleted
Args:
student_item (dict): The location of the problem this submission is
associated with, as defined by a course, student, and item.
requesting_user_id: The user that is requesting to delete student answer
"""
sub_api.create_submission(student_item, {
DELETE_INDICATOR: True,
REQUEST_USER_ID_KEY: requesting_user_id,
})

class Answers:
"""
Expand Down
55 changes: 53 additions & 2 deletions ubcpi/ubcpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from webob import Response
from xblock.core import XBlock
from xblock.exceptions import JsonHandlerError
# For supporting manual revision of scores. Commented out for now.
# from xblock.scorable import ScorableXBlockMixin, Score
from xblock.fields import Scope, String, List, Dict, Integer, DateTime, Float
from xblock.fragment import Fragment
from xblockutils.publish_event import PublishEventMixin
Expand Down Expand Up @@ -256,8 +258,6 @@ class PeerInstructionXBlock(XBlock, MissingDataFetcherMixin, PublishEventMixin):
help=_("The algorithm for selecting which answers to be presented to students"),
)

# Declare that we are not part of the grading System. Disabled for now as for the concern about the loading
# speed of the progress page.
has_score = True

start = DateTime(
Expand Down Expand Up @@ -293,6 +293,57 @@ def max_score(self):
"""
return 1

# def calculate_score(self):
# answers = self.get_answers_for_student()
# if answers.has_revision(0) and answers.has_revision(1):
# return Score(1, 1)
# return Score(0, 1)
#
# def set_score(self, score):
# # TODO persisting score
# pass
#
# def get_score(self):
# # TODO Since we are not persisting score, always return 1.
# # That means the Overriding Score function will always set the score to 1
# # Instructors can reset the score to 0 by deleting learner's state
# return Score(1, 1)

def clear_student_state(self, user_id, course_id, item_id, requesting_user_id):
"""
Being notified that student state is going to be deleted. Mark student's
submissions as deleted
"""
student_item = dict(
student_id=user_id,
item_id=item_id,
course_id=course_id,
item_type='ubcpi'
)

# TODO currently not possible to revise the stats as they are defined with scope Scope.user_state_summary.
# The stats are not available when clear_student_state is called
# answers = sas_api.get_answers_for_student(student_item)
# stats = self.get_current_stats()
# if answers.has_revision(0):
# num_resp = stats['original'].setdefault(answers.get_vote(0), 0)
# if num_resp > 0:
# stats['original'][answers.get_vote(0)] = num_resp - 1
# if answers.has_revision(1):
# num_resp = stats['revised'].setdefault(answers.get_vote(1), 0)
# if num_resp > 0:
# stats['revised'][answers.get_vote(1)] = num_resp - 1

# mark existing submission as deleted
sas_api.delete_answer_for_student(student_item, requesting_user_id)

# def has_submitted_answer(self):
# answers = self.get_answers_for_student()
# return answers.has_revision(0) and answers.has_revision(1)
#
# def publish_grade(self):
# self._publish_grade(self.get_score())

def studio_view(self, context=None):
"""
view function for studio edit
Expand Down

0 comments on commit 51d4a75

Please sign in to comment.