Skip to content

Commit

Permalink
Allow students to fresh rationales shown
Browse files Browse the repository at this point in the history
- on step 2, display a link to allow students to refresh the answers
shown
  • Loading branch information
kitsook committed Nov 26, 2019
1 parent f80be56 commit 32ba633
Show file tree
Hide file tree
Showing 12 changed files with 564 additions and 20 deletions.
137 changes: 137 additions & 0 deletions ubcpi/answer_pool.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
import copy
import persistence as sas_api
from utils import _ # pylint: disable=unused-import

Expand Down Expand Up @@ -172,6 +173,52 @@ def validate_seeded_answers(answers, options, algo):
else:
raise UnknownChooseAnswerAlgorithm()

def get_other_answers_count(pool, seeded_answers, get_student_item_dict):
"""
Count of available answers and seeds in the pool for each option
Args:
pool (dict): answer pool, format:
{
option1_index: {
student_id: { can store algorithm specific info here }
},
option2_index: {
student_id: { ... }
}
}
seeded_answers (list): seeded answers from instructor
[
{'answer': 0, 'rationale': 'rationale A'},
{'answer': 1, 'rationale': 'rationale B'},
]
get_student_item_dict (callable): get student item dict function to return student item dict
Returns:
dict: count for each option
{
0: 4,
1: 2,
3: 1,
...
}
"""
ret = {}

# clean up answers so that all keys are int
pool = {int(k): v for k, v in pool.items()}
merged_pool = convert_seeded_answers(seeded_answers)
student_id = get_student_item_dict()['student_id']
for key in pool:
merged_pool.setdefault(key, {})
merged_pool[key].update(pool[key])
# Pop student's own answer, if exists
merged_pool[key].pop(student_id, None)

for key in merged_pool:
ret[key] = len(merged_pool.get(key, {}))
return ret

def get_other_answers(pool, seeded_answers, get_student_item_dict, algo, options):
"""
Expand Down Expand Up @@ -323,6 +370,96 @@ def get_other_answers_random(pool, seeded_answers, get_student_item_dict, num_re
return {"answers": ret}


def refresh_answers(answers_shown, option, pool, seeded_answers, get_student_item_dict, seeded_first=False):
"""
Refresh the answers shown for given option
Args:
answers_shown (dict): answers being shown that need to be refreshed. Format:
{'answers': [
{'option': 0, 'rationale': 'rationale A'},
{'option': 1, 'rationale': 'rationale B'},
]}
option (int): the option to refresh
pool (dict): answer pool, format:
{
option1_index: {
student_id: { can store algorithm specific info here }
},
option2_index: {
student_id: { ... }
}
}
seeded_answers (list): seeded answers from instructor
[
{'answer': 0, 'rationale': 'rationale A'},
{'answer': 1, 'rationale': 'rationale B'},
]
get_student_item_dict (callable): get student item dict function to return student item dict
seeded_first (boolean): refresh with answers from seeded_answers first, when exhausted, pick from pool
Returns:
dict: refreshed answers lists
{
'answers':
[
{'option': 0, 'rationale': 'rationale A'},
{'option': 1, 'rationale': 'rationale B'},
]
}
"""
ret = copy.deepcopy(answers_shown)
# clean up answers so that all keys are int
pool = {int(k): v for k, v in pool.items()}
seeded_pool = convert_seeded_answers(seeded_answers)
student_id = get_student_item_dict()['student_id']

available_students = copy.deepcopy(pool.get(option, {}))
available_students.pop(student_id, None)
# if seed answers have higher priority, fill the available seeds.
# otherwise merge them into available students
available_seeds = {}
if seeded_first and seeded_pool.get(option, {}):
available_seeds = copy.deepcopy(seeded_pool.get(option, {}))
else:
for key in seeded_pool.get(option, {}):
available_students[key] = seeded_pool.get(option, {}).get(key, None)

for answer in ret.get('answers', []):
if answer.get('option', None) == option:
rationale = None

while available_seeds:
key = random.choice(available_seeds.keys())
rationale = available_seeds.pop(key, None)
if rationale is not None:
answer['rationale'] = rationale
break;

while available_students and rationale is None:
key = random.choice(available_students.keys())
# remove the chosen answer from pool
content = available_students.pop(key, None)

if key.startswith('seeded'):
rationale = content
else:
student_item = get_student_item_dict(key)
submission = sas_api.get_answers_for_student(student_item)
# 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)

if rationale:
answer['rationale'] = rationale
break

# random.shuffle(ret['answers'])
return ret


def convert_seeded_answers(answers):
"""
Convert seeded answers into the format that can be merged into student answers.
Expand Down
23 changes: 19 additions & 4 deletions ubcpi/static/css/ubcpi.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ fieldset .ubcpi-label {
border-radius: 3px;
padding: 10px;
width: 100%;
background-color: #fbfbfb;
}

