Commit 26a301f4 by Vik Paruchuri

Merge pull request #504 from edx/feature/vik/oe-ui

Feature/vik/oe ui
parents f3943c7e 21e13e44
......@@ -213,7 +213,7 @@ class CombinedOpenEndedFields(object):
help="The number of times the student can try to answer this problem.",
default=1,
scope=Scope.settings,
values={"min" : 1 }
values={"min": 1 }
)
accept_file_upload = Boolean(
display_name="Allow File Uploads",
......@@ -229,12 +229,10 @@ class CombinedOpenEndedFields(object):
)
due = Date(
help="Date that this problem is due by",
default=None,
scope=Scope.settings
)
graceperiod = Timedelta(
help="Amount of time after the due date that submissions will be accepted",
default=None,
scope=Scope.settings
)
version = VersionInteger(help="Current version number", default=DEFAULT_VERSION, scope=Scope.settings)
......@@ -244,7 +242,7 @@ class CombinedOpenEndedFields(object):
display_name="Problem Weight",
help="Defines the number of points each problem is worth. If the value is not set, each problem is worth one point.",
scope=Scope.settings,
values={"min" : 0 , "step": ".1"},
values={"min": 0, "step": ".1"},
default=1
)
min_to_calibrate = Integer(
......@@ -252,28 +250,28 @@ class CombinedOpenEndedFields(object):
help="The minimum number of calibration essays each student will need to complete for peer grading.",
default=3,
scope=Scope.settings,
values={"min" : 1, "max" : 20, "step" : "1"}
values={"min": 1, "max": 20, "step": "1"}
)
max_to_calibrate = Integer(
display_name="Maximum Peer Grading Calibrations",
help="The maximum number of calibration essays each student will need to complete for peer grading.",
default=6,
scope=Scope.settings,
values={"min" : 1, "max" : 20, "step" : "1"}
values={"min": 1, "max": 20, "step": "1"}
)
peer_grader_count = Integer(
display_name="Peer Graders per Response",
help="The number of peers who will grade each submission.",
default=3,
scope=Scope.settings,
values={"min" : 1, "step" : "1", "max" : 5}
values={"min": 1, "step": "1", "max": 5}
)
required_peer_grading = Integer(
display_name="Required Peer Grading",
help="The number of other students each student making a submission will have to grade.",
default=3,
scope=Scope.settings,
values={"min" : 1, "step" : "1", "max" : 5}
values={"min": 1, "step": "1", "max": 5}
)
markdown = String(
help="Markdown source of this module",
......
......@@ -19,10 +19,10 @@ h2 {
iframe[seamless]{
background-color: transparent;
border: 0px none transparent;
padding: 0px;
overflow: hidden;
padding: 0px;
border: 0px none transparent;
background-color: transparent;
}
.inline-error {
......@@ -31,17 +31,17 @@ iframe[seamless]{
section.problem-progress {
display: inline-block;
padding-left: 5px;
color: #999;
font-size: em(16);
font-weight: 100;
padding-left: 5px;
font-size: em(16);
}
section.problem {
@media print {
display: block;
width: auto;
padding: 0;
width: auto;
canvas, img {
page-break-inside: avoid;
......@@ -54,25 +54,24 @@ section.problem {
.choicegroup {
@include clearfix;
min-width: 100px;
width: auto !important;
width: 100px;
label.choicegroup_correct{
&:after{
label.choicegroup_correct {
&:after {
margin-left: 15px;
content: url('../images/correct-icon.png');
margin-left:15px
}
}
label.choicegroup_incorrect{
&:after{
label.choicegroup_incorrect {
&:after {
margin-left: 15px;
content: url('../images/incorrect-icon.png');
margin-left:15px;
}
}
min-width:100px;
width: auto !important;
width: 100px;
.indicator_container {
float: left;
width: 25px;
......@@ -82,9 +81,9 @@ section.problem {
fieldset {
@include box-sizing(border-box);
margin: 0px 0px $baseline;
padding-left: $baseline;
border-left: 1px solid #ddd;
padding-left: 20px;
margin: 0px 0px 20px;
}
input[type="radio"],
......@@ -102,21 +101,21 @@ section.problem {
ol.enumerate {
li {
&:before {
content: " ";
display: block;
height: 0;
visibility: hidden;
height: 0;
content: " ";
}
}
}
.solution-span {
> span {
margin: 20px 0;
margin: $baseline 0;
display: block;
border: 1px solid #ddd;
padding: 9px 15px 20px;
background: #FFF;
padding: 9px 15px $baseline;
background: #fff;
position: relative;
box-shadow: inset 0 0 0 1px #eee;
border-radius: 3px;
......@@ -133,26 +132,26 @@ section.problem {
margin-top: -2px;
}
&.status {
margin: 8px 0 0 $baseline/2;
text-indent: -9999px;
margin: 8px 0 0 10px;
}
}
&.unanswered {
p.status {
@include inline-block();
background: url('../images/unanswered-icon.png') center center no-repeat;
height: 14px;
width: 14px;
height: 14px;
background: url('../images/unanswered-icon.png') center center no-repeat;
}
}
&.correct, &.ui-icon-check {
p.status {
@include inline-block();
background: url('../images/correct-icon.png') center center no-repeat;
height: 20px;
width: 25px;
height: 20px;
background: url('../images/correct-icon.png') center center no-repeat;
}
input {
......@@ -163,9 +162,9 @@ section.problem {
&.processing {
p.status {
@include inline-block();
background: url('../images/spinner.gif') center center no-repeat;
height: 20px;
width: 20px;
height: 20px;
background: url('../images/spinner.gif') center center no-repeat;
}
input {
......@@ -176,9 +175,9 @@ section.problem {
&.incorrect, &.incomplete, &.ui-icon-close {
p.status {
@include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px;
width: 20px;
height: 20px;
background: url('../images/incorrect-icon.png') center center no-repeat;
text-indent: -9999px;
}
......@@ -195,12 +194,12 @@ section.problem {
p.answer {
@include inline-block();
margin-bottom: 0;
margin-left: 10px;
margin-left: $baseline/2;
&:before {
display: inline;
content: "Answer: ";
font-weight: bold;
display: inline;
}
&:empty {
......@@ -228,12 +227,12 @@ section.problem {
margin-bottom: 0;
&.math {
@include inline-block;
padding: 6px;
background: #f1f1f1;
min-width: 30px;
border: 1px solid #e3e3e3;
@include inline-block;
border-radius: 4px;
min-width: 30px;
background: #f1f1f1;
}
}
}
......@@ -241,98 +240,91 @@ section.problem {
span {
&.unanswered, &.ui-icon-bullet {
@include inline-block();
background: url('../images/unanswered-icon.png') center center no-repeat;
height: 14px;
position: relative;
top: 4px;
width: 14px;
height: 14px;
background: url('../images/unanswered-icon.png') center center no-repeat;
}
&.processing, &.ui-icon-processing {
@include inline-block();
background: url('../images/spinner.gif') center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px;
height: 20px;
background: url('../images/spinner.gif') center center no-repeat;
}
&.correct, &.ui-icon-check {
@include inline-block();
background: url('../images/correct-icon.png') center center no-repeat;
height: 20px;
position: relative;
top: 3px;
width: 25px;
height: 20px;
background: url('../images/correct-icon.png') center center no-repeat;
}
&.partially-correct {
@include inline-block();
background: url('../images/partially-correct-icon.png') center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px;
height: 20px;
background: url('../images/partially-correct-icon.png') center center no-repeat;
}
&.incorrect, &.incomplete, &.ui-icon-close {
@include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 3px;
width: 20px;
height: 20px;
background: url('../images/incorrect-icon.png') center center no-repeat;
}
}
.reload
{
.reload {
float:right;
margin: 10px;
margin: $baseline/2;
}
.grader-status {
padding: 9px;
background: #F6F6F6;
border: 1px solid #ddd;
border-top: 0;
margin-bottom: 20px;
@include clearfix;
margin: $baseline/2 0;
padding: $baseline/2;
border-radius: 5px;
background: #F6F6F6;
span {
text-indent: -9999px;
overflow: hidden;
display: block;
float: left;
overflow: hidden;
margin: -7px 7px 0 0;
text-indent: -9999px;
}
.grading {
background: url('../images/info-icon.png') left center no-repeat;
margin: 0px 7px 0 0;
padding-left: 25px;
background: url('../images/info-icon.png') left center no-repeat;
text-indent: 0px;
margin: 0px 7px 0 0;
}
p {
line-height: 20px;
text-transform: capitalize;
margin-bottom: 0;
float: left;
margin-bottom: 0;
text-transform: capitalize;
line-height: 20px;
}
&.file {
background: #FFF;
margin-top: 20px;
padding: 20px 0 0 0;
border: {
top: 1px solid #eee;
right: 0;
bottom: 0;
left: 0;
}
margin-top: $baseline;
padding: $baseline 0 0 0;
border: 0;
border-top: 1px solid #eee;
background: #fff;
p.debug {
display: none;
......@@ -352,8 +344,8 @@ section.problem {
.feedback-on-feedback {
margin-right: $baseline;
height: 100px;
margin-right: 20px;
}
.evaluation-response {
......@@ -367,32 +359,32 @@ section.problem {
.evaluation-scoring {
.scoring-list {
list-style-type: none;
margin-left: 3px;
list-style-type: none;
li {
display:inline;
margin-left: 50px;
&:first-child {
margin-left: 0px;
}
display:inline;
margin-left: 50px;
label {
font-size: .9em;
}
}
}
}
.submit-message-container {
margin: 10px 0px ;
margin: $baseline 0px ;
}
}
form.option-input {
margin: -10px 0 20px;
padding-bottom: 20px;
margin: -$baseline/2 0 $baseline;
padding-bottom: $baseline;
select {
margin-right: flex-gutter();
......@@ -400,17 +392,17 @@ section.problem {
}
ul {
list-style: disc outside none;
margin-bottom: lh();
margin-left: .75em;
margin-left: .75rem;
list-style: disc outside none;
}
ol {
list-style: decimal outside none;
margin-bottom: lh();
margin-left: .75em;
margin-left: .75rem;
list-style: decimal outside none;
}
dl {
......@@ -431,8 +423,8 @@ section.problem {
}
li {
line-height: 1.4em;
margin-bottom: lh(.5);
line-height: 1.4em;
&:last-child {
margin-bottom: 0;
......@@ -449,8 +441,8 @@ section.problem {
table-layout: auto;
th {
font-weight: bold;
text-align: left;
font-weight: bold;
}
td {
......@@ -463,44 +455,43 @@ section.problem {
}
caption {
background: #f1f1f1;
margin-bottom: .75em;
margin-bottom: .75rem;
padding: .75em 0;
padding: .75rem 0;
background: #f1f1f1;
}
tr, td, th {
vertical-align: middle;
}
}
code {
margin: 0 2px;
padding: 0px 5px;
white-space: nowrap;
border: 1px solid #EAEAEA;
background-color: #F8F8F8;
border: 1px solid #eaeaea;
border-radius: 3px;
background-color: #f8f8f8;
white-space: nowrap;
font-size: .9em;
}
pre {
background-color: #F8F8F8;
border: 1px solid #CCC;
font-size: .9em;
line-height: 1.4;
overflow: auto;
padding: 6px 10px;
padding: 6px $baseline/2;
border: 1px solid #ccc;
border-radius: 3px;
background-color: #f8f8f8;
font-size: .9em;
line-height: 1.4;
> code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
white-space: pre;
}
}
......@@ -517,26 +508,25 @@ section.problem {
}
pre {
border-radius: 0;
border-radius: 0;
border-width: 0;
overflow: hidden;
margin: 0;
padding: 0;
border-width: 0;
border-radius: 0;
background: transparent;
font-family: inherit;
font-size: inherit;
white-space: pre;
word-wrap: normal;
overflow: hidden;
font-size: inherit;
font-family: inherit;
resize: none;
&.CodeMirror-cursor {
z-index: 10;
position: absolute;
z-index: 10;
visibility: hidden;
border-left: 1px solid black;
border-right: none;
width: 0;
border-right: none;
border-left: 1px solid black;
}
}
}
......@@ -546,14 +536,14 @@ section.problem {
}
hr {
background: #ddd;
border: none;
clear: both;
color: #ddd;
float: none;
height: 1px;
clear: both;
margin: 0 0 .75rem;
width: 100%;
height: 1px;
border: none;
background: #ddd;
color: #ddd;
}
.hidden {
......@@ -570,17 +560,17 @@ section.problem {
center {
display: block;
margin: lh() 0;
border: 1px solid #ccc;
padding: lh();
border: 1px solid #ccc;
}
section.action {
margin-top: 20px;
margin-top: $baseline;
.save, .check, .show, .reset {
height: ($baseline*2);
font-weight: 600;
vertical-align: middle;
font-weight: 600;
}
.save {
......@@ -590,8 +580,8 @@ section.problem {
.show {
.show-label {
font-size: 1.0em;
font-weight: 600;
font-size: 1.0em;
}
}
......@@ -602,20 +592,20 @@ section.problem {
// padding: 8px 12px;
// margin-top: 10px;
@include inline-block;
font-style: italic;
margin: 8px 0 0 10px;
margin: 8px 0 0 $baseline/2;
color: #777;
font-style: italic;
-webkit-font-smoothing: antialiased;
}
}
.detailed-solution {
> p:first-child {
font-size: 0.9em;
color: #aaa;
text-transform: uppercase;
font-weight: bold;
font-style: normal;
text-transform: uppercase;
color: #AAA;
font-size: 0.9em;
}
p:last-child {
......@@ -624,12 +614,12 @@ section.problem {
}
div.capa_alert {
margin-top: $baseline;
padding: 8px 12px;
border: 1px solid #EBE8BF;
border: 1px solid #ebe8bf;
border-radius: 3px;
background: #FFFCDD;
background: #fffcdd;
font-size: 0.9em;
margin-top: 10px;
}
div.capa_reset {
......@@ -638,12 +628,14 @@ section.problem {
background-color: lighten($error-red, 25%);
border-radius: 3px;
font-size: 1em;
margin-top: 10px;
margin-bottom: 10px;
margin-top: $baseline/2;
margin-bottom: $baseline/2;
}
.capa_reset>h2 {
color: #AA0000;
color: #aa0000;
}
.capa_reset li {
font-size: 0.9em;
}
......@@ -652,10 +644,10 @@ section.problem {
border: 1px solid #ccc;
h3 {
border-bottom: 1px solid #e3e3e3;
text-shadow: 0 1px 0 #fff;
padding: 9px;
border-bottom: 1px solid #e3e3e3;
background: #eee;
text-shadow: 0 1px 0 #fff;
font-weight: bold;
font-size: em(16);
}
......@@ -675,7 +667,7 @@ section.problem {
a {
display: block;
padding: 9px;
background: #F6F6F6;
background: #f6f6f6;
box-shadow: inset 0 0 0 1px #fff;
}
}
......@@ -693,22 +685,22 @@ section.problem {
margin-bottom: 12px;
h3 {
font-size: 0.9em;
color: #aaa;
text-transform: uppercase;
font-weight: bold;
font-style: normal;
text-transform: uppercase;
color: #AAA;
font-size: 0.9em;
}
}
> section {
border: 1px solid #ddd;
padding: 9px 9px 20px;
margin-bottom: 10px;
background: #FFF;
position: relative;
box-shadow: inset 0 0 0 1px #eee;
margin-bottom: $baseline/2;
padding: 9px 9px $baseline;
border: 1px solid #ddd;
border-radius: 3px;
background: #fff;
box-shadow: inset 0 0 0 1px #eee;
p:last-of-type {
margin-bottom: 0;
......@@ -719,26 +711,27 @@ section.problem {
}
a.full {
@include position(absolute, 0 0 1px 0px);
font-size: .8em;
@include position(absolute, 0 0 1px 0);
@include box-sizing(border-box);
display: block;
padding: 4px;
text-align: right;
width: 100%;
display: block;
background: #F3F3F3;
@include box-sizing(border-box);
background: #f3f3f3;
text-align: right;
font-size: .8em;
}
}
}
.external-grader-message {
section {
padding-left: 20px;
background-color: #FAFAFA;
color: #2C2C2C;
font-family: monospace;
padding-top: $baseline/2;
padding-left: $baseline;
background-color: #fafafa;
color: #2c2c2c;
font-size: 1em;
padding-top: 10px;
font-family: monospace;
header {
font-size: 1.4em;
}
......@@ -748,35 +741,36 @@ section.problem {
}
.longform {
padding: 0px;
margin: 0px;
margin: 0;
padding: 0;
.result-errors {
margin: 5px;
padding: 10px 10px 10px 40px;
margin: $baseline/4;
padding: $baseline/2 $baseline/2 $baseline/2 $baseline*2;
background: url('../images/incorrect-icon.png') center left no-repeat;
li {
color: #B00;
color: #b00;
}
}
.result-output {
margin: 5px;
padding: 20px 0px 15px 50px;
border-top: 1px solid #DDD;
border-left: 20px solid #FAFAFA;
margin: $baseline/4;
padding: $baseline 0 15px 50px;
border-top: 1px solid #ddd;
border-left: $baseline solid #fafafa;
h4 {
font-family: monospace;
font-size: 1em;
font-family: monospace;
}
dl {
margin: 0px;
margin: 0;
}
dt {
margin-top: 20px;
margin-top: $baseline;
}
dd {
......@@ -786,6 +780,7 @@ section.problem {
.result-correct {
background: url('../images/correct-icon.png') left 20px no-repeat;
.result-actual-output {
color: #090;
}
......@@ -793,6 +788,7 @@ section.problem {
.result-incorrect {
background: url('../images/incorrect-icon.png') left 20px no-repeat;
.result-actual-output {
color: #B00;
}
......@@ -800,16 +796,16 @@ section.problem {
.markup-text{
margin: 5px;
padding: 20px 0px 15px 50px;
border-top: 1px solid #DDD;
border-left: 20px solid #FAFAFA;
padding: $baseline 0 15px 50px;
border-top: 1px solid #ddd;
border-left: 20px solid #fafafa;
bs {
color: #BB0000;
color: #bb0000;
}
bg {
color: #BDA046;
color: #bda046;
}
}
}
......@@ -818,44 +814,52 @@ section.problem {
.rubric {
tr {
margin:10px 0px;
margin: $baseline/2 0;
height: 100%;
}
td {
padding: 20px 0px;
margin: 10px 0px;
margin: $baseline/2 0;
padding: $baseline 0;
height: 100%;
}
th {
padding: 5px;
margin: 5px;
margin: $baseline/4;
padding: $baseline/4;
}
label,
.view-only {
margin:3px;
position: relative;
padding: 15px;
width: 150px;
height:100%;
display: inline-block;
min-height: 50px;
margin: 3px;
padding: 15px;
min-width: 50px;
background-color: #CCC;
min-height: 50px;
width: 150px;
height: 100%;
background-color: #ccc;
font-size: .9em;
}
.grade {
position: absolute;
bottom:0px;
right:0px;
margin:10px;
right: 0;
bottom: 0;
margin: $baseline/2;
}
.selected-grade {
background: #666;
color: white;
}
input[type=radio]:checked + label {
background: #666;
color: white; }
color: white;
}
input[class='score-selection'] {
display: none;
}
......@@ -863,50 +867,57 @@ section.problem {
.annotation-input {
$yellow: rgba(255,255,10,0.3);
margin: 0 0 1em 0;
border: 1px solid #ccc;
border-radius: 1em;
margin: 0 0 1em 0;
.annotation-header {
font-weight: bold;
border-bottom: 1px solid #ccc;
padding: .5em 1em;
border-bottom: 1px solid #ccc;
font-weight: bold;
}
.annotation-body { padding: .5em 1em; }
a.annotation-return {
float: right;
font: inherit;
font-weight: normal;
}
a.annotation-return:after { content: " \2191" }
.block, ul.tags {
margin: .5em 0;
padding: 0;
}
.block-highlight {
padding: .5em;
border: 1px solid darken($yellow, 10%);
background-color: $yellow;
color: #333;
font-style: normal;
background-color: $yellow;
border: 1px solid darken($yellow, 10%);
}
.block-comment { font-style: italic; }
ul.tags {
display: block;
list-style-type: none;
margin-left: 1em;
list-style-type: none;
li {
position: relative;
display: block;
margin: 1em 0 0 0;
position: relative;
.tag {
display: inline-block;
cursor: pointer;
margin-left: $baseline*2;
border: 1px solid rgb(102,102,102);
margin-left: 40px;
cursor: pointer;
&.selected {
background-color: $yellow;
}
......@@ -918,26 +929,31 @@ section.problem {
.tag-status, .tag { padding: .25em .5em; }
}
}
textarea.comment {
$num-lines-to-show: 5;
$line-height: 1.4em;
$padding: .2em;
width: 100%;
padding: $padding (2 * $padding);
line-height: $line-height;
width: 100%;
height: ($num-lines-to-show * $line-height) + (2*$padding) - (($line-height - 1)/2);
line-height: $line-height;
}
.answer-annotation { display: block; margin: 0; }
/* for debugging the input value field. enable the debug flag on the inputtype */
.debug-value {
color: #fff;
padding: 1em;
margin: 1em 0;
background-color: #999;
padding: 1em;
border: 1px solid #000;
background-color: #999;
color: #fff;
input[type="text"] { width: 100%; }
pre { background-color: #CCC; color: #000; }
&:before {
display: block;
content: "debug input value";
......@@ -947,13 +963,15 @@ section.problem {
}
}
}
.choicetextgroup{
@extend .choicegroup;
input[type="text"]{
margin-bottom: 0.5em;
}
@extend .choicegroup;
label.choicetextgroup_correct, section.choicetextgroup_correct{
label.choicetextgroup_correct, section.choicetextgroup_correct {
@extend label.choicegroup_correct;
input[type="text"] {
......@@ -961,17 +979,18 @@ section.problem {
}
}
label.choicetextgroup_incorrect, section.choicetextgroup_incorrect{
label.choicetextgroup_incorrect, section.choicetextgroup_incorrect {
@extend label.choicegroup_incorrect;
}
label.choicetextgroup_show_correct, section.choicetextgroup_show_correct{
&:after{
content: url('../images/correct-icon.png');
label.choicetextgroup_show_correct, section.choicetextgroup_show_correct {
&:after {
margin-left:15px;
content: url('../images/correct-icon.png');
}
}
span.mock_label{
span.mock_label {
cursor : default;
}
}
......
// lms - xmodule - combinedopenended
// ====================
h2 {
margin-top: 0;
margin-bottom: 15px;
......@@ -16,123 +19,299 @@ h2 {
}
}
// Problem Header
div.name{
padding-bottom: 15px;
h2 {
display: inline;
}
.progress-container {
display: inline;
float: right;
padding-top: 3px;
}
}
.inline-error {
color: darken($error-red, 10%);
}
section.combined-open-ended {
@include clearfix;
.status-container
{
padding-bottom: 5px;
}
div.problemwrapper {
border: 1px solid lightgray;
border-radius: $baseline/2;
.status-bar {
background-color: #eee;
border-radius: $baseline/2 $baseline/2 0 0;
border-bottom: 1px solid lightgray;
.statustable {
width: 100%;
padding: $baseline;
}
.status-container {
display: table-cell;
text-align: center;
.status-elements {
border-radius: $baseline/4;
border: 1px solid lightgray;
}
.item-container
{
padding-bottom: 10px;
}
.result-container
{
float:left;
width: 100%;
position:relative;
.problemtype-container {
padding: $baseline/2;
width: 60%;
}
.problemtype{
padding: $baseline/2;
}
.assessments-container {
float: right;
padding: $baseline/2 $baseline $baseline/2 $baseline/2;
.assessment-text {
display: inline-block;
display: table-cell;
padding-right: $baseline/2;
}
}
h4
{
margin-bottom:10px;
}
.item-container {
padding-bottom: $baseline/2;
margin: 15px;
}
.result-container {
float: left;
width: 100%;
position: relative;
}
}
section.legend-container {
margin: 15px;
border-radius: $baseline/4;
.legenditem {
background-color : #d4d4d4;
font-size: .9em;
padding: 2px;
display: inline;
padding: $baseline/2;
width: 20%;
background-color: #eee;
font-size: .9em;
}
margin-bottom: 5px;
}
section.combined-open-ended-status {
vertical-align: center;
.statusitem {
color: #2C2C2C;
background-color : #d4d4d4;
display: table-cell;
padding: $baseline/2;
width: 30px;
border-right: 1px solid lightgray;
background-color: #eee;
color: #2c2c2c;
font-size: .9em;
padding: 2px;
display: inline;
width: 20%;
&:first-child {
border-radius: $baseline/4 0 0 $baseline/4;
}
&:last-child {
border-right: 0;
border-radius: 0 $baseline/4 $baseline/4 0;
}
&:only-child {
border-radius: $baseline/4;
}
.show-results {
margin-top: .3em;
text-align:right;
}
.show-results-button {
font: 1em monospace;
}
}
.statusitem-current {
background-color: #B2B2B2;
background-color: #fff;
color: #222;
}
span {
&.unanswered {
@include inline-block();
background: url('../images/unanswered-icon.png') center center no-repeat;
height: 14px;
position: relative;
width: 14px;
float: right;
width: 14px;
height: 14px;
background: url('../images/unanswered-icon.png') center center no-repeat;
}
&.correct {
@include inline-block();
background: url('../images/correct-icon.png') center center no-repeat;
height: 20px;
position: relative;
width: 25px;
float: right;
width: 25px;
height: 20px;
background: url('../images/correct-icon.png') center center no-repeat;
}
&.incorrect {
@include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px;
width: 20px;
position: relative;
float: right;
width: 20px;
height: 20px;
background: url('../images/incorrect-icon.png') center center no-repeat;
}
}
.icon-caret-right {
display: inline-block;
margin-right: ($baseline/4);
vertical-align: baseline;
}
}
// Problem Section Controls
.visibility-control, .visibility-control-prompt {
display: block;
width: 100%;
height: 40px;
.inner {
float: left;
margin-top: $baseline;
width: 85%;
height: 5px;
border-top: 1px dotted #ddd;
}
}
.section-header {
display: block;
float: right;
padding-top: $baseline/2;
width: 15%;
text-align: center;
font-size: .9em;
}
// Rubric Styling
.wrapper-score-selection {
display: table-cell;
padding: 0 $baseline/2;
width: 20px;
vertical-align: middle;
}
.wrappable {
display: table-cell;
padding: $baseline/4;
}
.rubric-list-item {
margin-bottom: 2px;
padding: $baseline/2;
&:hover {
background-color: #eee;
}
.rubric-label-selected{
border-radius: $baseline/4;
background-color: #eee;
}
}
span.rubric-category {
display: block;
margin-bottom: $baseline/2;
padding-top: $baseline/2;
width: 100%;
border-bottom: 1px solid lightgray;
font-size: 1.1em;
}
div.combined-rubric-container {
ul.rubric-list{
margin: 15px;
padding-top: $baseline/2;
padding-bottom: $baseline/4;
ul.rubric-list {
margin: 0 $baseline $baseline/2 $baseline;
padding: 0;
list-style-type: none;
padding:0;
margin:0;
li {
&.rubric-list-item{
&.rubric-list-item {
margin-bottom: 2px;
padding: 0px;
padding: $baseline/2;
}
}
}
h4 {
padding-top: $baseline/2;
}
span.rubric-category {
font-size: .9em;
display: block;
width: 100%;
border-bottom: 1px solid lightgray;
font-weight: bold;
font-size: .9em;
}
label.choicegroup_correct {
&:before {
margin-right: 15px;
content: url('../images/correct-icon.png');
}
}
label.choicegroup_partialcorrect {
&:before {
margin-right: 15px;
content: url('../images/partially-correct-icon.png');
}
}
label.choicegroup_incorrect {
&:before {
margin-right: 15px;
content: url('../images/incorrect-icon.png');
}
}
div.written-feedback {
background: #f6f6f6;
padding: 15px;
}
padding-bottom: 5px;
padding-top: 10px;
}
div.result-container {
padding-top: 10px;
padding-bottom: 5px;
.evaluation {
padding-top: $baseline/2;
padding-bottom: $baseline/4;
.evaluation {
p {
margin-bottom: 1px;
}
......@@ -140,28 +319,31 @@ div.result-container {
.feedback-on-feedback {
height: 100px;
margin-right: 0px;
margin-right: 0;
}
.evaluation-response {
margin-bottom: 2px;
header {
a {
font-size: .85em;
}
}
}
.evaluation-scoring {
.scoring-list {
list-style-type: none;
margin-left: 3px;
list-style-type: none;
li {
display:inline;
margin-left: 0;
&:first-child {
margin-left: 0px;
margin-left: 0;
}
display:inline;
margin-left: 0px;
label {
font-size: .9em;
......@@ -169,20 +351,23 @@ div.result-container {
}
}
}
.submit-message-container {
margin: 10px 0px ;
margin: $baseline/2 0;
}
.external-grader-message {
margin-bottom: 5px;
margin-bottom: $baseline/4;
section {
padding-left: 20px;
background-color: #FAFAFA;
color: #2C2C2C;
padding-left: $baseline;
background-color: #fafafa;
color: #2c2c2c;
font-family: monospace;
font-size: 1em;
padding-top: 10px;
padding-bottom:30px;
padding-top: $baseline/2;
padding-bottom: 30px;
header {
font-size: 1.4em;
}
......@@ -192,35 +377,36 @@ div.result-container {
}
.longform {
padding: 0px;
margin: 0px;
padding: 0;
margin: 0;
.result-errors {
margin: 5px;
padding: 10px 10px 10px 40px;
margin: $baseline/4;
padding: $baseline/2 $baseline/2 $baseline/2 $baseline*2;
background: url('../images/incorrect-icon.png') center left no-repeat;
li {
color: #B00;
}
}
.result-output {
margin: 5px;
padding: 20px 0px 15px 50px;
border-top: 1px solid #DDD;
border-left: 20px solid #FAFAFA;
margin: $baseline/4;
padding: $baseline 0 15px 50px;
border-top: 1px solid #ddd;
border-left: 20px solid #fafafa;
h4 {
font-family: monospace;
font-size: 1em;
font-family: monospace;
}
dl {
margin: 0px;
margin: 0;
}
dt {
margin-top: 20px;
margin-top: $baseline;
}
dd {
......@@ -229,31 +415,74 @@ div.result-container {
}
.markup-text{
margin: 5px;
padding: 20px 0px 15px 50px;
border-top: 1px solid #DDD;
border-left: 20px solid #FAFAFA;
margin: $baseline/4;
padding: $baseline 0 15px 50px;
border-top: 1px solid #ddd;
border-left: 20px solid #fafafa;
bs {
color: #BB0000;
color: #bb0000;
}
bg {
color: #BDA046;
color: #bda046;
}
}
}
}
}
.rubric-result-container {
padding: 2px;
margin: 0;
display: inline;
.rubric-result {
font-size: .9em;
padding: 2px;
display: inline-table;
}
padding: 2px;
margin: 0px;
display : inline;
}
}
div.rubric {
ul.rubric-list{
margin: 0 $baseline $baseline/2 $baseline;
padding: 0;
list-style: none;
list-style-type: none;
li {
&.rubric-list-item {
margin-bottom: 2px;
padding: $baseline/2;
border-radius: $baseline/4;
&:hover {
background-color: #eee;
}
.wrapper-score-selection {
display: table-cell;
padding: 0 $baseline/2;
width: 20px;
vertical-align: middle;
}
.wrappable {
display: table-cell;
padding: $baseline/4;
}
}
}
}
span.rubric-category {
display: block;
width: 100%;
border-bottom: 1px solid lightgray;
font-weight: bold;
font-size: .9em;
}
}
......@@ -261,8 +490,8 @@ div.result-container {
section.open-ended-child {
@media print {
display: block;
width: auto;
padding: 0;
width: auto;
canvas, img {
page-break-inside: avoid;
......@@ -276,24 +505,24 @@ section.open-ended-child {
ol.enumerate {
li {
&:before {
content: " ";
display: block;
height: 0;
visibility: hidden;
height: 0;
content: " ";
}
}
}
.solution-span {
> span {
margin: 20px 0;
position: relative;
display: block;
margin: $baseline 0;
padding: 9px 15px $baseline;
border: 1px solid #ddd;
padding: 9px 15px 20px;
background: #FFF;
position: relative;
box-shadow: inset 0 0 0 1px #eee;
border-radius: 3px;
background: #fff;
box-shadow: inset 0 0 0 1px #eee;
&:empty {
display: none;
......@@ -306,26 +535,26 @@ section.open-ended-child {
margin-top: -2px;
}
&.status {
margin: 8px 0 0 $baseline/2;
text-indent: -9999px;
margin: 8px 0 0 10px;
}
}
div.unanswered {
p.status {
@include inline-block();
background: url('../images/unanswered-icon.png') center center no-repeat;
height: 14px;
width: 14px;
height: 14px;
background: url('../images/unanswered-icon.png') center center no-repeat;
}
}
div.correct, div.ui-icon-check {
p.status {
@include inline-block();
background: url('../images/correct-icon.png') center center no-repeat;
height: 20px;
width: 25px;
height: 20px;
background: url('../images/correct-icon.png') center center no-repeat;
}
input {
......@@ -336,9 +565,9 @@ section.open-ended-child {
div.processing {
p.status {
@include inline-block();
background: url('../images/spinner.gif') center center no-repeat;
height: 20px;
width: 20px;
height: 20px;
background: url('../images/spinner.gif') center center no-repeat;
}
input {
......@@ -349,9 +578,9 @@ section.open-ended-child {
div.incorrect, div.ui-icon-close {
p.status {
@include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px;
width: 20px;
height: 20px;
background: url('../images/incorrect-icon.png') center center no-repeat;
text-indent: -9999px;
}
......@@ -368,7 +597,7 @@ section.open-ended-child {
p.answer {
@include inline-block();
margin-bottom: 0;
margin-left: 10px;
margin-left: $baseline/2;
&:before {
content: "Answer: ";
......@@ -386,96 +615,91 @@ section.open-ended-child {
span {
&.unanswered, &.ui-icon-bullet {
@include inline-block();
background: url('../images/unanswered-icon.png') center center no-repeat;
height: 14px;
position: relative;
top: 4px;
width: 14px;
height: 14px;
background: url('../images/unanswered-icon.png') center center no-repeat;
}
&.processing, &.ui-icon-processing {
@include inline-block();
background: url('../images/spinner.gif') center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px;
height: 20px;
background: url('../images/spinner.gif') center center no-repeat;
}
&.correct, &.ui-icon-check {
@include inline-block();
background: url('../images/correct-icon.png') center center no-repeat;
height: 20px;
position: relative;
top: 6px;
width: 25px;
height: 20px;
background: url('../images/correct-icon.png') center center no-repeat;
}
&.incorrect, &.ui-icon-close {
@include inline-block();
background: url('../images/incorrect-icon.png') center center no-repeat;
height: 20px;
width: 20px;
position: relative;
top: 6px;
width: 20px;
height: 20px;
background: url('../images/incorrect-icon.png') center center no-repeat;
}
}
.reload
{
.reload {
float:right;
margin: 10px;
margin: $baseline/2;
}
div.short-form-response {
background: #F6F6F6;
border: 1px solid #ddd;
margin-bottom: 0px;
overflow-y: auto;
height: 200px;
@include clearfix;
overflow-y: auto;
margin-bottom: 0;
padding: $baseline/2;
min-height: 20px;
height: auto;
border: 1px solid #ddd;
background: #f6f6f6;
}
.grader-status {
padding: 9px;
background: #F6F6F6;
border: 1px solid #ddd;
border-top: 0;
margin-bottom: 20px;
@include clearfix;
margin: $baseline/2 0;
padding: $baseline/2;
border-radius: 5px;
background: #f6f6f6;
span {
text-indent: -9999px;
overflow: hidden;
display: block;
float: left;
overflow: hidden;
margin: -7px 7px 0 0;
text-indent: -9999px;
}
.grading {
background: url('../images/info-icon.png') left center no-repeat;
margin: 0 7px 0 0;
padding-left: 25px;
text-indent: 0px;
margin: 0px 7px 0 0;
background: url('../images/info-icon.png') left center no-repeat;
text-indent: 0;
}
p {
line-height: 20px;
margin-bottom: 0;
float: left;
margin-bottom: 0;
line-height: 20px;
}
&.file {
background: #FFF;
margin-top: 20px;
padding: 20px 0 0 0;
border: {
top: 1px solid #eee;
right: 0;
bottom: 0;
left: 0;
}
margin-top: $baseline;
padding: $baseline 0 0 0;
border: 0;
border-top: 1px solid #eee;
background: #fff;
p.debug {
display: none;
......@@ -485,12 +709,11 @@ section.open-ended-child {
float: left;
}
}
}
form.option-input {
margin: -10px 0 20px;
padding-bottom: 20px;
margin: -$baseline/2 0 $baseline;
padding-bottom: $baseline;
select {
margin-right: flex-gutter();
......@@ -498,29 +721,31 @@ section.open-ended-child {
}
ul {
list-style: disc outside none;
margin-bottom: lh();
margin-left: .75em;
margin-left: .75rem;
}
ul.rubric-list{
margin: 0;
padding: 0;
list-style-type: none;
padding:0;
margin:0;
list-style: none;
li {
&.rubric-list-item{
margin-bottom: 0px;
padding: 0px;
&.rubric-list-item {
margin-bottom: 0;
padding: 0;
border-radius: $baseline/4;
}
}
}
ol {
list-style: decimal outside none;
margin-bottom: lh();
margin-left: .75em;
margin-left: .75rem;
list-style: decimal outside none;
}
dl {
......@@ -541,8 +766,9 @@ section.open-ended-child {
}
li {
margin-bottom: 0px;
padding: 0px;
margin-bottom: 0;
padding: 0;
&:last-child {
margin-bottom: 0;
}
......@@ -553,14 +779,14 @@ section.open-ended-child {
}
hr {
background: #ddd;
border: none;
clear: both;
color: #ddd;
float: none;
height: 1px;
clear: both;
margin: 0 0 .75rem;
width: 100%;
height: 1px;
border: none;
background: #ddd;
color: #ddd;
}
.hidden {
......@@ -574,7 +800,7 @@ section.open-ended-child {
}
section.action {
margin-top: 20px;
margin-top: $baseline;
input.save {
@extend .blue-button !optional;
......@@ -582,20 +808,20 @@ section.open-ended-child {
.submission_feedback {
@include inline-block;
font-style: italic;
margin: 8px 0 0 10px;
margin: 8px 0 0 $baseline/2;
color: #777;
font-style: italic;
-webkit-font-smoothing: antialiased;
}
}
.detailed-solution {
> p:first-child {
font-size: 0.9em;
color: #aaa;
text-transform: uppercase;
font-weight: bold;
font-style: normal;
text-transform: uppercase;
color: #AAA;
font-size: 0.9em;
}
p:last-child {
......@@ -605,45 +831,47 @@ section.open-ended-child {
div.open-ended-alert,
.save_message {
margin-top: $baseline/2;
margin-bottom: $baseline/4;
padding: 8px 12px;
border: 1px solid #EBE8BF;
border: 1px solid #ebe8bf;
border-radius: 3px;
background: #FFFCDD;
background: #fffcdd;
font-size: 0.9em;
margin-top: 10px;
margin-bottom:5px;
}
div.capa_reset {
margin-top: $baseline/2;
margin-bottom: $baseline/2;
padding: 25px;
border: 1px solid $error-red;
background-color: lighten($error-red, 25%);
border-radius: 3px;
background-color: lighten($error-red, 25%);
font-size: 1em;
margin-top: 10px;
margin-bottom: 10px;
}
.capa_reset>h2 {
color: #AA0000;
.capa_reset > h2 {
color: #aa0000;
}
.capa_reset li {
font-size: 0.9em;
}
.assessment-container {
margin: 40px 0px 30px 0px;
.scoring-container
{
p
{
margin: $baseline*2 0px 30px 0px;
.scoring-container {
p {
margin-bottom: 1em;
}
label {
margin: 10px;
padding: 5px;
display: inline-block;
margin: $baseline/2;
padding: $baseline/4;
min-width: 50px;
background-color: #CCC;
background-color: #ccc;
text-size: 1.5em;
}
......@@ -651,10 +879,88 @@ section.open-ended-child {
background: #666;
color: white;
}
input[class='grade-selection'] {
display: none;
}
}
}
div.prompt {
background-color: white;
}
h4 {
padding: $baseline/2 0;
}
}
//OE Tool Area Styling
.oe-tools {
display: inline-block;
width: 100%;
border-radius: 5px;
.oe-tools-label, .oe-tools-scores-label {
display: inline-block;
padding: $baseline/2;
vertical-align: middle;
font-size: 0.8em;
}
.rubric-button {
padding: 8px $baseline/4;
}
.rubric-previous-button {
margin-right: $baseline/4;
}
.rubric-next-button {
margin-left: $baseline/4;
}
.next-step-button {
margin: $baseline/2;
}
.reset-button {
vertical-align: middle;
}
}
// Staff Grading
.problem-list-container {
margin: $baseline/2;
.instructions {
padding-bottom: $baseline/2;
}
}
.staff-grading {
.breadcrumbs {
padding: $baseline/10;
background-color: #f6f6f6;
border-radius: 5px;
margin-bottom: $baseline/2;
}
.prompt-wrapper {
padding-top: $baseline/2;
.meta-info-wrapper {
padding: $baseline/2;
border-radius: $baseline/4;
}
}
}
section.peer-grading-container{
div.peer-grading{
section.calibration-feedback {
padding: 20px;
}
}
}
<section class="course-content">
<section class="xmodule_display xmodule_CombinedOpenEndedModule" data-type="CombinedOpenEnded">
<section id="combined-open-ended" class="combined-open-ended" data-ajax-url="/courses/MITx/6.002x/2012_Fall/modx/i4x://MITx/6.002x/combinedopenended/CombinedOE" data-allow_reset="False" data-state="assessing" data-task-count="2" data-task-number="1">
<h2>Problem 1</h2>
<div class="status-container">
<h4>Status</h4>
<div class="status-elements">
<section id="combined-open-ended-status" class="combined-open-ended-status">
<div class="status-elements">
<section id="combined-open-ended-status" class="combined-open-ended-status">
<div class="statusitem" data-status-number="0">
Step 1 (Problem complete) : 1 / 1
<span class="correct" id="status"></span>
</div>
<div class="statusitem statusitem-current" data-status-number="1">
Step 2 (Being scored) : None / 1
<span class="grading" id="status"></span>
</div>
</section>
</div>
</section>
</div>
</div>
<div class="item-container">
<h4>Problem</h4>
<div class="problem-container">
<div class="item"><section id="openended_open_ended" class="open-ended-child" data-state="assessing" data-child-type="openended"><div class="error"></div>
<div class="item">
<section id="openended_open_ended" class="open-ended-child" data-state="assessing" data-child-type="openended">
<div class="error">
</div>
<div class="prompt">
Some prompt.
</div>
<textarea rows="30" cols="80" name="answer" class="answer short-form-response" id="input_open_ended" disabled="disabled">Test submission. Yaaaaaaay!</textarea><div class="message-wrapper"></div>
<textarea rows="30" cols="80" name="answer" class="answer short-form-response" id="input_open_ended" disabled="disabled">
Test submission. Yaaaaaaay!
</textarea>
<div class="message-wrapper"></div>
<div class="grader-status">
<span class="grading" id="status_open_ended">Submitted for grading.</span>
</div>
<input type="button" value="Submit assessment" class="submit-button" name="show" style="display: none;"><input name="skip" class="skip-button" type="button" value="Skip Post-Assessment" style="display: none;"><div class="open-ended-action"></div>
<input type="button" value="Submit assessment" class="submit-button" name="show" style="display: none;">
<input name="skip" class="skip-button" type="button" value="Skip Post-Assessment" style="display: none;">
<div class="open-ended-action"></div>
<span id="answer_open_ended"></span>
</section></div>
</section>
</div>
</div>
<div class="oe-tools response-tools">
<span class="oe-tools-label"></span>
<input type="button" value="Reset" class="reset-button" name="reset" style="display: none;">
</div>
<input type="button" value="Next Step" class="next-step-button" name="reset" style="display: none;">
</div>
<a name="results">
<div class="result-container">
</div>
</a></section><a name="results">
</a></section><a name="results">
</a><div><a name="results">
</a><a href="https://github.com/MITx/content-mit-6002x/tree/master/combinedopenended/CombinedOE.xml">Edit</a> /
</a>
</section>
<a name="results">
</a>
</section>
<a name="results">
</a>
<div>
<a name="results">
</a>
<a href="https://github.com/MITx/content-mit-6002x/tree/master/combinedopenended/CombinedOE.xml">
Edit
</a> /
<a href="#i4x_MITx_6_002x_combinedopenended_CombinedOE_xqa-modal" onclick="javascript:getlog('i4x_MITx_6_002x_combinedopenended_CombinedOE', {
'location': 'i4x://MITx/6.002x/combinedopenended/CombinedOE',
'xqa_key': 'KUBrWtK3RAaBALLbccHrXeD3RHOpmZ2A',
'category': 'CombinedOpenEndedModule',
'user': 'blah'
})" id="i4x_MITx_6_002x_combinedopenended_CombinedOE_xqa_log">QA</a>
</div>
<div><a href="#i4x_MITx_6_002x_combinedopenended_CombinedOE_debug" id="i4x_MITx_6_002x_combinedopenended_CombinedOE_trig">Staff Debug Info</a></div>
</div>
<div>
<a href="#i4x_MITx_6_002x_combinedopenended_CombinedOE_debug" id="i4x_MITx_6_002x_combinedopenended_CombinedOE_trig">
Staff Debug Info
</a>
</div>
<section id="i4x_MITx_6_002x_combinedopenended_CombinedOE_xqa-modal" class="modal xqa-modal" style="width:80%; left:20%; height:80%; overflow:auto">
<div class="inner-wrapper">
......
<section id="combined-open-ended" class="combined-open-ended" data-location="i4x://test/2323/combinedopenended/b893eedec151441f8644187266ccce00" data-ajax-url="/courses/test/2323/Test2/modx/i4x://test/2323/combinedopenended/b893eedec151441f8644187266ccce00" data-allow_reset="False" data-state="initial" data-task-count="1" data-task-number="1" data-accept-file-upload="False">
<div class="name">
<h2>Open Response Assessment</h2>
<div class="progress-container">
</div>
</div>
<div class="problemwrapper">
<div class="status-bar">
<table class="statustable">
<tbody><tr>
<td class="problemtype-container">
<div class="problemtype">
Open Response
</div>
</td>
<td class="assessments-container">
<div class="assessment-text">
Assessments:
</div>
<div class="status-container">
<div class="status-elements">
<section id="combined-open-ended-status" class="combined-open-ended-status">
<div class="statusitem statusitem-current" data-status-number="0">
Peer
</div>
</section>
</div>
</div>
</td>
</tr>
</tbody></table>
</div>
<div class="item-container">
<div class="visibility-control visibility-control-prompt">
<div class="inner">
</div>
<a href="" class="question-header">Show Prompt</a>
</div>
<div class="problem-container">
<div class="item">
<section id="openended_open_ended" class="open-ended-child" data-state="post_assessment" data-child-type="openended">
<div class="error"></div>
<div class="prompt open" style="display: none;">
<h3>Censorship in the Libraries</h3><p>'All of us can think of a book that we hope none of our children or any other children have taken off the shelf. But if I have the right to remove that book from the shelf -- that work I abhor -- then you also have exactly the same right and so does everyone else. And then we have no books left on the shelf for any of us.' --Katherine Paterson, Author
</p><p>
Write a persuasive essay to a newspaper reflecting your vies on censorship in libraries. Do you believe that certain materials, such as books, music, movies, magazines, etc., should be removed from the shelves if they are found offensive? Support your position with convincing arguments from your own experience, observations, and/or reading.
</p>
</div>
<div class="visibility-control visibility-control-response">
<div class="inner">
</div>
<span class="section-header section-header-response">Response</span>
</div>
<div class="answer short-form-response" id="input_open_ended"></div>
<div class="message-wrapper"></div>
<div class="grader-status">
</div>
<div class="file-upload"></div>
<input type="button" value="Submit post-assessment" class="submit-button" name="show" style="display: none;">
<input name="skip" class="skip-button" type="button" value="Skip Post-Assessment" style="display: none;">
<div class="open-ended-action"></div>
<span id="answer_open_ended"></span>
</section>
</div>
</div>
<div class="oe-tools response-tools">
<span class="oe-tools-label"></span>
<input type="button" value="Reset" class="reset-button" name="reset" style="display: inline-block;">
</div>
</div>
<div class="combined-rubric-container" data-status="shown" data-number="0" style="">
<div class="visibility-control visibility-control-rubric">
<div class="inner">
</div>
<span class="section-header section-header-rubric">Submitted Rubric</span>
</div>
<div class="rubric-header">
<button class="rubric-collapse" href="#">Show Full Rubric</button>
Scored rubric from grader 1
</div>
<div class="rubric">
<span class="rubric-category">
Ideas
</span> <br>
<ul class="rubric-list">
<li class="rubric-list-item">
<div class="rubric-label">
<label class="choicegroup_incorrect wrapper-score-selection"></label>
<span class="wrappable"> 0 points :
Difficult for the reader to discern the main idea. Too brief or too repetitive to establish or maintain a focus.
</span>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 1 points :
Attempts a main idea. Sometimes loses focus or ineffectively displays focus.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 2 points :
Presents a unifying theme or main idea, but may include minor tangents. Stays somewhat focused on topic and task.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 3 points :
Presents a unifying theme or main idea without going off on tangents. Stays completely focused on topic and task.
</span>
</label>
</div>
</li>
</ul>
<span class="rubric-category">
Content
</span> <br>
<ul class="rubric-list">
<li class="rubric-list-item">
<div class="rubric-label">
<label class="choicegroup_incorrect wrapper-score-selection"></label>
<span class="wrappable"> 0 points :
Includes little information with few or no details or unrelated details. Unsuccessful in attempts to explore any facets of the topic.
</span>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 1 points :
Includes little information and few or no details. Explores only one or two facets of the topic.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 2 points :
Includes sufficient information and supporting details. (Details may not be fully developed; ideas may be listed.) Explores some facets of the topic.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 3 points :
Includes in-depth information and exceptional supporting details that are fully developed. Explores all facets of the topic.
</span>
</label>
</div>
</li>
</ul>
<span class="rubric-category">
Organization
</span> <br>
<ul class="rubric-list">
<li class="rubric-list-item">
<div class="rubric-label">
<label class="choicegroup_incorrect wrapper-score-selection"></label>
<span class="wrappable"> 0 points :
Ideas organized illogically, transitions weak, and response difficult to follow.
</span>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 1 points :
Attempts to logically organize ideas. Attempts to progress in an order that enhances meaning, and demonstrates use of transitions.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 2 points :
Ideas organized logically. Progresses in an order that enhances meaning. Includes smooth transitions.
</span>
</label>
</div>
</li>
</ul>
<span class="rubric-category">
Style
</span> <br>
<ul class="rubric-list">
<li class="rubric-list-item">
<div class="rubric-label">
<label class="choicegroup_incorrect wrapper-score-selection"></label>
<span class="wrappable"> 0 points :
Contains limited vocabulary, with many words used incorrectly. Demonstrates problems with sentence patterns.
</span>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 1 points :
Contains basic vocabulary, with words that are predictable and common. Contains mostly simple sentences (although there may be an attempt at more varied sentence patterns).
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 2 points :
Includes vocabulary to make explanations detailed and precise. Includes varied sentence patterns, including complex sentences.
</span>
</label>
</div>
</li>
</ul>
<span class="rubric-category">
Voice
</span> <br>
<ul class="rubric-list">
<li class="rubric-list-item">
<div class="rubric-label">
<label class="choicegroup_incorrect wrapper-score-selection"></label>
<span class="wrappable"> 0 points :
Demonstrates language and tone that may be inappropriate to task and reader.
</span>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 1 points :
Demonstrates an attempt to adjust language and tone to task and reader.
</span>
</label>
</div>
</li>
<li class="rubric-list-item rubric-info-item" style="display: none;">
<div class="rubric-label">
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> 2 points :
Demonstrates effective adjustment of language and tone to task and reader.
</span>
</label>
</div>
</li>
</ul>
</div>
<div class="written-feedback">
</div>
</div>
<input type="button" value="Next Step" class="next-step-button" name="reset" style="display: none;">
<section class="legend-container">
</section>
<div class="result-container">
</div>
</div>
</section>
\ No newline at end of file
describe 'Rubric', ->
beforeEach ->
spyOn Logger, 'log'
# load up some fixtures
loadFixtures 'rubric.html'
jasmine.Clock.useMock()
@element = $('.combined-open-ended')
@location = @element.data('location')
describe 'constructor', ->
beforeEach ->
@rub = new Rubric @element
it 'rubric should properly grab the element', ->
expect(@rub.el).toEqual @element
describe 'initialize', ->
beforeEach ->
@rub = new Rubric @element
@rub.initialize @location
it 'rubric correctly sets location', ->
expect($(@rub.rubric_sel).data('location')).toEqual @location
it 'rubric correctly read', ->
expect(@rub.categories.length).toEqual 5
describe 'CombinedOpenEnded', ->
beforeEach ->
spyOn Logger, 'log'
......@@ -13,7 +40,7 @@ describe 'CombinedOpenEnded', ->
@combined = new CombinedOpenEnded @element
it 'set the element', ->
expect(@combined.element).toEqual @element
expect(@combined.el).toEqual @element
it 'get the correct values from data fields', ->
expect(@combined.ajax_url).toEqual '/courses/MITx/6.002x/2012_Fall/modx/i4x://MITx/6.002x/combinedopenended/CombinedOE'
......@@ -77,7 +104,7 @@ describe 'CombinedOpenEnded', ->
@combined.child_state = 'done'
@combined.rebind()
expect(@combined.answer_area.attr("disabled")).toBe("disabled")
expect(@combined.next_problem).toHaveBeenCalled()
expect(@combined.next_problem_button).toBe(":visible")
describe 'next_problem', ->
beforeEach ->
......@@ -109,3 +136,5 @@ describe 'CombinedOpenEnded', ->
class @Rubric
constructor: () ->
@initialize: (location) ->
$('.rubric').data("location", location)
$('input[class="score-selection"]').change @tracking_callback
rubric_category_sel: '.rubric-category'
rubric_sel: '.rubric'
constructor: (el) ->
@el = el
initialize: (location) =>
@$(@rubric_sel).data("location", location)
@$('input[class="score-selection"]').change @tracking_callback
# set up the hotkeys
$(window).unbind('keydown', @keypress_callback)
$(window).keydown @keypress_callback
# display the 'current' carat
@categories = $('.rubric-category')
@category = $(@categories.first())
@category.prepend('> ')
@categories = @$(@rubric_category_sel)
@category = @$(@categories.first())
@category_index = 0
# locally scoped jquery.
$: (selector) ->
$(selector, @el)
@keypress_callback: (event) =>
keypress_callback: (event) =>
# don't try to do this when user is typing in a text input
if $(event.target).is('input, textarea')
if @$(event.target).is('input, textarea')
return
# for when we select via top row
if event.which >= 48 and event.which <= 57
......@@ -31,124 +38,158 @@ class @Rubric
# if we actually have a current category (not past the end)
if(@category_index <= @categories.length)
# find the valid selections for this category
inputs = $("input[name='score-selection-#{@category_index}']")
inputs = @$("input[name='score-selection-#{@category_index}']")
max_score = inputs.length - 1
if selected > max_score or selected < 0
return
inputs.filter("input[value=#{selected}]").click()
# move to the next category
old_category_text = @category.html().substring(5)
@category.html(old_category_text)
@category_index++
@category = $(@categories[@category_index])
@category.prepend('> ')
@category = @$(@categories[@category_index])
@tracking_callback: (event) ->
target_selection = $(event.target).val()
tracking_callback: (event) =>
target_selection = @$(event.target).val()
# chop off the beginning of the name so that we can get the number of the category
category = $(event.target).data("category")
location = $('.rubric').data('location')
category = @$(event.target).data("category")
location = @$(@rubric_sel).data('location')
# probably want the original problem location as well
data = {location: location, selection: target_selection, category: category}
Logger.log 'rubric_select', data
# finds the scores for each rubric category
@get_score_list: () =>
get_score_list: () =>
# find the number of categories:
num_categories = $('.rubric-category').length
num_categories = @$(@rubric_category_sel).length
score_lst = []
# get the score for each one
for i in [0..(num_categories-1)]
score = $("input[name='score-selection-#{i}']:checked").val()
score = @$("input[name='score-selection-#{i}']:checked").val()
score_lst.push(score)
return score_lst
@get_total_score: () ->
get_total_score: () =>
score_lst = @get_score_list()
tot = 0
for score in score_lst
tot += parseInt(score)
return tot
@check_complete: () ->
check_complete: () =>
# check to see whether or not any categories have not been scored
num_categories = $('.rubric-category').length
num_categories = @$(@rubric_category_sel).length
for i in [0..(num_categories-1)]
score = $("input[name='score-selection-#{i}']:checked").val()
score = @$("input[name='score-selection-#{i}']:checked").val()
if score == undefined
return false
return true
class @CombinedOpenEnded
constructor: (element) ->
@element=element
@reinitialize(element)
wrapper_sel: 'section.xmodule_CombinedOpenEndedModule'
coe_sel: 'section.combined-open-ended'
reset_button_sel: '.reset-button'
next_step_sel: '.next-step-button'
question_header_sel: '.question-header'
submit_evaluation_sel: '.submit-evaluation-button'
result_container_sel: 'div.result-container'
combined_rubric_sel: '.combined-rubric-container'
open_ended_child_sel: 'section.open-ended-child'
error_sel: '.error'
answer_area_sel: 'textarea.answer'
answer_area_div_sel : 'div.answer'
prompt_sel: '.prompt'
rubric_wrapper_sel: '.rubric-wrapper'
hint_wrapper_sel: '.hint-wrapper'
message_wrapper_sel: '.message-wrapper'
submit_button_sel: '.submit-button'
skip_button_sel: '.skip-button'
file_upload_sel: '.file-upload'
file_upload_box_sel: '.file-upload-box'
file_upload_preview_sel: '.file-upload-preview'
fof_sel: 'textarea.feedback-on-feedback'
sub_id_sel: 'input.submission_id'
grader_id_sel: 'input.grader_id'
grader_status_sel: '.grader-status'
info_rubric_elements_sel: '.rubric-info-item'
rubric_collapse_sel: '.rubric-collapse'
next_rubric_sel: '.rubric-next-button'
previous_rubric_sel: '.rubric-previous-button'
oe_alert_sel: '.open-ended-alert'
constructor: (el) ->
@el=el
@$el = $(el)
@reinitialize(el)
$(window).keydown @keydown_handler
$(window).keyup @keyup_handler
reinitialize: (element) ->
@wrapper=$(element).find('section.xmodule_CombinedOpenEndedModule')
@el = $(element).find('section.combined-open-ended')
@combined_open_ended=$(element).find('section.combined-open-ended')
@id = @el.data('id')
@ajax_url = @el.data('ajax-url')
@state = @el.data('state')
@task_count = @el.data('task-count')
@task_number = @el.data('task-number')
@accept_file_upload = @el.data('accept-file-upload')
@location = @el.data('location')
# locally scoped jquery.
$: (selector) ->
$(selector, @el)
reinitialize: () ->
@has_been_reset = false
@wrapper=@$(@wrapper_sel)
@coe = @$(@coe_sel)
@ajax_url = @coe.data('ajax-url')
@get_html()
@coe = @$(@coe_sel)
#Get data from combinedopenended
@allow_reset = @coe.data('allow_reset')
@id = @coe.data('id')
@state = @coe.data('state')
@task_count = @coe.data('task-count')
@task_number = @coe.data('task-number')
@accept_file_upload = @coe.data('accept-file-upload')
@location = @coe.data('location')
# set up handlers for click tracking
Rubric.initialize(@location)
@rub = new Rubric(@coe)
@rub.initialize(@location)
@is_ctrl = false
@allow_reset = @el.data('allow_reset')
@reset_button = @$('.reset-button')
#Setup reset
@reset_button = @$(@reset_button_sel)
@reset_button.click @reset
@next_problem_button = @$('.next-step-button')
#Setup next problem
@next_problem_button = @$(@next_step_sel)
@next_problem_button.click @next_problem
@status_container = @$('.status-elements')
@show_results_button=@$('.show-results-button')
@show_results_button.click @show_results
@question_header = @$('.question-header')
@question_header = @$(@question_header_sel)
@question_header.click @collapse_question
# valid states: 'initial', 'assessing', 'post_assessment', 'done'
Collapsible.setCollapsibles(@el)
@submit_evaluation_button = $('.submit-evaluation-button')
Collapsible.setCollapsibles(@$el)
@submit_evaluation_button = @$(@submit_evaluation_sel)
@submit_evaluation_button.click @message_post
@results_container = $('.result-container')
@combined_rubric_container = $('.combined-rubric-container')
@legend_container= $('.legend-container')
@show_legend_current()
@results_container = @$(@result_container_sel)
@combined_rubric_container = @$(@combined_rubric_sel)
# Where to put the rubric once we load it
@el = $(element).find('section.open-ended-child')
@errors_area = @$('.error')
@answer_area = @$('textarea.answer')
@prompt_container = @$('.prompt')
@rubric_wrapper = @$('.rubric-wrapper')
@hint_wrapper = @$('.hint-wrapper')
@message_wrapper = @$('.message-wrapper')
@submit_button = @$('.submit-button')
@child_state = @el.data('state')
@child_type = @el.data('child-type')
@oe = @$(@open_ended_child_sel)
@errors_area = @$(@oe).find(@error_sel)
@answer_area = @$(@oe).find(@answer_area_sel)
@prompt_container = @$(@oe).find(@prompt_sel)
@rubric_wrapper = @$(@oe).find(@rubric_wrapper_sel)
@hint_wrapper = @$(@oe).find(@hint_wrapper_sel)
@message_wrapper = @$(@oe).find(@message_wrapper_sel)
@submit_button = @$(@oe).find(@submit_button_sel)
@child_state = @oe.data('state')
@child_type = @oe.data('child-type')
if @child_type=="openended"
@skip_button = @$('.skip-button')
@skip_button = @$(@oe).find(@skip_button_sel)
@skip_button.click @skip_post_assessment
@file_upload_area = @$('.file-upload')
@file_upload_area = @$(@oe).find(@file_upload_sel)
@can_upload_files = false
@open_ended_child= @$('.open-ended-child')
@open_ended_child= @$(@oe).find(@open_ended_child_sel)
@out_of_sync_message = 'The problem state got out of sync. Try reloading the page.'
......@@ -162,71 +203,43 @@ class @CombinedOpenEnded
@rebind()
if @task_number>1
@show_combined_rubric_current()
@show_results_current()
get_html_callback: (response) =>
@coe.replaceWith(response.html)
# locally scoped jquery.
$: (selector) ->
$(selector, @el)
show_results_current: () =>
data = {'task_number' : @task_number-1}
$.postWithPrefix "#{@ajax_url}/get_results", data, (response) =>
if response.success
@results_container.after(response.html).remove()
@results_container = $('div.result-container')
@submit_evaluation_button = $('.submit-evaluation-button')
@submit_evaluation_button.click @message_post
Collapsible.setCollapsibles(@results_container)
# make sure we still have click tracking
$('.evaluation-response a').click @log_feedback_click
$('input[name="evaluation-score"]').change @log_feedback_selection
show_results: (event) =>
status_item = $(event.target).parent()
status_number = status_item.data('status-number')
data = {'task_number' : status_number}
$.postWithPrefix "#{@ajax_url}/get_results", data, (response) =>
if response.success
@results_container.after(response.html).remove()
@results_container = $('div.result-container')
@submit_evaluation_button = $('.submit-evaluation-button')
@submit_evaluation_button.click @message_post
Collapsible.setCollapsibles(@results_container)
else
@gentle_alert response.error
get_html: () =>
url = "#{@ajax_url}/get_html"
$.ajaxWithPrefix({
type: 'POST',
url: url,
data: {},
success: @get_html_callback,
async:false
});
show_combined_rubric_current: () =>
data = {}
$.postWithPrefix "#{@ajax_url}/get_combined_rubric", data, (response) =>
if response.success
@combined_rubric_container.after(response.html).remove()
@combined_rubric_container= $('div.combined_rubric_container')
show_status_current: () =>
data = {}
$.postWithPrefix "#{@ajax_url}/get_status", data, (response) =>
if response.success
@status_container.after(response.html).remove()
@status_container= $('.status-elements')
show_legend_current: () =>
data = {}
$.postWithPrefix "#{@ajax_url}/get_legend", data, (response) =>
if response.success
@legend_container.after(response.html).remove()
@legend_container= $('.legend-container')
@combined_rubric_container= @$(@combined_rubric_sel)
@toggle_rubric("")
@rubric_collapse = @$(@rubric_collapse_sel)
@rubric_collapse.click @toggle_rubric
@hide_rubrics()
@$(@previous_rubric_sel).click @previous_rubric
@$(@next_rubric_sel).click @next_rubric
if response.hide_reset
@reset_button.hide()
message_post: (event)=>
external_grader_message=$(event.target).parent().parent().parent()
evaluation_scoring = $(event.target).parent()
fd = new FormData()
feedback = evaluation_scoring.find('textarea.feedback-on-feedback')[0].value
submission_id = external_grader_message.find('input.submission_id')[0].value
grader_id = external_grader_message.find('input.grader_id')[0].value
score = evaluation_scoring.find("input:radio[name='evaluation-score']:checked").val()
feedback = @$(evaluation_scoring).find(@fof_sel)[0].value
submission_id = @$(external_grader_message).find(@sub_id_sel)[0].value
grader_id = @$(external_grader_message).find(@grader_id_sel)[0].value
score = @$(evaluation_scoring).find("input:radio[name='evaluation-score']:checked").val()
fd.append('feedback', feedback)
fd.append('submission_id', submission_id)
......@@ -244,7 +257,7 @@ class @CombinedOpenEnded
contentType: false
success: (response) =>
@gentle_alert response.msg
$('section.evaluation').slideToggle()
@$('section.evaluation').slideToggle()
@message_wrapper.html(response.message_html)
......@@ -256,11 +269,9 @@ class @CombinedOpenEnded
@submit_button.unbind('click')
@submit_button.show()
@reset_button.hide()
@next_problem_button.hide()
@hide_file_upload()
@next_problem_button.hide()
@hint_area.attr('disabled', false)
if @task_number>1 or @child_state!='initial'
@show_status_current()
if @task_number==1 and @child_state=='assessing'
@prompt_hide()
......@@ -269,12 +280,14 @@ class @CombinedOpenEnded
if @child_type=="openended"
@skip_button.hide()
if @allow_reset=="True"
@show_results_current
@show_combined_rubric_current()
@reset_button.show()
@submit_button.hide()
@answer_area.attr("disabled", true)
@replace_text_inputs()
@hint_area.attr('disabled', true)
if @task_number<@task_count
@gentle_alert "Your score did not meet the criteria to move to the next step."
else if @child_state == 'initial'
@answer_area.attr("disabled", false)
@submit_button.prop('value', 'Submit')
......@@ -286,12 +299,14 @@ class @CombinedOpenEnded
@hide_file_upload()
@submit_button.prop('value', 'Submit assessment')
@submit_button.click @save_assessment
@submit_button.attr("disabled",true)
if @child_type == "openended"
@submit_button.hide()
@queueing()
if @task_number==1 and @task_count==1
@grader_status = $('.grader-status')
@grader_status.html("<p>Response submitted for scoring.</p>")
@grader_status = @$(@grader_status_sel)
@grader_status.html("<span class='grading'>Your response has been submitted. Please check back later for your grade.</span> ")
else if @child_type == "selfassessment"
@setup_score_selection()
else if @child_state == 'post_assessment'
if @child_type=="openended"
@skip_button.show()
......@@ -304,6 +319,7 @@ class @CombinedOpenEnded
else
@submit_button.click @message_post
else if @child_state == 'done'
@show_combined_rubric_current()
@rubric_wrapper.hide()
@answer_area.attr("disabled", true)
@replace_text_inputs()
......@@ -312,11 +328,8 @@ class @CombinedOpenEnded
if @child_type=="openended"
@skip_button.hide()
if @task_number<@task_count
@next_problem()
@next_problem_button.show()
else
if @task_number==1 and @task_count==1
@show_combined_rubric_current()
@show_results_current()
@reset_button.show()
......@@ -326,14 +339,32 @@ class @CombinedOpenEnded
find_hint_elements: ->
@hint_area = @$('textarea.post_assessment')
replace_answer: (response) =>
if response.success
@rubric_wrapper.html(response.rubric_html)
@rubric_wrapper.show()
@rub = new Rubric(@coe)
@rub.initialize(@location)
@child_state = 'assessing'
@find_assessment_elements()
@rebind()
answer_area_div = @$(@answer_area_div_sel)
answer_area_div.html(response.student_response)
else
@can_upload_files = pre_can_upload_files
@gentle_alert response.error
save_answer: (event) =>
@submit_button.attr("disabled",true)
@submit_button.hide()
event.preventDefault()
@answer_area.attr("disabled", true)
max_filesize = 2*1000*1000 #2MB
pre_can_upload_files = @can_upload_files
if @child_state == 'initial'
files = ""
if @can_upload_files == true
files = $('.file-upload-box')[0].files[0]
files = @$(@file_upload_box_sel)[0].files[0]
if files != undefined
if files.size > max_filesize
@can_upload_files = false
......@@ -351,21 +382,11 @@ class @CombinedOpenEnded
data: fd
processData: false
contentType: false
async: false
success: (response) =>
if response.success
@rubric_wrapper.html(response.rubric_html)
@rubric_wrapper.show()
Rubric.initialize(@location)
@answer_area.html(response.student_response)
@child_state = 'assessing'
@find_assessment_elements()
@rebind()
else
@can_upload_files = pre_can_upload_files
@gentle_alert response.error
@replace_answer(response)
$.ajaxWithPrefix("#{@ajax_url}/save_answer",settings)
else
@errors_area.html(@out_of_sync_message)
......@@ -373,7 +394,7 @@ class @CombinedOpenEnded
#Previously, responses were submitted when hitting enter. Add in a modifier that ensures that ctrl+enter is needed.
if event.which == 17 && @is_ctrl==false
@is_ctrl=true
else if @is_ctrl==true && event.which == 13 && @child_state == 'assessing' && Rubric.check_complete()
else if @is_ctrl==true && event.which == 13 && @child_state == 'assessing' && @rub.check_complete()
@save_assessment(event)
keyup_handler: (event) =>
......@@ -382,10 +403,12 @@ class @CombinedOpenEnded
@is_ctrl=false
save_assessment: (event) =>
@submit_button.attr("disabled",true)
@submit_button.hide()
event.preventDefault()
if @child_state == 'assessing' && Rubric.check_complete()
checked_assessment = Rubric.get_total_score()
score_list = Rubric.get_score_list()
if @child_state == 'assessing' && @rub.check_complete()
checked_assessment = @rub.get_total_score()
score_list = @rub.get_score_list()
data = {'assessment' : checked_assessment, 'score_list' : score_list}
$.postWithPrefix "#{@ajax_url}/save_assessment", data, (response) =>
if response.success
......@@ -440,9 +463,10 @@ class @CombinedOpenEnded
@hint_wrapper.html('')
@message_wrapper.html('')
@child_state = 'initial'
@combined_open_ended.after(response.html).remove()
@coe.after(response.html).remove()
@allow_reset="False"
@reinitialize(@element)
@has_been_reset = true
@rebind()
@reset_button.hide()
else
......@@ -459,7 +483,7 @@ class @CombinedOpenEnded
@hint_wrapper.html('')
@message_wrapper.html('')
@child_state = 'initial'
@combined_open_ended.after(response.html).remove()
@coe.after(response.html).remove()
@reinitialize(@element)
@rebind()
@next_problem_button.hide()
......@@ -467,18 +491,18 @@ class @CombinedOpenEnded
@gentle_alert "Moved to next step."
else
@gentle_alert "Your score did not meet the criteria to move to the next step."
@show_results_current()
@show_combined_rubric_current()
else
@errors_area.html(response.error)
else
@errors_area.html(@out_of_sync_message)
gentle_alert: (msg) =>
if @el.find('.open-ended-alert').length
@el.find('.open-ended-alert').remove()
if @$el.find(@oe_alert_sel).length
@$el.find(@oe_alert_sel).remove()
alert_elem = "<div class='open-ended-alert'>" + msg + "</div>"
@el.find('.open-ended-action').after(alert_elem)
@el.find('.open-ended-alert').css(opacity: 0).animate(opacity: 1, 700)
@$el.find('.open-ended-action').after(alert_elem)
@$el.find(@oe_alert_sel).css(opacity: 0).animate(opacity: 1, 700)
queueing: =>
if @child_state=="assessing" and @child_type=="openended"
......@@ -500,8 +524,8 @@ class @CombinedOpenEnded
@can_upload_files = true
@file_upload_area.html('<input type="file" class="file-upload-box"><img class="file-upload-preview" src="#" alt="Uploaded image" />')
@file_upload_area.show()
$('.file-upload-preview').hide()
$('.file-upload-box').change @preview_image
@$(@file_upload_preview_sel).hide()
@$(@file_upload_box_sel).change @preview_image
else
@gentle_alert 'File uploads are required for this question, but are not supported in this browser. Try the newest version of google chrome. Alternatively, if you have uploaded the image to the web, you can paste a link to it into the answer box.'
......@@ -519,33 +543,66 @@ class @CombinedOpenEnded
# wrap this so that it can be mocked
reload: ->
location.reload()
@reinitialize()
collapse_question: () =>
collapse_question: (event) =>
@prompt_container.slideToggle()
@prompt_container.toggleClass('open')
if @question_header.text() == "(Hide)"
new_text = "(Show)"
if @question_header.text() == "Hide Prompt"
new_text = "Show Prompt"
Logger.log 'oe_hide_question', {location: @location}
else
Logger.log 'oe_show_question', {location: @location}
new_text = "(Hide)"
new_text = "Hide Prompt"
@question_header.text(new_text)
return false
hide_rubrics: () =>
rubrics = @$(@combined_rubric_sel)
for rub in rubrics
if @$(rub).data('status')=="shown"
@$(rub).show()
else
@$(rub).hide()
next_rubric: =>
@shift_rubric(1)
return false
previous_rubric: =>
@shift_rubric(-1)
return false
shift_rubric: (i) =>
rubrics = @$(@combined_rubric_sel)
number = 0
for rub in rubrics
if @$(rub).data('status')=="shown"
number = @$(rub).data('number')
@$(rub).data('status','hidden')
if i==1 and number < rubrics.length - 1
number = number + i
if i==-1 and number>0
number = number + i
@$(rubrics[number]).data('status', 'shown')
@hide_rubrics()
prompt_show: () =>
if @prompt_container.is(":hidden")==true
@prompt_container.slideToggle()
@prompt_container.toggleClass('open')
@question_header.text("(Hide)")
@question_header.text("Hide Prompt")
prompt_hide: () =>
if @prompt_container.is(":visible")==true
@prompt_container.slideToggle()
@prompt_container.toggleClass('open')
@question_header.text("(Show)")
@question_header.text("Show Prompt")
log_feedback_click: (event) ->
link_text = $(event.target).html()
link_text = @$(event.target).html()
if link_text == 'See full feedback'
Logger.log 'oe_show_full_feedback', {}
else if link_text == 'Respond to Feedback'
......@@ -553,32 +610,44 @@ class @CombinedOpenEnded
else
generated_event_type = link_text.toLowerCase().replace(" ","_")
Logger.log "oe_" + generated_event_type, {}
log_feedback_selection: (event) ->
target_selection = $(event.target).val()
target_selection = @$(event.target).val()
Logger.log 'oe_feedback_response_selected', {value: target_selection}
remove_attribute: (name) =>
if $('.file-upload-preview').attr(name)
$('.file-upload-preview')[0].removeAttribute(name)
if @$(@file_upload_preview_sel).attr(name)
@$(@file_upload_preview_sel)[0].removeAttribute(name)
preview_image: () =>
if $('.file-upload-box')[0].files && $('.file-upload-box')[0].files[0]
if @$(@file_upload_box_sel)[0].files && @$(@file_upload_box_sel)[0].files[0]
reader = new FileReader()
reader.onload = (e) =>
max_dim = 150
@remove_attribute('src')
@remove_attribute('height')
@remove_attribute('width')
$('.file-upload-preview').attr('src', e.target.result)
height_px = $('.file-upload-preview')[0].height
width_px = $('.file-upload-preview')[0].width
@$(@file_upload_preview_sel).attr('src', e.target.result)
height_px = @$(@file_upload_preview_sel)[0].height
width_px = @$(@file_upload_preview_sel)[0].width
scale_factor = 0
if height_px>width_px
scale_factor = height_px/max_dim
else
scale_factor = width_px/max_dim
$('.file-upload-preview')[0].width = width_px/scale_factor
$('.file-upload-preview')[0].height = height_px/scale_factor
$('.file-upload-preview').show()
reader.readAsDataURL($('.file-upload-box')[0].files[0])
@$(@file_upload_preview_sel)[0].width = width_px/scale_factor
@$(@file_upload_preview_sel)[0].height = height_px/scale_factor
@$(@file_upload_preview_sel).show()
reader.readAsDataURL(@$(@file_upload_box_sel)[0].files[0])
toggle_rubric: (event) =>
info_rubric_elements = @$(@info_rubric_elements_sel)
info_rubric_elements.slideToggle()
return false
setup_score_selection: () =>
@$("input[class='score-selection']").change @graded_callback
graded_callback: () =>
if @rub.check_complete()
@submit_button.attr("disabled",false)
@submit_button.show()
......@@ -3,10 +3,20 @@
# Can (and should be) expanded upon when our problem list
# becomes more sophisticated
class @PeerGrading
peer_grading_sel: '.peer-grading'
peer_grading_container_sel: '.peer-grading-container'
error_container_sel: '.error-container'
message_container_sel: '.message-container'
problem_button_sel: '.problem-button'
problem_list_sel: '.problem-list'
progress_bar_sel: '.progress-bar'
constructor: (element) ->
@peer_grading_container = $('.peer-grading')
@el = element
@peer_grading_container = @$(@peer_grading_sel)
@use_single_location = @peer_grading_container.data('use-single-location')
@peer_grading_outer_container = $('.peer-grading-container')
@peer_grading_outer_container = @$(@peer_grading_container_sel)
@ajax_url = @peer_grading_container.data('ajax-url')
if @use_single_location.toLowerCase() == "true"
......@@ -14,23 +24,27 @@ class @PeerGrading
@activate_problem()
else
#Otherwise, activate the panel view.
@error_container = $('.error-container')
@error_container = @$(@error_container_sel)
@error_container.toggle(not @error_container.is(':empty'))
@message_container = $('.message-container')
@message_container = @$(@message_container_sel)
@message_container.toggle(not @message_container.is(':empty'))
@problem_button = $('.problem-button')
@problem_button = @$(@problem_button_sel)
@problem_button.click @show_results
@problem_list = $('.problem-list')
@problem_list = @$(@problem_list_sel)
@construct_progress_bar()
# locally scoped jquery.
$: (selector) ->
$(selector, @el)
construct_progress_bar: () =>
problems = @problem_list.find('tr').next()
problems.each( (index, element) =>
problem = $(element)
progress_bar = problem.find('.progress-bar')
progress_bar = problem.find(@progress_bar_sel)
bar_value = parseInt(problem.data('graded'))
bar_max = parseInt(problem.data('required')) + bar_value
progress_bar.progressbar({value: bar_value, max: bar_max})
......@@ -43,10 +57,10 @@ class @PeerGrading
if response.success
@peer_grading_outer_container.after(response.html).remove()
backend = new PeerGradingProblemBackend(@ajax_url, false)
new PeerGradingProblem(backend)
new PeerGradingProblem(backend, @el)
else
@gentle_alert response.error
activate_problem: () =>
backend = new PeerGradingProblemBackend(@ajax_url, false)
new PeerGradingProblem(backend)
\ No newline at end of file
new PeerGradingProblem(backend, @el)
\ No newline at end of file
......@@ -158,11 +158,47 @@ class @PeerGradingProblemBackend
return response
class @PeerGradingProblem
constructor: (backend) ->
@prompt_wrapper = $('.prompt-wrapper')
prompt_wrapper_sel: '.prompt-wrapper'
peer_grading_container_sel: '.peer-grading-container'
submission_container_sel: '.submission-container'
prompt_container_sel: '.prompt-container'
rubric_container_sel: '.rubric-container'
flag_student_container_sel: '.flag-student-container'
answer_unknown_container_sel: '.answer-unknown-container'
calibration_panel_sel: '.calibration-panel'
grading_panel_sel: '.grading-panel'
content_panel_sel: '.content-panel'
grading_message_sel: '.grading-message'
question_header_sel: '.question-header'
flag_submission_confirmation_sel: '.flag-submission-confirmation'
flag_submission_confirmation_button_sel: '.flag-submission-confirmation-button'
flag_submission_removal_button_sel: '.flag-submission-removal-button'
grading_wrapper_sel: '.grading-wrapper'
calibration_feedback_sel: '.calibration-feedback'
interstitial_page_sel: '.interstitial-page'
calibration_interstitial_page_sel: '.calibration-interstitial-page'
error_container_sel: '.error-container'
feedback_area_sel: '.feedback-area'
score_selection_container_sel: '.score-selection-container'
rubric_selection_container_sel: '.rubric-selection-container'
submit_button_sel: '.submit-button'
action_button_sel: '.action-button'
calibration_feedback_button_sel: '.calibration-feedback-button'
interstitial_page_button_sel: '.interstitial-page-button'
calibration_interstitial_page_button_sel: '.calibration-interstitial-page-button'
flag_checkbox_sel: '.flag-checkbox'
answer_unknown_checkbox_sel: '.answer-unknown-checkbox'
calibration_text_sel: '.calibration-text'
grading_text_sel: '.grading-text'
calibration_feedback_wrapper_sel: '.calibration-feedback-wrapper'
constructor: (backend, el) ->
@el = el
@prompt_wrapper = $(@prompt_wrapper_sel)
@backend = backend
@is_ctrl = false
@el = $(@peer_grading_container_sel)
# get the location of the problem
@location = $('.peer-grading').data('location')
......@@ -172,57 +208,55 @@ class @PeerGradingProblem
return
# get the other elements we want to fill in
@submission_container = $('.submission-container')
@prompt_container = $('.prompt-container')
@rubric_container = $('.rubric-container')
@flag_student_container = $('.flag-student-container')
@answer_unknown_container = $('.answer-unknown-container')
@calibration_panel = $('.calibration-panel')
@grading_panel = $('.grading-panel')
@content_panel = $('.content-panel')
@grading_message = $('.grading-message')
@submission_container = @$(@submission_container_sel)
@prompt_container = @$(@prompt_container_sel)
@rubric_container = @$(@rubric_container_sel)
@flag_student_container = @$(@flag_student_container_sel)
@answer_unknown_container = @$(@answer_unknown_container_sel)
@calibration_panel = @$(@calibration_panel_sel)
@grading_panel = @$(@grading_panel_sel)
@content_panel = @$(@content_panel_sel)
@grading_message = @$(@grading_message_sel)
@grading_message.hide()
@question_header = $('.question-header')
@question_header = @$(@question_header_sel)
@question_header.click @collapse_question
@flag_submission_confirmation = $('.flag-submission-confirmation')
@flag_submission_confirmation_button = $('.flag-submission-confirmation-button')
@flag_submission_removal_button = $('.flag-submission-removal-button')
@flag_submission_confirmation = @$(@flag_submission_confirmation_sel)
@flag_submission_confirmation_button = @$(@flag_submission_confirmation_button_sel)
@flag_submission_removal_button = @$(@flag_submission_removal_button_sel)
@flag_submission_confirmation_button.click @close_dialog_box
@flag_submission_removal_button.click @remove_flag
@grading_wrapper =$('.grading-wrapper')
@calibration_feedback_panel = $('.calibration-feedback')
@interstitial_page = $('.interstitial-page')
@grading_wrapper = @$(@grading_wrapper_sel)
@calibration_feedback_panel = @$(@calibration_feedback_sel)
@interstitial_page = @$(@interstitial_page_sel)
@interstitial_page.hide()
@calibration_interstitial_page = $('.calibration-interstitial-page')
@calibration_interstitial_page = @$(@calibration_interstitial_page_sel)
@calibration_interstitial_page.hide()
@error_container = $('.error-container')
@error_container = @$(@error_container_sel)
@submission_key_input = $("input[name='submission-key']")
@essay_id_input = $("input[name='essay-id']")
@feedback_area = $('.feedback-area')
@essay_id_input = @$("input[name='essay-id']")
@feedback_area = @$(@feedback_area_sel)
@score_selection_container = $('.score-selection-container')
@rubric_selection_container = $('.rubric-selection-container')
@score_selection_container = @$(@score_selection_container_sel)
@rubric_selection_container = @$(@rubric_selection_container_sel)
@grade = null
@calibration = null
@submit_button = $('.submit-button')
@action_button = $('.action-button')
@calibration_feedback_button = $('.calibration-feedback-button')
@interstitial_page_button = $('.interstitial-page-button')
@calibration_interstitial_page_button = $('.calibration-interstitial-page-button')
@flag_student_checkbox = $('.flag-checkbox')
@answer_unknown_checkbox = $('.answer-unknown-checkbox')
@submit_button = @$(@submit_button_sel)
@action_button = @$(@action_button_sel)
@calibration_feedback_button = @$(@calibration_feedback_button_sel)
@interstitial_page_button = @$(@interstitial_page_button_sel)
@calibration_interstitial_page_button = @$(@calibration_interstitial_page_button_sel)
@flag_student_checkbox = @$(@flag_checkbox_sel)
@answer_unknown_checkbox = @$(@answer_unknown_checkbox_sel)
$(window).keydown @keydown_handler
$(window).keyup @keyup_handler
@collapse_question()
Collapsible.setCollapsibles(@content_panel)
# Set up the click event handlers
......@@ -230,7 +264,7 @@ class @PeerGradingProblem
@calibration_feedback_button.click =>
@calibration_feedback_panel.hide()
@grading_wrapper.show()
@gentle_alert "Calibration essay saved. Fetched the next essay."
@gentle_alert "Calibration essay saved. Fetching the next essay."
@is_calibrated_check()
@interstitial_page_button.click =>
......@@ -251,6 +285,10 @@ class @PeerGradingProblem
@is_calibrated_check()
# locally scoped jquery.
$: (selector) ->
$(selector, @el)
##########
#
......@@ -269,8 +307,8 @@ class @PeerGradingProblem
construct_data: () ->
data =
rubric_scores: Rubric.get_score_list()
score: Rubric.get_total_score()
rubric_scores: @rub.get_score_list()
score: @rub.get_total_score()
location: @location
submission_id: @essay_id_input.val()
submission_key: @submission_key_input.val()
......@@ -282,10 +320,12 @@ class @PeerGradingProblem
submit_calibration_essay: ()=>
data = @construct_data()
@submit_button.hide()
@backend.post('save_calibration_essay', data, @calibration_callback)
submit_grade: () =>
data = @construct_data()
@submit_button.hide()
@backend.post('save_grade', data, @submission_callback)
......@@ -298,13 +338,15 @@ class @PeerGradingProblem
remove_flag: () =>
@flag_student_checkbox.removeAttr("checked")
@close_dialog_box()
@submit_button.attr('disabled', true)
close_dialog_box: () =>
$( ".flag-submission-confirmation" ).dialog('close')
$(@flag_submission_confirmation_sel).dialog('close')
flag_box_checked: () =>
if @flag_student_checkbox.is(':checked')
$( ".flag-submission-confirmation" ).dialog({ height: 400, width: 400 })
@$(@flag_submission_confirmation_sel).dialog({ height: 400, width: 400 })
@submit_button.attr('disabled', false)
# called after we perform an is_student_calibrated check
calibration_check_callback: (response) =>
......@@ -344,7 +386,11 @@ class @PeerGradingProblem
if response.success
@is_calibrated_check()
@grading_message.fadeIn()
@grading_message.html("<p>Successfully saved your feedback. Fetched the next essay.</p>")
message = "<p>Successfully saved your feedback. Fetching the next essay."
if response.required_done
message = message + " You have completed the required number of gradings."
message = message + "</p>"
@grading_message.html(message)
else
if response.error
@render_error(response.error)
......@@ -353,12 +399,16 @@ class @PeerGradingProblem
# called after a grade is selected on the interface
graded_callback: (event) =>
ev = @$(event.target).parent().parent()
ul = ev.parent().parent()
ul.find(".rubric-label-selected").removeClass('rubric-label-selected')
ev.addClass('rubric-label-selected')
# check to see whether or not any categories have not been scored
if Rubric.check_complete()
if @rub.check_complete()
# show button if we have scores for all categories
@grading_message.hide()
@show_submit_button()
@grade = Rubric.get_total_score()
@grade = @rub.get_total_score()
keydown_handler: (event) =>
#Previously, responses were submitted when hitting enter. Add in a modifier that ensures that ctrl+enter is needed.
......@@ -395,18 +445,20 @@ class @PeerGradingProblem
# Display the right text
# both versions of the text are written into the template itself
# we only need to show/hide the correct ones at the correct time
@calibration_panel.find('.calibration-text').show()
@grading_panel.find('.calibration-text').show()
@calibration_panel.find('.grading-text').hide()
@grading_panel.find('.grading-text').hide()
@calibration_panel.find(@calibration_text_sel).show()
@grading_panel.find(@calibration_text_sel).show()
@calibration_panel.find(@grading_text_sel).hide()
@grading_panel.find(@grading_text_sel).hide()
@flag_student_container.hide()
@answer_unknown_container.hide()
@feedback_area.val("")
@submit_button.show()
@submit_button.unbind('click')
@submit_button.click @submit_calibration_essay
@submit_button.attr('disabled', true)
@scroll_to_top()
else if response.error
@render_error(response.error)
else
......@@ -425,16 +477,20 @@ class @PeerGradingProblem
# Display the correct text
# both versions of the text are written into the template itself
# we only need to show/hide the correct ones at the correct time
@calibration_panel.find('.calibration-text').hide()
@grading_panel.find('.calibration-text').hide()
@calibration_panel.find('.grading-text').show()
@grading_panel.find('.grading-text').show()
@calibration_panel.find(@calibration_text_sel).hide()
@grading_panel.find(@calibration_text_sel).hide()
@calibration_panel.find(@grading_text_sel).show()
@grading_panel.find(@grading_text_sel).show()
@flag_student_container.show()
@answer_unknown_container.show()
@feedback_area.val("")
@flag_student_checkbox.removeAttr("checked")
@submit_button.show()
@submit_button.unbind('click')
@submit_button.click @submit_grade
@submit_button.attr('disabled', true)
@scroll_to_top()
else if response.error
@render_error(response.error)
else
......@@ -463,13 +519,14 @@ class @PeerGradingProblem
@submit_button.hide()
@action_button.hide()
@calibration_feedback_panel.hide()
Rubric.initialize(@location)
@rub = new Rubric(@el)
@rub.initialize(@location)
render_calibration_feedback: (response) =>
# display correct grade
@calibration_feedback_panel.slideDown()
calibration_wrapper = $('.calibration-feedback-wrapper')
calibration_wrapper = @$(@calibration_feedback_wrapper_sel)
calibration_wrapper.html("<p>The score you gave was: #{@grade}. The actual score is: #{response.actual_score}</p>")
score = parseInt(@grade)
......@@ -482,11 +539,11 @@ class @PeerGradingProblem
if response.actual_rubric != undefined
calibration_wrapper.append("<div>Instructor Scored Rubric: #{response.actual_rubric}</div>")
if response.actual_feedback!=undefined
if response.actual_feedback.feedback!=undefined
calibration_wrapper.append("<div>Instructor Feedback: #{response.actual_feedback}</div>")
# disable score selection and submission from the grading interface
$("input[name='score-selection']").attr('disabled', true)
@$("input[name='score-selection']").attr('disabled', true)
@submit_button.hide()
@calibration_feedback_button.show()
......@@ -508,23 +565,30 @@ class @PeerGradingProblem
@action_button.show()
show_submit_button: () =>
@submit_button.attr('disabled', false)
@submit_button.show()
setup_score_selection: (max_score) =>
# And now hook up an event handler again
$("input[class='score-selection']").change @graded_callback
@$("input[class='score-selection']").change @graded_callback
gentle_alert: (msg) =>
@grading_message.fadeIn()
@grading_message.html("<p>" + msg + "</p>")
collapse_question: () =>
collapse_question: (event) =>
@prompt_container.slideToggle()
@prompt_container.toggleClass('open')
if @question_header.text() == "(Hide)"
Logger.log 'peer_grading_hide_question', {location: @location}
new_text = "(Show)"
if @question_header.text() == "Hide Prompt"
new_text = "Show Prompt"
Logger.log 'oe_hide_question', {location: @location}
else
Logger.log 'peer_grading_show_question', {location: @location}
new_text = "(Hide)"
Logger.log 'oe_show_question', {location: @location}
new_text = "Hide Prompt"
@question_header.text(new_text)
return false
scroll_to_top: () =>
$('html, body').animate({
scrollTop: $(".peer-grading").offset().top
}, 200)
......@@ -7,7 +7,9 @@ from xmodule.progress import Progress
from xmodule.stringify import stringify_children
import self_assessment_module
import open_ended_module
from functools import partial
from .combined_open_ended_rubric import CombinedOpenEndedRubric, GRADER_TYPE_IMAGE_DICT, HUMAN_GRADER_TYPE, LEGEND_LIST
from peer_grading_service import PeerGradingService, MockPeerGradingService, GradingServiceError
log = logging.getLogger("mitx.courseware")
......@@ -31,8 +33,17 @@ ACCEPT_FILE_UPLOAD = False
TRUE_DICT = ["True", True, "TRUE", "true"]
HUMAN_TASK_TYPE = {
'selfassessment': "Self Assessment",
'openended': "edX Assessment",
'selfassessment': "Self",
'openended': "edX",
'ml_grading.conf' : "AI",
'peer_grading.conf' : "Peer",
}
HUMAN_STATES = {
'intitial': "Not started.",
'assessing': "Being scored.",
'intermediate_done': "Scoring finished.",
'done': "Complete.",
}
# Default value that controls whether or not to skip basic spelling checks in the controller
......@@ -56,7 +67,6 @@ class CombinedOpenEndedV1Module():
ajax actions implemented by combined open ended module are:
'reset' -- resets the whole combined open ended module and returns to the first child moduleresource_string
'next_problem' -- moves to the next child module
'get_results' -- gets results from a given child module
Types of children. Task is synonymous with child module, so each combined open ended module
incorporates multiple children (tasks):
......@@ -106,6 +116,11 @@ class CombinedOpenEndedV1Module():
self.accept_file_upload = instance_state.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
self.skip_basic_checks = instance_state.get('skip_spelling_checks', SKIP_BASIC_CHECKS) in TRUE_DICT
if system.open_ended_grading_interface:
self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system)
else:
self.peer_gs = MockPeerGradingService()
self.required_peer_grading = instance_state.get('required_peer_grading', 3)
self.peer_grader_count = instance_state.get('peer_grader_count', 3)
self.min_to_calibrate = instance_state.get('min_to_calibrate', 3)
......@@ -300,6 +315,7 @@ class CombinedOpenEndedV1Module():
'accept_file_upload': self.accept_file_upload,
'location': self.location,
'legend_list': LEGEND_LIST,
'human_state': HUMAN_STATES.get(self.state,"Not started.")
}
return context
......@@ -334,6 +350,14 @@ class CombinedOpenEndedV1Module():
self.update_task_states()
return self.current_task.get_html(self.system)
def get_html_ajax(self, data):
"""
Get HTML in AJAX callback
data - Needed to preserve AJAX structure
Output: Dictionary with html attribute
"""
return {'html': self.get_html()}
def get_current_attributes(self, task_number):
"""
Gets the min and max score to attempt attributes of the specified task.
......@@ -419,6 +443,7 @@ class CombinedOpenEndedV1Module():
grader_type = grader_types[0]
else:
grader_type = "IN"
grader_types = ["IN"]
if grader_type in HUMAN_GRADER_TYPE:
human_grader_name = HUMAN_GRADER_TYPE[grader_type]
......@@ -446,9 +471,22 @@ class CombinedOpenEndedV1Module():
'feedback_dicts': feedback_dicts,
'grader_ids': grader_ids,
'submission_ids': submission_ids,
'success' : True
}
return last_response_dict
def extract_human_name_from_task(self, task_xml):
tree = etree.fromstring(task_xml)
payload = tree.xpath("/openended/openendedparam/grader_payload")
if len(payload)==0:
task_name = "selfassessment"
else:
inner_payload = json.loads(payload[0].text)
task_name = inner_payload['grader_settings']
human_task = HUMAN_TASK_TYPE[task_name]
return human_task
def update_task_states(self):
"""
Updates the task state of the combined open ended module with the task state of the current child module.
......@@ -481,6 +519,51 @@ class CombinedOpenEndedV1Module():
pass
return return_html
def check_if_student_has_done_needed_grading(self):
"""
Checks with the ORA server to see if the student has completed the needed peer grading to be shown their grade.
For example, if a student submits one response, and three peers grade their response, the student
cannot see their grades and feedback unless they reciprocate.
Output:
success - boolean indicator of success
allowed_to_submit - boolean indicator of whether student has done their needed grading or not
error_message - If not success, explains why
"""
student_id = self.system.anonymous_student_id
success = False
allowed_to_submit = True
try:
response = self.peer_gs.get_data_for_location(self.location.url(), student_id)
count_graded = response['count_graded']
count_required = response['count_required']
student_sub_count = response['student_sub_count']
count_available = response['count_available']
success = True
except GradingServiceError:
# This is a dev_facing_error
log.error("Could not contact external open ended graders for location {0} and student {1}".format(
self.location, student_id))
# This is a student_facing_error
error_message = "Could not contact the graders. Please notify course staff."
return success, allowed_to_submit, error_message
except KeyError:
log.error("Invalid response from grading server for location {0} and student {1}".format(self.location, student_id))
error_message = "Received invalid response from the graders. Please notify course staff."
return success, allowed_to_submit, error_message
if count_graded >= count_required or count_available==0:
error_message = ""
return success, allowed_to_submit, error_message
else:
allowed_to_submit = False
# This is a student_facing_error
error_string = ("<h4>Feedback not available yet</h4>"
"<p>You need to peer grade {0} more submissions in order to see your feedback.</p>"
"<p>You have graded responses from {1} students, and {2} students have graded your submissions. </p>"
"<p>You have made {3} submissions.</p>")
error_message = error_string.format(count_required - count_graded, count_graded, count_required,
student_sub_count)
return success, allowed_to_submit, error_message
def get_rubric(self, _data):
"""
Gets the results of a given grader via ajax.
......@@ -488,30 +571,39 @@ class CombinedOpenEndedV1Module():
Output: Dictionary to be rendered via ajax that contains the result html.
"""
all_responses = []
loop_up_to_task = self.current_task_number + 1
for i in xrange(0, loop_up_to_task):
all_responses.append(self.get_last_response(i))
rubric_scores = [all_responses[i]['rubric_scores'] for i in xrange(0, len(all_responses)) if
len(all_responses[i]['rubric_scores']) > 0 and all_responses[i]['grader_types'][
0] in HUMAN_GRADER_TYPE.keys()]
grader_types = [all_responses[i]['grader_types'] for i in xrange(0, len(all_responses)) if
len(all_responses[i]['grader_types']) > 0 and all_responses[i]['grader_types'][
0] in HUMAN_GRADER_TYPE.keys()]
feedback_items = [all_responses[i]['feedback_items'] for i in xrange(0, len(all_responses)) if
len(all_responses[i]['feedback_items']) > 0 and all_responses[i]['grader_types'][
0] in HUMAN_GRADER_TYPE.keys()]
success, can_see_rubric, error = self.check_if_student_has_done_needed_grading()
if not can_see_rubric:
return {'html' : self.system.render_template('{0}/combined_open_ended_hidden_results.html'.format(self.TEMPLATE_DIR), {'error' : error}), 'success' : True, 'hide_reset' : True}
contexts = []
rubric_number = self.current_task_number
if self.ready_to_reset:
rubric_number+=1
response = self.get_last_response(rubric_number)
score_length = len(response['grader_types'])
for z in xrange(score_length):
if response['grader_types'][z] in HUMAN_GRADER_TYPE:
try:
feedback = response['feedback_dicts'][z].get('feedback', '')
except TypeError:
return {'success' : False}
rubric_scores = [[response['rubric_scores'][z]]]
grader_types = [[response['grader_types'][z]]]
feedback_items = [[response['feedback_items'][z]]]
rubric_html = self.rubric_renderer.render_combined_rubric(stringify_children(self.static_data['rubric']),
rubric_scores,
grader_types, feedback_items)
contexts.append({
'result': rubric_html,
'task_name': 'Scored rubric',
'feedback' : feedback
})
response_dict = all_responses[-1]
context = {
'results': rubric_html,
'task_name': 'Scored Rubric',
'class_name': 'combined-rubric-container'
'results': contexts,
}
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
return {'html': html, 'success': True, 'hide_reset' : False}
def get_legend(self, _data):
"""
......@@ -525,59 +617,6 @@ class CombinedOpenEndedV1Module():
html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
def get_results(self, _data):
"""
Gets the results of a given grader via ajax.
Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html.
"""
self.update_task_states()
loop_up_to_task = self.current_task_number + 1
all_responses = []
for i in xrange(0, loop_up_to_task):
all_responses.append(self.get_last_response(i))
context_list = []
for ri in all_responses:
for i in xrange(0, len(ri['rubric_scores'])):
feedback = ri['feedback_dicts'][i].get('feedback', '')
rubric_data = self.rubric_renderer.render_rubric(stringify_children(self.static_data['rubric']),
ri['rubric_scores'][i])
if rubric_data['success']:
rubric_html = rubric_data['html']
else:
rubric_html = ''
context = {
'rubric_html': rubric_html,
'grader_type': ri['grader_type'],
'feedback': feedback,
'grader_id': ri['grader_ids'][i],
'submission_id': ri['submission_ids'][i],
}
context_list.append(context)
feedback_table = self.system.render_template('{0}/open_ended_result_table.html'.format(self.TEMPLATE_DIR), {
'context_list': context_list,
'grader_type_image_dict': GRADER_TYPE_IMAGE_DICT,
'human_grader_types': HUMAN_GRADER_TYPE,
'rows': 50,
'cols': 50,
})
context = {
'results': feedback_table,
'task_name': "Feedback",
'class_name': "result-container",
}
html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True}
def get_status_ajax(self, _data):
"""
Gets the results of a given grader via ajax.
Input: AJAX data dictionary
Output: Dictionary to be rendered via ajax that contains the result html.
"""
html = self.get_status(True)
return {'html': html, 'success': True}
def handle_ajax(self, dispatch, data):
"""
This is called by courseware.module_render, to handle an AJAX call.
......@@ -592,10 +631,11 @@ class CombinedOpenEndedV1Module():
handlers = {
'next_problem': self.next_problem,
'reset': self.reset,
'get_results': self.get_results,
'get_combined_rubric': self.get_rubric,
'get_status': self.get_status_ajax,
'get_legend': self.get_legend,
'get_last_response': self.get_last_response_ajax,
'get_current_state': self.get_current_state,
'get_html': self.get_html_ajax,
}
if dispatch not in handlers:
......@@ -605,6 +645,17 @@ class CombinedOpenEndedV1Module():
d = handlers[dispatch](data)
return json.dumps(d, cls=ComplexEncoder)
def get_current_state(self, data):
return self.get_context()
def get_last_response_ajax(self, data):
"""
Get the last response via ajax callback
data - Needed to preserve ajax callback structure
Output: Last response dictionary
"""
return self.get_last_response(self.current_task_number)
def next_problem(self, _data):
"""
Called via ajax to advance to the next problem.
......@@ -623,10 +674,12 @@ class CombinedOpenEndedV1Module():
if self.state != self.DONE:
if not self.ready_to_reset:
return self.out_of_sync_error(data)
if self.student_attempts >= self.max_attempts-1:
if self.student_attempts==self.max_attempts-1:
self.student_attempts +=1
success, can_reset, error = self.check_if_student_has_done_needed_grading()
if not can_reset:
return {'error': error, 'success': False}
if self.student_attempts >= self.max_attempts - 1:
if self.student_attempts == self.max_attempts - 1:
self.student_attempts += 1
return {
'success': False,
# This is a student_facing_error
......@@ -638,7 +691,7 @@ class CombinedOpenEndedV1Module():
self.student_attempts +=1
self.state = self.INITIAL
self.ready_to_reset = False
for i in xrange(0, len(self.task_xml)):
for i in xrange(len(self.task_xml)):
self.current_task_number = i
self.setup_next_task(reset=True)
self.current_task.reset(self.system)
......@@ -673,9 +726,10 @@ class CombinedOpenEndedV1Module():
Output: The status html to be rendered
"""
status = []
for i in xrange(0, self.current_task_number + 1):
task_data = self.get_last_response(i)
task_data.update({'task_number': i + 1})
for i in xrange(0, len(self.task_xml)):
human_task_name = self.extract_human_name_from_task(self.task_xml[i])
task_data = {'task_number': i + 1, 'human_task' : human_task_name, 'current' : self.current_task_number==i}
status.append(task_data)
context = {
......
......@@ -206,20 +206,49 @@ class CombinedOpenEndedRubric(object):
def render_combined_rubric(self, rubric_xml, scores, score_types, feedback_types):
success, score_tuples = CombinedOpenEndedRubric.reformat_scores_for_rendering(scores, score_types,
feedback_types)
#Get all the categories in the rubric
rubric_categories = self.extract_categories(rubric_xml)
#Get a list of max scores, each entry belonging to a rubric category
max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories)
actual_scores = []
#Get the highest possible score across all categories
max_score = max(max_scores)
for i in xrange(0, len(rubric_categories)):
category = rubric_categories[i]
for j in xrange(0, len(category['options'])):
#Loop through each category
for i, category in enumerate(rubric_categories):
#Loop through each option in the category
for j in xrange(len(category['options'])):
#Intialize empty grader types list
rubric_categories[i]['options'][j]['grader_types'] = []
for tuple in score_tuples:
if tuple[1] == i and tuple[2] == j:
for grader_type in tuple[3]:
#Score tuples are a flat data structure with (category, option, grader_type_list) for selected graders
for tup in score_tuples:
if tup[1] == i and tup[2] == j:
for grader_type in tup[3]:
#Set the rubric grader type to the tuple grader types
rubric_categories[i]['options'][j]['grader_types'].append(grader_type)
#Grab the score and add it to the actual scores. J will be the score for the selected
#grader type
if len(actual_scores)<=i:
#Initialize a new list in the list of lists
actual_scores.append([j])
else:
#If a list in the list of lists for this position exists, append to it
actual_scores[i] += [j]
actual_scores = [sum(i) / len(i) for i in actual_scores]
correct = []
#Define if the student is "correct" (1) "incorrect" (0) or "partially correct" (.5)
for (i, a) in enumerate(actual_scores):
if int(a) == max_scores[i]:
correct.append(1)
elif int(a)==0:
correct.append(0)
else:
correct.append(.5)
html = self.system.render_template('{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR),
{'categories': rubric_categories,
'max_scores': max_scores,
'correct' : correct,
'has_score': True,
'view_only': True,
'max_score': max_score,
......
......@@ -11,6 +11,9 @@ log = logging.getLogger(__name__)
class GradingServiceError(Exception):
"""
Exception for grading service. Shown when Open Response Assessment servers cannot be reached.
"""
pass
......@@ -62,7 +65,6 @@ class GradingService(object):
"""
Make a get request to the grading controller
"""
log.debug(params)
op = lambda: self.session.get(url,
allow_redirects=allow_redirects,
params=params)
......
......@@ -641,6 +641,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
"""
# Once we close the problem, we should not allow students
# to save answers
error_message = ""
closed, msg = self.check_if_closed()
if closed:
return msg
......@@ -650,25 +651,19 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
# add new history element with answer and empty score and hint.
success, data = self.append_image_to_student_answer(data)
error_message = ""
if success:
success, allowed_to_submit, error_message = self.check_if_student_can_submit()
if allowed_to_submit:
data['student_answer'] = OpenEndedModule.sanitize_html(data['student_answer'])
self.new_history_entry(data['student_answer'])
self.send_to_grader(data['student_answer'], system)
self.change_state(self.ASSESSING)
else:
# Error message already defined
success = False
else:
# This is a student_facing_error
error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box."
return {
'success': success,
'error': error_message,
'student_response': data['student_answer']
'student_response': data['student_answer'].replace("\n","<br/>")
}
def update_score(self, data, system):
......@@ -699,12 +694,12 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
score = self.latest_score()
correct = 'correct' if self.is_submission_correct(score) else 'incorrect'
if self.child_state == self.ASSESSING:
eta_string = self.get_eta()
eta_string = "Your response has been submitted. Please check back later for your grade."
else:
post_assessment = ""
correct = ""
previous_answer = self.initial_display
previous_answer = ""
previous_answer = previous_answer.replace("\n","<br/>")
context = {
'prompt': self.child_prompt,
'previous_answer': previous_answer,
......
......@@ -179,10 +179,11 @@ class OpenEndedChild(object):
answer = autolink_html(answer)
cleaner = Cleaner(style=True, links=True, add_nofollow=False, page_structure=True, safe_attrs_only=True,
host_whitelist=open_ended_image_submission.TRUSTED_IMAGE_DOMAINS,
whitelist_tags=set(['embed', 'iframe', 'a', 'img']))
whitelist_tags=set(['embed', 'iframe', 'a', 'img', 'br']))
clean_html = cleaner.clean_html(answer)
clean_html = re.sub(r'</p>$', '', re.sub(r'^<p>', '', clean_html))
except:
clean_html = re.sub("\n","<br/>", clean_html)
except Exception:
clean_html = answer
return clean_html
......@@ -332,7 +333,7 @@ class OpenEndedChild(object):
try:
image_data.seek(0)
image_ok = open_ended_image_submission.run_image_tests(image_data)
except:
except Exception:
log.exception("Could not create image and check it.")
if image_ok:
......@@ -345,7 +346,7 @@ class OpenEndedChild(object):
success, s3_public_url = open_ended_image_submission.upload_to_s3(
image_data, image_key, self.s3_interface
)
except:
except Exception:
log.exception("Could not upload image to S3.")
return success, image_ok, s3_public_url
......@@ -434,38 +435,6 @@ class OpenEndedChild(object):
return success, string
def check_if_student_can_submit(self):
location = self.location_string
student_id = self.system.anonymous_student_id
success = False
allowed_to_submit = True
response = {}
# This is a student_facing_error
error_string = ("You need to peer grade {0} more in order to make another submission. "
"You have graded {1}, and {2} are required. You have made {3} successful peer grading submissions.")
try:
response = self.peer_gs.get_data_for_location(self.location_string, student_id)
count_graded = response['count_graded']
count_required = response['count_required']
student_sub_count = response['student_sub_count']
success = True
except:
# This is a dev_facing_error
log.error("Could not contact external open ended graders for location {0} and student {1}".format(
self.location_string, student_id))
# This is a student_facing_error
error_message = "Could not contact the graders. Please notify course staff."
return success, allowed_to_submit, error_message
if count_graded >= count_required:
return success, allowed_to_submit, ""
else:
allowed_to_submit = False
# This is a student_facing_error
error_message = error_string.format(count_required - count_graded, count_graded, count_required,
student_sub_count)
return success, allowed_to_submit, error_message
def get_eta(self):
if self.controller_qs:
response = self.controller_qs.check_for_eta(self.location_string)
......
......@@ -124,4 +124,4 @@ class MockPeerGradingService(object):
]}
def get_data_for_location(self, problem_location, student_id):
return {"version": 1, "count_graded": 3, "count_required": 3, "success": True, "student_sub_count": 1}
return {"version": 1, "count_graded": 3, "count_required": 3, "success": True, "student_sub_count": 1, 'submissions_available' : 0}
......@@ -61,6 +61,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
else:
previous_answer = ''
previous_answer = previous_answer.replace("\n","<br/>")
context = {
'prompt': self.child_prompt,
'previous_answer': previous_answer,
......@@ -184,15 +185,10 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
# add new history element with answer and empty score and hint.
success, data = self.append_image_to_student_answer(data)
if success:
success, allowed_to_submit, error_message = self.check_if_student_can_submit()
if allowed_to_submit:
data['student_answer'] = SelfAssessmentModule.sanitize_html(data['student_answer'])
self.new_history_entry(data['student_answer'])
self.change_state(self.ASSESSING)
else:
# Error message already defined
success = False
else:
# This is a student_facing_error
error_message = "There was a problem saving the image in your submission. Please try a different image, or try pasting a link to an image into the answer box."
......@@ -200,7 +196,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
'success': success,
'rubric_html': self.get_rubric_html(system),
'error': error_message,
'student_response': data['student_answer'],
'student_response': data['student_answer'].replace("\n","<br/>")
}
def save_assessment(self, data, _system):
......@@ -272,8 +268,6 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
try:
rubric_scores = json.loads(latest_post_assessment)
except:
# This is a dev_facing_error
log.error("Cannot parse rubric scores in self assessment module from {0}".format(latest_post_assessment))
rubric_scores = []
return [rubric_scores]
......
......@@ -46,7 +46,6 @@ class PeerGradingFields(object):
)
due = Date(
help="Due date that should be displayed.",
default=None,
scope=Scope.settings)
graceperiod = Timedelta(
help="Amount of grace to give on the due date.",
......@@ -189,9 +188,8 @@ class PeerGradingModule(PeerGradingFields, XModule):
return json.dumps(d, cls=ComplexEncoder)
def query_data_for_location(self):
def query_data_for_location(self, location):
student_id = self.system.anonymous_student_id
location = self.link_to_location
success = False
response = {}
......@@ -229,7 +227,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
count_graded = self.student_data_for_location['count_graded']
count_required = self.student_data_for_location['count_required']
except:
success, response = self.query_data_for_location()
success, response = self.query_data_for_location(self.location)
if not success:
log.exception(
"No instance data found and could not get data from controller for loc {0} student {1}".format(
......@@ -312,17 +310,26 @@ class PeerGradingModule(PeerGradingFields, XModule):
error: if there was an error in the submission, this is the error message
"""
required = set(['location', 'submission_id', 'submission_key', 'score', 'feedback', 'rubric_scores[]', 'submission_flagged', 'answer_unknown'])
success, message = self._check_required(data, required)
required = ['location', 'submission_id', 'submission_key', 'score', 'feedback', 'submission_flagged', 'answer_unknown']
if data.get("submission_flagged", False) in ["false", False, "False", "FALSE"]:
required.append("rubric_scores[]")
success, message = self._check_required(data, set(required))
if not success:
return self._err_response(message)
data_dict = {k:data.get(k) for k in required}
if 'rubric_scores[]' in required:
data_dict['rubric_scores'] = data.getlist('rubric_scores[]')
data_dict['grader_id'] = self.system.anonymous_student_id
try:
response = self.peer_gs.save_grade(**data_dict)
success, location_data = self.query_data_for_location(data_dict['location'])
#Don't check for success above because the response = statement will raise the same Exception as the one
#that will cause success to be false.
response.update({'required_done' : False})
if 'count_graded' in location_data and 'count_required' in location_data and int(location_data['count_graded'])>=int(location_data['count_required']):
response['required_done'] = True
return response
except GradingServiceError:
# This is a dev_facing_error
......@@ -502,7 +509,7 @@ class PeerGradingModule(PeerGradingFields, XModule):
error_text = "Could not get list of problems to peer grade. Please notify course staff."
log.error(error_text)
success = False
except:
except Exception:
log.exception("Could not contact peer grading service.")
success = False
......@@ -513,20 +520,24 @@ class PeerGradingModule(PeerGradingFields, XModule):
'''
try:
return modulestore().get_instance(self.system.course_id, location)
except:
except Exception:
# the linked problem doesn't exist
log.error("Problem {0} does not exist in this course".format(location))
raise
good_problem_list = []
for problem in problem_list:
problem_location = problem['location']
try:
descriptor = _find_corresponding_module_for_location(problem_location)
except Exception:
continue
if descriptor:
problem['due'] = descriptor.lms.due
grace_period = descriptor.lms.graceperiod
try:
problem_timeinfo = TimeInfo(problem['due'], grace_period)
except:
except Exception:
log.error("Malformed due date or grace period string for location {0}".format(problem_location))
raise
if self._closed(problem_timeinfo):
......@@ -537,13 +548,14 @@ class PeerGradingModule(PeerGradingFields, XModule):
# if we can't find the due date, assume that it doesn't have one
problem['due'] = None
problem['closed'] = False
good_problem_list.append(problem)
ajax_url = self.ajax_url
html = self.system.render_template('peer_grading/peer_grading.html', {
'course_id': self.system.course_id,
'ajax_url': ajax_url,
'success': success,
'problem_list': problem_list,
'problem_list': good_problem_list,
'error_text': error_text,
# Checked above
'staff_access': False,
......
......@@ -73,6 +73,7 @@ class OpenEndedChildTest(unittest.TestCase):
def setUp(self):
self.test_system = get_test_system()
self.test_system.open_ended_grading_interface = None
self.openendedchild = OpenEndedChild(self.test_system, self.location,
self.definition, self.descriptor, self.static_data, self.metadata)
......@@ -203,7 +204,7 @@ class OpenEndedModuleTest(unittest.TestCase):
def setUp(self):
self.test_system = get_test_system()
self.test_system.open_ended_grading_interface = None
self.test_system.location = self.location
self.mock_xqueue = MagicMock()
self.mock_xqueue.send_to_queue.return_value = (None, "Message")
......@@ -410,6 +411,7 @@ class CombinedOpenEndedModuleTest(unittest.TestCase):
full_definition = definition_template.format(prompt=prompt, rubric=rubric, task1=task_xml1, task2=task_xml2)
descriptor = Mock(data=full_definition)
test_system = get_test_system()
test_system.open_ended_grading_interface = None
combinedoe_container = CombinedOpenEndedModule(
test_system,
descriptor,
......@@ -536,6 +538,7 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
def setUp(self):
self.test_system = get_test_system()
self.test_system.open_ended_grading_interface = None
self.test_system.xqueue['interface'] = Mock(
send_to_queue=Mock(side_effect=[1, "queued"])
)
......@@ -569,9 +572,9 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
module = self.get_module_from_location(self.problem_location, COURSE)
#Simulate a student saving an answer
module.handle_ajax("save_answer", {"student_answer": self.answer})
status = module.handle_ajax("get_status", {})
self.assertTrue(isinstance(status, basestring))
html = module.handle_ajax("get_html", {})
module.handle_ajax("save_answer", {"student_answer": self.answer, "can_upload_files" : False, "student_file" : None})
html = module.handle_ajax("get_html", {})
#Mock a student submitting an assessment
assessment_dict = MockQueryDict()
......@@ -579,8 +582,7 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
module.handle_ajax("save_assessment", assessment_dict)
task_one_json = json.loads(module.task_states[0])
self.assertEqual(json.loads(task_one_json['child_history'][0]['post_assessment']), assessment)
status = module.handle_ajax("get_status", {})
self.assertTrue(isinstance(status, basestring))
rubric = module.handle_ajax("get_combined_rubric", {})
#Move to the next step in the problem
module.handle_ajax("next_problem", {})
......@@ -617,7 +619,6 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
module.handle_ajax("save_assessment", assessment_dict)
task_one_json = json.loads(module.task_states[0])
self.assertEqual(json.loads(task_one_json['child_history'][0]['post_assessment']), assessment)
module.handle_ajax("get_status", {})
#Move to the next step in the problem
try:
......@@ -660,15 +661,11 @@ class OpenEndedModuleXmlTest(unittest.TestCase, DummyModulestore):
#Get html and other data client will request
module.get_html()
legend = module.handle_ajax("get_legend", {})
self.assertTrue(isinstance(legend, basestring))
module.handle_ajax("get_status", {})
module.handle_ajax("skip_post_assessment", {})
self.assertTrue(isinstance(legend, basestring))
#Get all results
module.handle_ajax("get_results", {})
module.handle_ajax("get_combined_rubric", {})
#reset the problem
module.handle_ajax("reset", {})
......@@ -686,6 +683,7 @@ class OpenEndedModuleXmlAttemptTest(unittest.TestCase, DummyModulestore):
def setUp(self):
self.test_system = get_test_system()
self.test_system.open_ended_grading_interface = None
self.test_system.xqueue['interface'] = Mock(
send_to_queue=Mock(side_effect=[1, "queued"])
)
......@@ -702,8 +700,6 @@ class OpenEndedModuleXmlAttemptTest(unittest.TestCase, DummyModulestore):
#Simulate a student saving an answer
module.handle_ajax("save_answer", {"student_answer": self.answer})
status = module.handle_ajax("get_status", {})
self.assertTrue(isinstance(status, basestring))
#Mock a student submitting an assessment
assessment_dict = MockQueryDict()
......@@ -711,8 +707,6 @@ class OpenEndedModuleXmlAttemptTest(unittest.TestCase, DummyModulestore):
module.handle_ajax("save_assessment", assessment_dict)
task_one_json = json.loads(module.task_states[0])
self.assertEqual(json.loads(task_one_json['child_history'][0]['post_assessment']), assessment)
status = module.handle_ajax("get_status", {})
self.assertTrue(isinstance(status, basestring))
#Move to the next step in the problem
module.handle_ajax("next_problem", {})
......
......@@ -61,7 +61,7 @@ class PeerGradingModuleTest(unittest.TestCase, DummyModulestore):
Try getting data from the external grading service
@return:
"""
success, data = self.peer_grading.query_data_for_location()
success, data = self.peer_grading.query_data_for_location(self.problem_location.url())
self.assertEqual(success, True)
def test_get_score(self):
......
......@@ -93,7 +93,6 @@ def peer_grading_notifications(course, user):
log.info(
"Problem with getting notifications from peer grading service for course {0} user {1}.".format(course_id,
student_id))
if pending_grading:
img_path = "/static/images/grading_notification.png"
......@@ -154,7 +153,7 @@ def combined_notifications(course, user):
last_time_viewed)
notifications = json.loads(controller_response)
if notifications['success']:
if notifications['overall_need_to_check']:
if notifications['staff_needs_to_grade'] or notifications['student_needs_to_peer_grade']:
pending_grading = True
except:
#Non catastrophic error, so no real action
......
......@@ -103,7 +103,6 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
else if cmd == 'save_grade'
console.log("eval: #{data.score} pts, Feedback: #{data.feedback}")
response =
@mock('get_next', {location: data.location})
# get_problem_list
......@@ -147,12 +146,14 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t
class @StaffGrading
grading_message_sel: '.grading-message'
constructor: (backend) ->
AjaxPrefix.addAjaxPrefix(jQuery, -> "")
@backend = backend
# all the jquery selectors
@el = $('.staff-grading')
@problem_list_container = $('.problem-list-container')
@problem_list = $('.problem-list')
......@@ -224,12 +225,12 @@ class @StaffGrading
setup_score_selection: =>
@score_selection_container.html(@rubric)
$('input[class="score-selection"]').change => @graded_callback()
Rubric.initialize(@location)
@rub = new Rubric(@el)
@rub.initialize(@location)
graded_callback: () =>
# show button if we have scores for all categories
if Rubric.check_complete()
if @rub.check_complete()
@state = state_graded
@submit_button.show()
......@@ -237,7 +238,7 @@ class @StaffGrading
#Previously, responses were submitted when hitting enter. Add in a modifier that ensures that ctrl+enter is needed.
if event.which == 17 && @is_ctrl==false
@is_ctrl=true
else if @is_ctrl==true && event.which == 13 && !@list_view && Rubric.check_complete()
else if @is_ctrl==true && event.which == 13 && !@list_view && @rub.check_complete()
@submit_and_get_next()
keyup_handler: (event) =>
......@@ -264,6 +265,7 @@ class @StaffGrading
@error(response.error)
@render_view()
@scroll_to_top()
get_next_submission: (location) ->
@location = location
......@@ -272,13 +274,14 @@ class @StaffGrading
skip_and_get_next: () =>
data =
score: Rubric.get_total_score()
rubric_scores: Rubric.get_score_list()
score: @rub.get_total_score()
rubric_scores: @rub.get_score_list()
feedback: @feedback_area.val()
submission_id: @submission_id
location: @location
skipped: true
submission_flagged: false
@gentle_alert "Skipped the submission."
@backend.post('save_grade', data, @ajax_callback)
get_problem_list: () ->
......@@ -287,15 +290,21 @@ class @StaffGrading
submit_and_get_next: () ->
data =
score: Rubric.get_total_score()
rubric_scores: Rubric.get_score_list()
score: @rub.get_total_score()
rubric_scores: @rub.get_score_list()
feedback: @feedback_area.val()
submission_id: @submission_id
location: @location
submission_flagged: @flag_submission_checkbox.is(':checked')
@gentle_alert "Grades saved. Fetching the next submission to grade."
@backend.post('save_grade', data, @ajax_callback)
gentle_alert: (msg) =>
@grading_message = $(@grading_message_sel)
@grading_message.html("")
@grading_message.fadeIn()
@grading_message.html("<p>" + msg + "</p>")
error: (msg) ->
@error_msg = msg
@state = state_error
......@@ -466,6 +475,15 @@ class @StaffGrading
new_text = "(Hide)"
@question_header.text(new_text)
scroll_to_top: () =>
#This try/catch is needed because jasmine fails with it
try
$('html, body').animate({
scrollTop: $(".staff-grading").offset().top
}, 200)
catch error
console.log("Scrolling error.")
# for now, just create an instance and load it...
......
......@@ -23,7 +23,11 @@
</section>
<section class="rubric-wrapper">
<h3>Rubric</h3>
<div class="visibility-control visibility-control-rubric">
<div class="inner">
</div>
<span class="section-header section-header-rubric">Rubric</span>
</div>
<div class="rubric-container">
</div>
......
.rubric-header {
background-color: #fafafa;
border-radius: 5px;
.rubric-collapse {
margin-right: $baseline/2;
}
}
.button {
display: inline-block;
}
.rubric {
margin: 0px 0px;
margin: 0;
color: #3C3C3C;
tr {
margin:0px 0px;
margin: 0;
height: 100%;
}
td {
height: 100%;
border: 1px black solid;
text-align: center;
}
th {
padding: 5px;
margin: 5px;
margin: $baseline/4;
padding: $baseline/4;
text-align: center;
}
.points-header th {
padding: 0px;
}
.rubric-label
{
.rubric-label {
position: relative;
font-size: .9em;
display: block;
font-size: .9em;
.choicegroup-correct {
//nothing
}
.choicegroup-incorrect {
display:none;
}
}
.grade {
position: absolute;
bottom:0px;
right:0px;
bottom: 0;
right: 0;
}
.selected-grade,
.selected-grade .rubric-label {
background: #666;
color: white;
}
input[type=radio]:checked + .rubric-label {
background: white;
color: $base-font-color;
white-space:nowrap;
}
.wrappable {
white-space:normal;
}
input[class='score-selection'] {
position: relative;
font-size: 16px;
}
ul.rubric-list
{
ul.rubric-list {
margin: 0;
padding: 0;
list-style-type: none;
padding:0;
margin:0;
}
}
div.staff-grading,
div.peer-grading{
border: 1px solid lightgray;
textarea.feedback-area {
margin: 0;
height: 75px;
margin: 0px;
}
ul.rubric-list{
margin: 0;
padding: 0;
list-style-type: none;
padding:0;
margin:0;
li {
&.rubric-list-item{
margin-bottom: 0px;
padding: 0px;
margin-bottom: 0;
padding: 0;
}
}
}
h1 {
margin : 0 0 0 10px;
margin: 0 0 0 $baseline/2;
}
h2{
a
{
h2 {
a {
text-size: .5em;
}
}
div {
margin: 0px;
margin: 0;
&.submission-container{
@include clearfix;
overflow-y: auto;
height: 150px;
background: #F6F6F6;
max-height: 300px;
height: auto;
border: 1px solid #ddd;
@include clearfix;
background: #f6f6f6;
}
}
label {
margin: 0px;
margin: 0;
padding: 2px;
min-width: 50px;
background-color: white;
text-size: 1.5em;
}
......@@ -58,143 +61,161 @@ div.peer-grading{
display: none;
}
.problem-list
{
text-align: center;
.problem-list {
width: 100%;
table-layout: auto;
width:100%;
th
{
text-align: center;
th {
padding: 2px;
}
td
{
padding:2px;
td {
padding: 2px;
}
td.problem-name
{
text-align:left;
td.problem-name {
text-align: left;
}
.ui-progressbar
{
height:1em;
margin:0px;
padding:0px;
.ui-progressbar {
margin: 0;
padding: 0;
height: 1em;
}
}
.prompt-information-container,
.rubric-wrapper,
.calibration-feedback-wrapper,
.grading-container
{
padding: 2px;
.grading-container {
padding: $baseline/2 0;
}
.error-container
{
background-color: #FFCCCC;
.error-container {
margin-left: 0;
padding: 2px;
margin-left: 0px;
background-color: #ffcccc;
}
.submission-wrapper
{
h3
{
.submission-wrapper {
padding: 2px;
padding-bottom: 15px;
h3 {
margin-bottom: 2px;
}
p
{
margin-left:2px;
p {
margin-left: 2px;
}
padding: 2px;
padding-bottom: 15px;
}
.meta-info-wrapper
{
background-color: #eee;
.meta-info-wrapper {
padding:2px;
div
{
display : inline;
background-color: #eee;
div {
display: inline;
}
}
.message-container,
.grading-message
{
background-color: $yellow;
.grading-message {
margin-left: 0;
padding: 2px;
margin-left:0px;
background-color: $yellow;
}
.breadcrumbs
{
margin-top:2px;
margin-left:0px;
margin-bottom:2px;
.breadcrumbs {
margin: $baseline/2 $baseline/4;
font-size: .8em;
}
.instructions-panel
{
.instructions-panel {
@include clearfix;
padding: $baseline/2;
background-color: #eee;
font-size: .8em;
margin-right:2px;
> div
{
padding: 2px;
> div {
margin-bottom: 5px;
padding: $baseline/2;
width: 49%;
background: #eee;
width:47.6%;
h3
{
text-align:center;
text-transform:uppercase;
h3 {
color: #777;
text-align: center;
text-transform: uppercase;
}
p
{
p{
color: #777;
}
}
.calibration-panel
{
float:left;
.calibration-panel {
display: inline-block;
width: 20%;
border-radius: 3px;
}
.grading-panel
{
float:right;
.grading-panel {
display: inline-block;
width: 20%;
border-radius: 3px;
}
.current-state
{
background: #1D9DD9;
h3, p
{
color: white;
.current-state {
background: #fff;
}
}
@include clearfix;
}
.collapsible {
margin-left: 0;
.collapsible
{
margin-left: 0px;
header
{
margin-top:2px;
margin-bottom:2px;
header {
margin-top: 2px;
margin-bottom: 2px;
font-size: 1.2em;
}
}
.interstitial-page
{
.interstitial-page {
text-align: center;
input[type=button]
{
margin-top: 20px;
input[type=button] {
margin-top: $baseline;
}
}
padding: 15px;
border: none;
}
div.peer-grading {
border-radius: $baseline/2;
padding: 0;
.peer-grading-tools {
padding: $baseline;
}
.error-container {
margin: $baseline;
border-radius: $baseline/4;
padding: $baseline/2;
}
.interstitial-page, .calibration -feedback, .calibration-interstitial-page {
padding: $baseline;
}
.prompt-wrapper {
padding: $baseline;
}
.grading-wrapper {
padding: $baseline;
}
}
div.staff-grading {
padding: $baseline;
}
<%! from django.utils.translation import ugettext as _ %>
<section id="combined-open-ended" class="combined-open-ended" data-location="${location}" data-ajax-url="${ajax_url}" data-allow_reset="${allow_reset}" data-state="${state}" data-task-count="${task_count}" data-task-number="${task_number}" data-accept-file-upload = "${accept_file_upload}">
<div class="name">
<h2>${display_name}</h2>
<div class="progress-container">
</div>
</div>
<div class="problemwrapper">
<div class="status-bar">
<table class="statustable">
<tr>
<td class="problemtype-container">
<div class="problemtype">
${_("Open Response")}
</div>
</td>
<td class="assessments-container">
<div class="assessment-text">
${_("Assessments:")}
</div>
<div class="status-container">
${status|n}
</div>
<h2>${display_name}</h2>
</td>
</tr>
</table>
</div>
<div class="item-container">
<h4>Prompt <a href="#" class="question-header">(Hide)</a> </h4>
<div class="visibility-control visibility-control-prompt">
<div class="inner">
</div>
<a href="" class="section-header section-header-prompt question-header">${_("Hide Prompt")}</a>
</div>
<div class="problem-container">
% for item in items:
<div class="item">${item['content'] | n}</div>
% endfor
</div>
<div class="oe-tools response-tools">
<span class="oe-tools-label"></span>
<input type="button" value="${_('Try Again')}" class="reset-button" name="reset"/>
</div>
</div>
<input type="button" value="${_("Reset")}" class="reset-button" name="reset"/>
<input type="button" value="${_("Next Step")}" class="next-step-button" name="reset"/>
<div class="combined-rubric-container">
</div>
<div class="oe-tools problem-tools">
<!--<span class="oe-tools-label">Once you have completed this form of assessment, you may continue. </span>-->
<input type="button" value="${_('Next Step')}" class="next-step-button" name="reset"/>
</div>
<section class="legend-container">
</section>
<div class="combined-rubric-container">
</div>
<div class="result-container">
</div>
</div>
</section>
<div class="combined-rubric-container" data-status="shown" data-number="1">
<div class="visibility-control visibility-control-rubric">
<div class="inner">
</div>
<span class="section-header section-header-rubric">Submitted Rubric</span>
</div>
<div class="written-feedback">
${error}
</div>
</div>
<div class="${class_name}">
<h4>${task_name}</h4>
${results | n}
</div>
<%! from django.utils.translation import ugettext as _ %>
% for (i,result) in enumerate(results):
% if 'task_name' in result and 'result' in result:
<div class="combined-rubric-container"
%if i>0:
data-status="hidden" data-number="${i}">
% else:
data-status="shown" data-number="${i}">
% endif
<div class="visibility-control visibility-control-rubric">
<div class="inner">
</div>
<span class="section-header section-header-rubric">${_("Submitted Rubric")}</span>
</div>
<div class="oe-tools rubric-header">
<span class="oe-tools-label"></span>
<button class="rubric-collapse" href="#">${_("Toggle Full Rubric")}</button>
<span class="oe-tools-scores">
<span class="oe-tools-scores-label"></span>
% if len(results)>1:
<button href="#" alt="Previous" class="rubric-button rubric-previous-button"><i class="icon-chevron-left"></i></button>
% endif
${result['task_name']} from grader ${i+1}
% if len(results)>1:
<button href="#" alt="Next" class="rubric-button rubric-next-button"><i class="icon-chevron-right"></i></button>
% endif
</span>
</div>
${result['result'] | n}
<div class="written-feedback">
${result['feedback'] | n}
</div>
</div>
%endif
% endfor
<%! from django.utils.translation import ugettext as _ %>
<div class="status-elements">
<section id="combined-open-ended-status" class="combined-open-ended-status">
<div class="statusitem">
${_("Status")}
</div>
%for i in xrange(0,len(status_list)):
<%status=status_list[i]%>
%if i==len(status_list)-1:
%if status['current']:
<div class="statusitem statusitem-current" data-status-number="${i}">
%else:
<div class="statusitem" data-status-number="${i}">
%endif
%if status['grader_type'] in grader_type_image_dict and render_via_ajax:
<% grader_image = grader_type_image_dict[status['grader_type']]%>
<img src="${grader_image}" title=${status['human_grader_type']}>
%else:
${status['human_task']}
%endif
(${status['human_state']})
</div>
%endfor
</section>
......
......@@ -4,7 +4,11 @@
<div class="prompt">
${prompt|n}
</div>
<h4>${_("Response")}</h4>
<div class="visibility-control visibility-control-response">
<div class="inner">
</div>
<span class="section-header section-header-response">${_("Response")}</span>
</div>
<textarea rows="${rows}" cols="${cols}" name="answer" class="answer short-form-response" id="input_${id}">${previous_answer|h}</textarea>
<div class="message-wrapper"></div>
......@@ -12,7 +16,7 @@
% if state == 'initial':
<span class="unanswered" style="display:inline-block;" id="status_${id}">${_("Unanswered")}</span>
% elif state == 'assessing':
<span class="grading" id="status_${id}">${_("Submitted for grading.")}
<span class="grading" id="status_${id}">
% if eta_message is not None:
${eta_message}
% endif
......@@ -27,8 +31,8 @@
<div class="file-upload"></div>
<input type="button" value="${_("Submit")}" class="submit-button" name="show"/>
<input name="skip" class="skip-button" type="button" value="${_("Skip Post-Assessment")}"/>
<input type="button" value="${_('Submit')}" class="submit-button" name="show"/>
<input name="skip" class="skip-button" type="button" value="${_('Skip Post-Assessment')}"/>
<div class="open-ended-action"></div>
......
<div class="rubric">
% for i in range(len(categories)):
<% category = categories[i] %>
<span class="rubric-category">${category['description']}</span> <br/>
<span class="rubric-category">${category['description']}</span>
<ul class="rubric-list">
% for j in range(len(category['options'])):
<% option = category['options'][j] %>
%if len(category['options'][j]['grader_types'])>0:
<li class="rubric-list-item">
%else:
<li class="rubric-list-item rubric-info-item">
%endif
<div class="rubric-label">
%for grader_type in category['options'][j]['grader_types']:
% if grader_type in grader_type_image_dict:
<% grader_image = grader_type_image_dict[grader_type] %>
% if grader_type in human_grader_types:
<% human_title = human_grader_types[grader_type] %>
% else:
<% human_title = grader_type %>
% endif
<img src="${grader_image}" title="${human_title}"/>
% endif
%endfor
${option['points']} points : ${option['text']}
%if len(category['options'][j]['grader_types'])>0:
%if correct[i]==1:
<label class="choicegroup_correct wrapper-score-selection"></label>
%elif correct[i]==.5:
<label class="choicegroup_partialcorrect wrapper-score-selection"></label>
%else:
<label class="choicegroup_incorrect wrapper-score-selection"></label>
%endif
<span class="wrappable"> ${option['points']} points : ${option['text']}</span>
</label>
%else:
<label class="rubric-elements-info">
<span class="wrapper-score-selection"> </span>
<span class="wrappable"> ${option['points']} points : ${option['text']}</span>
</label>
%endif
</div>
</li>
% endfor
......
<%! from django.utils.translation import ugettext as _ %>
<% from random import randint %>
<form class="rubric-template" id="inputtype_${id}" xmlns="http://www.w3.org/1999/html">
<h3>${_("Rubric")}</h3>
<p>${_("Select the criteria you feel best represents this submission in each category.")}</p>
<div class="visibility-control visibility-control-rubric">
<div class="inner">
</div>
<span class="section-header section-header-rubric">${_("Rubric")}</span>
</div>
<p>Select the criteria you feel best represents this submission in each category.</p>
<div class="rubric">
% for i in range(len(categories)):
<% category = categories[i] %>
<span class="rubric-category">${category['description']}</span> <br/>
<% m = randint(0,1000) %>
<span class="rubric-category">${category['description']}</span>
<ul class="rubric-list">
% for j in range(len(category['options'])):
<% option = category['options'][j] %>
......@@ -14,8 +20,8 @@
%else:
<li class="rubric-list-item">
% endif
<label class="rubric-label" for="score-${i}-${j}">
<input type="radio" class="score-selection" data-category="${i}" name="score-selection-${i}" id="score-${i}-${j}" value="${option['points']}"/>
<label class="rubric-label" for="score-${i}-${j}-${m}">
<span class="wrapper-score-selection"><input type="radio" class="score-selection" data-category="${i}" name="score-selection-${i}" id="score-${i}-${j}-${m}" value="${option['points']}"/></span>
<span class="wrappable"> ${option['points']} points : ${option['text']}</span>
</label>
</li>
......
......@@ -5,20 +5,22 @@
<div class="prompt">
${prompt}
</div>
<h4>${_("Response")}</h4>
<div class="visibility-control visibility-control-response">
<div class="inner">
</div>
<span class="section-header section-header-response">${_("Response")}</span>
</div>
<div>
<textarea name="answer" class="answer short-form-response" cols="70" rows="20">${previous_answer|n}</textarea>
</div>
<div class="open-ended-action"></div>
<div class="message-wrapper"></div>
<div class="grader-status"></div>
<div class="rubric-wrapper">${initial_rubric}</div>
<div class="hint-wrapper"></div>
<div class="message-wrapper"></div>
<div class="file-upload"></div>
<input type="button" value="${_("Submit")}" class="submit-button" name="show"/>
<input type="button" value="${_('Submit')}" class="submit-button" name="show"/>
<div class="open-ended-action"></div>
<span id="answer_${id}"></span>
</section>
......@@ -19,21 +19,18 @@
<div class="staff-grading" data-ajax_url="${ajax_url}">
<h1>${_("Staff grading")}</h1>
<div class="breadcrumbs">
</div>
<div class="error-container">
</div>
<div class="message-container">
</div>
<div class="breadcrumbs"></div>
<div class="error-container"></div>
<div class="message-container"></div>
<! -- Problem List View -->
<section class="problem-list-container">
<h2>${_("Instructions")}</h2>
<div class="instructions">
<p>${_("This is the list of problems that currently need to be graded in order to train the machine learning models. Each problem needs to be trained separately, and we have indicated the number of student submissions that need to be graded in order for a model to be generated. You can grade more than the minimum required number of submissions--this will improve the accuracy of machine learning, though with diminishing returns. You can see the current accuracy of machine learning while grading.")}</p>
</div>
<h2>${_("Instructions")}</h2>
<div class="instructions">
<p>${_("This is the list of problems that currently need to be graded in order to train the machine learning models. Each problem needs to be trained separately, and we have indicated the number of student submissions that need to be graded in order for a model to be generated. You can grade more than the minimum required number of submissions--this will improve the accuracy of machine learning, though with diminishing returns. You can see the current accuracy of machine learning while grading.")}</p>
</div>
<h2>${_("Problem List")}</h2>
<h2>${_("Problem List")}</h2>
<table class="problem-list">
</table>
</section>
......@@ -41,7 +38,9 @@
<!-- Grading View -->
<section class="prompt-wrapper">
<h2 class="prompt-name"></h2>
<div class="grading-message"></div>
<h2 class="prompt-name">
</h2>
<div class="meta-info-wrapper">
<div class="problem-meta-info-container">
</div>
......@@ -53,7 +52,6 @@
<div class="prompt-container">
</div>
</div>
</section>
<div class="action-button">
......@@ -61,33 +59,31 @@
</div>
<section class="grading-wrapper">
<div class="grading-container">
<div class="submission-wrapper">
<h3>${_("Student Response")}</h3>
<div class="submission-container">
</div>
</div>
<div class="evaluation">
<div class="grading-container">
<div class="submission-wrapper">
<h3>${_("Student Response")}</h3>
<div class="submission-container">
</div>
</div>
<div class="evaluation">
<p class="score-selection-container">
</p>
<p class="grade-selection-container">
</p>
<h3>${_("Written Feedback")}</h3>
<textarea name="feedback" placeholder="${_("Feedback for student (optional)")}"
class="feedback-area" cols="70" ></textarea>
<p>
<textarea name="feedback" placeholder="${_("Feedback for student (optional)")}" class="feedback-area" cols="70" >
</textarea>
<p>
${_("Flag as inappropriate content for later review")} <input class="flag-checkbox" type="checkbox" />
</p>
</div>
<div class="submission">
<input type="button" value="${_("Submit")}" class="submit-button" name="show"/>
<input type="button" value="${_("Skip")}" class="skip-button" name="skip"/>
</div>
</p>
</div>
<div class="submission">
<input type="button" value="${_("Submit")}" class="submit-button" name="show"/>
<input type="button" value="${_("Skip")}" class="skip-button" name="skip"/>
</div>
</div>
</section>
</div>
</div>
</section>
......@@ -2,13 +2,14 @@
<section class="container peer-grading-container">
<div class="peer-grading" data-ajax-url="${ajax_url}" data-use-single-location="${use_single_location}">
<div class="error-container">${error_text}</div>
<h1>${_("Peer Grading")}</h1>
<h2>${_("Instructions")}</h2>
<div class="peer-grading-tools">
<h1 class="peer-grading-title">${_("Peer Grading")}</h1>
<h2 class="peer-grading-instructions">${_("Instructions")}</h2>
<p>${_("Here are a list of problems that need to be peer graded for this course.")}</p>
% if success:
% if len(problem_list) == 0:
<div class="message-container">
${_("Nothing to grade!")}
${_("You currently do not having any peer grading to do. In order to have peer grading to do, you need to have submitted a response to a peer grading problem. The instructor also needs to score the essays that are used to help you better understand the grading criteria.")}
</div>
%else:
<div class="problem-list-container">
......@@ -57,4 +58,5 @@
%endif
%endif
</div>
</div>
</section>
......@@ -14,20 +14,22 @@
</div>
<div class="prompt-wrapper">
<h2>${_('Prompt')} <a href="#" class="question-header">${_('(Hide)')}</a></h2>
<div class="visibility-control visibility-control-prompt">
<div class="inner">
</div>
<a href="" class="section-header section-header-prompt question-header">${_('Hide Prompt')}</a>
</div>
<div class="prompt-information-container">
<section>
<div class="prompt-container">
</div>
</section>
</div>
</div>
<section class="grading-wrapper">
<div class="grading-message">
</div>
<h2>${_("Student Response")}</h2>
<div class="grading-container">
<div class="submission-wrapper">
<h3></h3>
......@@ -38,25 +40,21 @@
</div>
<div class="evaluation">
<p class="rubric-selection-container"></p>
<p class="score-selection-container">
</p>
<p class="score-selection-container"></p>
<h3>${_("Written Feedback")}</h3>
<p>${_("Please include some written feedback as well.")}</p>
<textarea name="feedback" placeholder="Feedback for student"
class="feedback-area" cols="70" ></textarea>
<div class="flag-student-container"> ${_("This submission has explicit or pornographic content : ")}<input type="checkbox" class="flag-checkbox" value="student_is_flagged"> </div>
<div class="answer-unknown-container"> ${_("I do not know how to grade this question : ")}<input type="checkbox" class="answer-unknown-checkbox" value="answer_is_unknown"></div>
<textarea name="feedback" placeholder="Feedback for student" class="feedback-area" cols="70" ></textarea>
<div class="flag-student-container"> ${_("This submission has explicit or pornographic content : ")}
<input type="checkbox" class="flag-checkbox" value="student_is_flagged">
</div>
<div class="answer-unknown-container"> ${_("I do not know how to grade this question : ")}
<input type="checkbox" class="answer-unknown-checkbox" value="answer_is_unknown">
</div>
</div>
<div class="submission">
<input type="button" value="${_("Submit")}" class="submit-button" name="show"/>
</div>
</div>
<div class="grading-message">
</div>
</section>
</section>
<!-- Calibration feedback: Shown after a calibration is sent -->
......@@ -95,7 +93,6 @@
</div>
</section>
<input type="button" value="${_("Go Back")}" class="action-button" name="back" />
</div>
</section>
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