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