fieldset .ubcpi-label:hover {
Expand Down Expand Up @@ -191,10 +192,6 @@ div.course-wrapper section.course-content .vert-mod > div ul.ubcpi-other-answers
border-bottom: 1px solid #cfc6c6;
}

div.course-wrapper section.course-content .vert-mod .sample-answer-list > :not(:first-child) {
border-top: 1px solid #cfc6c6;
}

div.course-wrapper section.course-content .vert-mod .sample-answer {
margin: 1em;
display: block;
Expand Down Expand Up @@ -625,12 +622,30 @@ text.ubcpibar.label {
border: 2px solid #e5e5e5;
border-radius: 3px;
padding: 10px;
background-color: #fbfbfb;
}

.ubcpi-breakdown-answer-options .ubcpi-option .ubcpi-breakdown-answer-text {
display: block;
}

.ubcpi-refresh-section {
margin: 1em;
border-top: 1px solid #cfc6c6;
color: #0075b4;
}

.ubcpi-refresh-option-button * {
font-size: 0.9em;
transition: none;
cursor: pointer;
}

.ubcpi-refresh-option-button-disabled {
pointer-events: none;
color: #888;
}

#pi-form .list-input.settings-list .setting-label {
vertical-align: top;
}
Expand Down
18 changes: 11 additions & 7 deletions ubcpi/static/html/ubcpi.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n

<a id="reflecting" name="reflecting"></a>

<div role="group" aria-label="Progress Indicator">
<div class="ubcpi_progress_bar" role="group" aria-label="Progress Indicator">
<span id="answer" data-ng-if="rc.status() == rc.ALL_STATUS.NEW">
<ol style="background:#f8f8f8;border:2px solid #ddd;margin:1em 0;padding-top:.3em;">
<li style="display:inline-block;width:30%;text-align:center;font-weight:bold;color:#111111;padding-top:.3em"><span class="fa fa-arrow-circle-down" aria-hidden="true" style="font-size:1.4em;"></span><br><span translate>Answer</span><span class="sr" translate>, In Progress</span></li>
Expand All @@ -47,13 +47,13 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n
<legend>
<span data-ng-if="rc.status() == rc.ALL_STATUS.NEW" style="color:#414141" translate>Step 1) Give Initial Answer <span class="inline-hint">You can change this answer later, if you change your mind.</span></span>
<span data-ng-if="rc.status() == rc.ALL_STATUS.ANSWERED" style="color:#414141" translate>Step 2) Read Other Student Answers
<p class="ubcpi-other-answers-instructions" translate>These are samples of other student answers for this question. Read them and then compare with your answer below.</p>
<p class="ubcpi-other-answers-instructions" translate>These are randomly chosen samples of other student answers for this question. Read them and compare with your answer below. Then you may revise your answer, if you wish.</p>
</span>
</legend>

<div id="hiding-options-div" class="ubcpi-possible-options">
<div class="ubcpi-option" data-ng-repeat="(optionKey, option) in options track by $index">
<label class="ubcpi-label ubcpi-answer" data-ng-class="{'ubcpi-no-pointer': rc.status() == rc.ALL_STATUS.ANSWERED && !rc.revising}" for="original-option-input-{{ $index }}">
<label class="ubcpi-label ubcpi-answer" data-ng-class="{'ubcpi-no-pointer': rc.status() == rc.ALL_STATUS.ANSWERED && !rc.revising}">
<input class="ubcpi-field" type="radio" id="original-option-input-{{ $index }}" data-ng-if="rc.status() == rc.ALL_STATUS.NEW || rc.revising" name="q" data-ng-model="rc.answer" value="{{optionKey}}" required integer>

<img data-ng-src="{{option.image_url}}" id="original-option-image-{{ $index }}" alt="{{option.image_alt}}" data-ng-if="option.image_position == 'above' && option.image_url" />
Expand All @@ -69,6 +69,9 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n
<span class="other-rationale">"{{otherAnswer.rationale}}"</span>
</div>
</div>
<div class="ubcpi-refresh-section" data-ng-if="rc.status() == rc.ALL_STATUS.ANSWERED && !rc.revising">
<span class="ubcpi-refresh-option-button" data-ng-class="{'ubcpi-refresh-option-button-disabled': rc.alt_answers_available && !rc.alt_answers_available[optionKey]}" ubcpi-refresh-rationale ubcpi-option="{{optionKey}}" ubcpi-refresh-model="rc.other_answers.answers" translate></span>
</div>
</div>
<div class="no-sample-answer" ng-if="rc.status() == rc.ALL_STATUS.ANSWERED && !rc.hasSampleExplanationForOption(optionKey)">
(no student explanations were randomly selected for this answer)
Expand Down Expand Up @@ -98,7 +101,7 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n
</p>

