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 Aug 29, 2018
1 parent 80018da commit 77fb16e
Show file tree
Hide file tree
Showing 23 changed files with 1,491 additions and 50 deletions.
40 changes: 33 additions & 7 deletions ubcpi/answer_pool.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,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,
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))
147 changes: 146 additions & 1 deletion ubcpi/static/css/ubcpi.css
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,10 @@ text.ubcpibar.correct-answer {
padding-left: 7px;
}

.other-rationale-inappropriate {
cursor: pointer;
}

.ubcpi-class-breakdown {
padding: 10px 20px;
border: 1px solid #D7DBDF;
Expand Down Expand Up @@ -625,6 +629,42 @@ text.ubcpibar.correct-answer {
display: block;
}

.ubcpi-staff-explanation-pool {
text-align: left;
background: #F8F8F8;
padding: 15px;
margin: 15px 0 20px;
}

.ubcpi-staff-explanation-pool .ubcpi-staff-explanation-pool-loading {
text-align: center;
}

.ubcpi-staff-explanation-pool .pool-table {
width: 100%;
border: 1px solid black;
font-size: 0.8em;
}

.ubcpi-staff-explanation-pool .pool-table thead td {
text-align: center;
font-weight: bold;
}

.ubcpi-staff-explanation-pool .pool-table td {
border: 1px solid #ccc;
}

.ubcpi-staff-explanation-pool .pool-table .ubcpi-staff-override-options {
display: inline-block;
margin-left: 10px;
cursor: pointer;
}

.ubcpi-staff-explanation-pool .pool-table tbody tr:nth-child(even){
background-color: #f2f2f2;
}

#pi-form .list-input.settings-list .setting-label {
vertical-align: top;
}
Expand Down Expand Up @@ -768,6 +808,12 @@ div.course-wrapper section.course-content .warning-notice p {
transition:none;
}

.ubcpi-staff-statistics-button *{
font-size:12px;
color:#0075b4;
transition:none;
}

.ubcpi-staff-statistics-button--active{
color:white;
background-color:#b62568;
Expand All @@ -779,6 +825,12 @@ div.course-wrapper section.course-content .warning-notice p {
transition:none;
}

.ubcpi-staff-statistics-button--active *{
font-size:12px;
color:#0075b4;
transition:none;
}

.ubcpi-staff-statistics{
padding:1em;
margin-top:0.5em;
Expand All @@ -789,7 +841,7 @@ div.course-wrapper section.course-content .warning-notice p {

.ubcpi-staff-statistics-button:hover{
color:white;
background-color: #61b5e6;
background-color:#b62568;
}

.ubcpi-staff-statistics-button:active{
Expand All @@ -805,6 +857,53 @@ div.course-wrapper section.course-content .warning-notice p {
outline:none;
}

.ubcpi-staff-answer-pool-button {
border:none;
border-radius:5px;
padding:5px 10px;
text-transform:uppercase;
font-size:12px;
background-color:rgba(3,3,3,0.05);
transition:none;
}

.ubcpi-staff-answer-pool-button * {
font-size:12px;
color:#0075b4;
transition:none;
}

.ubcpi-staff-answer-pool-button--active{
color:white;
background-color:#b62568;
border:none;
border-radius:5px;
padding: 5px 10px;
text-transform:uppercase;
font-size:12px;
transition:none;
}

.ubcpi-staff-answer-pool-button--active * {
color:#white;
font-size:12px;
transition:none;
}

.ubcpi-staff-answer-pool-button:hover{
background-color: #b62568;
}

.ubcpi-staff-answer-pool-button:active {
color:white;
background-color:#b62568;
}

.ubcpi-staff-answer-pool-button:focus, .ubcpi-staff-answer-pool-button:focus{
outline:none;
}


pi-barchart > span{
text-align:center;
display:block;
Expand Down Expand Up @@ -849,3 +948,49 @@ pi-barchart > svg {
#not-enough-data {
font-size: 0.85em;
}

.ubcpi-modal-dialog {
display: none;
background: #F0F0F0;
box-shadow: 0 0 20px 0 rgba(0,0,0,0.4);
border-radius: 5px;
padding: 10px;
width: 50%;
height: 60%;
overflow: auto;
}

.ubcpi-modal-dialog-header {
text-align: center;
font-size: 1.2em;
font-weight: bold;
}

.ubcpi-modal-dialog-body {
text-align: left;
font-size: 0.8em;
padding: 10px;
}

.ubcpi-report-inappropriate-button {
font-size: 0.9em;
border-radius: 4px;
padding-left: 10px;
padding-right: 10px;
padding-top: 5px;
padding-bottom: 5px;
border: none;
color: #0075b4;
background-color: rgba(3,3,3, 0.05);
transition: none;
}

.ubcpi-report-inappropriate-button * {
font-size: 0.9em;
color: #0075b4;
transition: none;
}

.ubcpi-report-inappropriate-button:hover {
background-color: #61b5e6;
}

0 comments on commit 77fb16e

Please sign in to comment.