<div style="text-align: right;" data-ng-if="!rc.revising">
<input id="submit-button" onclick="this.blur();" style="display: inline; margin-left: 5px;" data-ng-disabled="answerForm.$invalid" type='button' class='ubcpi_submit' value="{{ 'Next Step' | translate }}" name='ubcpi_next_step' data-ng-click="rc.clickSubmit($event);" aria-describedby="button-disabled-reason ubcpi-next-inline-hints"/>
<input id="submit-button" onclick="this.blur();" style="display: inline; margin-left: 5px;" data-ng-disabled="answerForm.$invalid" type='button' class='ubcpi_submit' value="{{ 'Next Step' | translate }}" name='ubcpi_next_step' data-ng-click="rc.clickSubmit($event);" aria-describedby="button-disabled-reason ubcpi-next-inline-hints" scroll-to-progress-bar='click'/>
</div>

</div>
Expand All @@ -110,9 +113,10 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n
</div>
<div style="text-align: right;">
<p id="decision-prompt" style="text-align: right;" data-ng-if="!rc.revising" translate>What would you like to do?</p>
<input id="dummy-button" style="margin-right: 5px; display: inline-block; display:none;" type='button' class='ubcpi_submit' name='ubcpi_dummy' />
<input id="update-button" style="margin-right: 5px; display: inline-block;" type='button' class='ubcpi_submit' data-ng-if="!rc.revising" value="{{ 'Revise Answer' | translate }}" name='ubcpi_update_step' data-ng-click="rc.revising=true;"/>
<input id="cancel-button" style="margin-right: 5px; display: inline-block;" type='button' class='ubcpi_submit' data-ng-if="rc.revising" value="{{ 'Cancel' | translate }}" name='ubcpi_update_step' data-ng-click="rc.answer=rc.answer_original; rc.rationale=rc.rationale_original; rc.revising=false;"/>
<input id="submit-button" style="display: inline-block;" onclick="this.blur(); " style="display: inline; margin-left: 5px;" data-ng-disabled="answerForm.$invalid" type='button' class='ubcpi_submit' value="{{ 'Submit Answer' | translate }}" name='ubcpi_next_step' data-ng-click="rc.clickSubmit($event);" aria-describedby="button-disabled-reason ubcpi-next-inline-hints"/>
<input id="cancel-button" style="margin-right: 5px; display: inline-block;" type='button' class='ubcpi_submit' data-ng-if="rc.revising" value="{{ 'Cancel' | translate }}" name='ubcpi_update_step_cancel' data-ng-click="rc.answer=rc.answer_original; rc.rationale=rc.rationale_original; rc.revising=false;"/>
<input id="submit-button" style="display: inline-block;" onclick="this.blur(); " style="display: inline; margin-left: 5px;" data-ng-disabled="answerForm.$invalid" type='button' class='ubcpi_submit' value="{{ 'Submit Answer' | translate }}" name='ubcpi_next_step' data-ng-click="rc.clickSubmit($event);" aria-describedby="button-disabled-reason ubcpi-next-inline-hints" scroll-to-progress-bar='click' onclick=""/>
</div>
</div>
</div>
Expand Down Expand Up @@ -167,7 +171,7 @@ <h3 id="pi-question-h" class="question-text" style="display:inline;">{{display_n
<img data-ng-src="{{question_text.image_url}}" id="question-image" alt="{{question_text.image_alt}}" data-ng-if="question_text.image_position == 'below' && question_text.image_url" />
</div>

<div id="finalReflecting" role="group" aria-label="Progress Indicator">
<div class="ubcpi_progress_bar" id="finalReflecting" role="group" aria-label="Progress Indicator">
<span id="test" data-ng-if="rc.status() == rc.ALL_STATUS.REVISED">
<ol style="background:#f8f8f8;border:2px solid #ddd;margin:1em 0;padding-top:.3em;">
<li style="display:inline-block;width:30%;text-align:center;color:#414141;padding-top:.3em"><span class="fa fa-check-circle" aria-hidden="true" style="font-size:1.4em;"></span><br><span translate>Answer</span><span class="sr" translate>, Completed</span></li>
Expand Down
2 changes: 1 addition & 1 deletion ubcpi/static/html/ubcpi_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
<div class="wrapper-comp-setting pi-wrapper-comp-setting">
<div class="pi-label-and-hint">
<label class="label setting-label pi-setting-label" for="pi-algo-num-responses" aria-describedby="pi-num-responses-tip" translate>Answers Students See - Number Selected <font color="red">*</font></label>
<span id="pi-num-responses-tip" class="tip setting-help" translate>This is the number of examples shown to the students after they answer. Enter the # symbol to use the same number as the answer possibilities you've set.</span>
<span id="pi-num-responses-tip" class="tip setting-help" translate>This is the number of examples shown on screen to the students after they answer. Students can choose to refresh for some other samples. Enter the # symbol to use the same number as the answer possibilities you've set.</span>
</div>
<input class="input setting-input pi-options" name="pi-algo-num-responses" id="pi-algo-num-responses" ng-model="esc.data.algo.num_responses" ng-model-options="{ debounce: 500 }" type="text" required />
</div>
Expand Down

0 comments on commit 32ba633

Please sign in to comment.