Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
problem-builder
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
problem-builder
Commits
22e12f6d
Commit
22e12f6d
authored
Apr 10, 2015
by
Jonathan Piacenti
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implemented extended feedback.
parent
29003051
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
133 additions
and
65 deletions
+133
-65
problem_builder/answer.py
+2
-1
problem_builder/mcq.py
+6
-4
problem_builder/mentoring.py
+10
-2
problem_builder/mrq.py
+17
-13
problem_builder/public/css/mentoring.css
+26
-0
problem_builder/public/js/answer.js
+9
-2
problem_builder/public/js/mentoring.js
+1
-1
problem_builder/public/js/mentoring_assessment_view.js
+0
-0
problem_builder/public/js/mentoring_standard_view.js
+23
-14
problem_builder/public/js/questionnaire.js
+14
-11
problem_builder/templates/html/mentoring.html
+6
-1
problem_builder/templates/html/mentoring_grade.html
+3
-0
problem_builder/templates/html/mentoring_review_questions.html
+1
-1
problem_builder/templates/xml/mentoring_assessment.xml
+15
-15
No files found.
problem_builder/answer.py
View file @
22e12f6d
...
...
@@ -179,7 +179,8 @@ class AnswerBlock(AnswerMixin, StepMixin, StudioEditableXBlockMixin, XBlock):
""" Normal view of this XBlock, identical to mentoring_view """
return
self
.
mentoring_view
(
context
)
def
get_results
(
self
):
def
get_results
(
self
,
previous_response
=
None
):
# Previous result is actually stored in database table-- ignore.
return
{
'student_input'
:
self
.
student_input
,
'status'
:
self
.
status
,
...
...
problem_builder/mcq.py
View file @
22e12f6d
...
...
@@ -81,10 +81,12 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
if
submission
in
tip
.
values
:
tips_html
.
append
(
tip
.
render
(
'mentoring_view'
)
.
content
)
formatted_tips
=
None
if
tips_html
:
formatted_tips
=
loader
.
render_template
(
'templates/html/tip_choice_group.html'
,
{
'tips_html'
:
tips_html
,
})
})
self
.
student_choice
=
submission
...
...
@@ -95,13 +97,13 @@ class MCQBlock(SubmittingXBlockMixin, QuestionnaireAbstractBlock):
return
{
'submission'
:
submission
,
'status'
:
'correct'
if
correct
else
'incorrect'
,
'tips'
:
formatted_tips
if
tips_html
else
None
,
'tips'
:
formatted_tips
,
'weight'
:
self
.
weight
,
'score'
:
1
if
correct
else
0
,
}
def
get_results
(
self
):
return
self
.
calculate_results
(
self
.
student_choice
)
def
get_results
(
self
,
previous_result
):
return
self
.
calculate_results
(
previous_result
[
'submission'
]
)
def
submit
(
self
,
submission
):
log
.
debug
(
u'Received MCQ submission: "
%
s"'
,
submission
)
...
...
problem_builder/mentoring.py
View file @
22e12f6d
...
...
@@ -21,6 +21,7 @@
# Imports ###########################################################
import
logging
import
json
from
collections
import
namedtuple
...
...
@@ -432,7 +433,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
# of the whole mentoring block being completed. This is because in standard mode, all children
# must be correct to complete the block. In assessment mode with extended feedback, completion
# happens when you're out of attempts, no matter how you did.
completed
=
choices
[
child
.
name
][
'status'
]
==
'correct'
completed
=
choices
[
child
.
name
][
'status'
]
break
# The 'completed' message should always be shown in this case, since no more attempts are available.
...
...
@@ -510,7 +511,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
})
return
{
'
submitR
esults'
:
submit_results
,
'
r
esults'
:
submit_results
,
'completed'
:
self
.
completed
,
'attempted'
:
self
.
attempted
,
'message'
:
message
,
...
...
@@ -526,6 +527,8 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
steps
=
[
child
for
child
in
children
if
isinstance
(
child
,
StepMixin
)]
# Faster than the self.steps property
assessment_message
=
None
print
children
print
submissions
for
child
in
children
:
if
child
.
name
and
child
.
name
in
submissions
:
submission
=
submissions
[
child
.
name
]
...
...
@@ -565,6 +568,7 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
self
.
num_attempts
+=
1
self
.
completed
=
True
print
current_child
event_data
[
'exercise_id'
]
=
current_child
.
name
event_data
[
'num_attempts'
]
=
self
.
num_attempts
event_data
[
'submitted_answer'
]
=
submissions
...
...
@@ -581,6 +585,10 @@ class MentoringBlock(XBlock, StepParentMixin, StudioEditableXBlockMixin, StudioC
'correct_answer'
:
len
(
score
.
correct
),
'incorrect_answer'
:
len
(
score
.
incorrect
),
'partially_correct_answer'
:
len
(
score
.
partially_correct
),
'correct'
:
self
.
correct_json
(
stringify
=
False
),
'incorrect'
:
self
.
incorrect_json
(
stringify
=
False
),
'partial'
:
self
.
partial_json
(
stringify
=
False
),
'extended_feedback'
:
self
.
show_extended_feedback
()
or
''
,
'assessment_message'
:
assessment_message
,
}
...
...
problem_builder/mrq.py
View file @
22e12f6d
...
...
@@ -81,9 +81,25 @@ class MRQBlock(QuestionnaireAbstractBlock):
return
self
.
_
(
u"Ignored"
)
return
self
.
_
(
u"Not Acceptable"
)
def
get_results
(
self
,
previous_result
):
"""
Get the results a student has already submitted.
"""
result
=
self
.
calculate_results
(
previous_result
[
'submissions'
])
result
[
'completed'
]
=
True
return
result
def
submit
(
self
,
submissions
):
log
.
debug
(
u'Received MRQ submissions: "
%
s"'
,
submissions
)
result
=
self
.
calculate_results
(
submissions
)
self
.
student_choices
=
submissions
log
.
debug
(
u'MRQ submissions result:
%
s'
,
result
)
return
result
def
calculate_results
(
self
,
submissions
):
score
=
0
results
=
[]
for
choice
in
self
.
custom_choices
:
choice_completed
=
True
...
...
@@ -126,18 +142,6 @@ class MRQBlock(QuestionnaireAbstractBlock):
'score'
:
(
float
(
score
)
/
len
(
results
))
if
results
else
0
,
}
def
get_results
(
self
):
return
self
.
calculate_results
(
self
.
student_choices
)
def
submit
(
self
,
submissions
):
log
.
debug
(
u'Received MRQ submissions: "
%
s"'
,
submissions
)
result
=
self
.
calculate_results
(
submissions
)
self
.
student_choices
=
submissions
log
.
debug
(
u'MRQ submissions result:
%
s'
,
result
)
return
result
def
validate_field_data
(
self
,
validation
,
data
):
"""
Validate this block's field data.
...
...
problem_builder/public/css/mentoring.css
View file @
22e12f6d
...
...
@@ -136,3 +136,28 @@
.mentoring
input
[
type
=
"radio"
]
{
margin
:
0
;
}
.mentoring
.review-list
{
list-style
:
none
;
padding-left
:
0
!important
;
margin-left
:
0
;
}
.mentoring
.review-list
li
{
display
:
inline
;
}
.mentoring
.review-list
li
a
{
font-weight
:
bold
;
}
.mentoring
.results-section
{
float
:
left
;
}
.mentoring
.clear
{
display
:
block
;
clear
:
both
;
}
.mentoring
.review-link
{
float
:
right
;
}
\ No newline at end of file
problem_builder/public/js/answer.js
View file @
22e12f6d
...
...
@@ -17,15 +17,22 @@ function AnswerBlock(runtime, element) {
return
$
(
':input'
,
element
).
serializeArray
();
},
handleReview
:
function
(
result
)
{
$
(
'textarea'
,
element
).
prop
(
'disabled'
,
true
);
},
handleSubmit
:
function
(
result
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
var
checkmark
=
$
(
'.answer-checkmark'
,
element
);
$
(
element
).
find
(
'.message'
).
text
((
result
||
{}).
error
||
''
);
this
.
clearResult
();
if
(
this
.
mode
===
'assessment'
)
{
// Display of checkmark would be redundant.
return
}
if
(
result
.
status
===
"correct"
)
{
checkmark
.
addClass
(
'checkmark-correct icon-ok fa-check'
);
}
...
...
problem_builder/public/js/mentoring.js
View file @
22e12f6d
...
...
@@ -60,7 +60,7 @@ function MentoringBlock(runtime, element) {
if
(
typeof
obj
!==
'undefined'
&&
typeof
obj
[
fn
]
==
'function'
)
{
return
obj
[
fn
].
apply
(
obj
,
Array
.
prototype
.
slice
.
call
(
arguments
,
2
));
}
else
{
return
undefined
;
return
null
;
}
}
...
...
problem_builder/public/js/mentoring_assessment_view.js
View file @
22e12f6d
This diff is collapsed.
Click to expand it.
problem_builder/public/js/mentoring_standard_view.js
View file @
22e12f6d
...
...
@@ -4,26 +4,26 @@ function MentoringStandardView(runtime, element, mentoring) {
var
callIfExists
=
mentoring
.
callIfExists
;
function
handleSubmitResults
(
res
ults
)
{
function
handleSubmitResults
(
res
ponse
)
{
messagesDOM
.
empty
().
hide
();
$
.
each
(
res
ults
.
submitResults
||
[],
function
(
index
,
submitResult
)
{
var
input
=
submitResult
[
0
];
var
result
=
submitResult
[
1
];
$
.
each
(
res
ponse
.
results
||
[],
function
(
index
,
result_spec
)
{
var
input
=
result_spec
[
0
];
var
result
=
result_spec
[
1
];
var
child
=
mentoring
.
getChildByName
(
input
);
var
options
=
{
max_attempts
:
res
ults
.
max_attempts
,
num_attempts
:
res
ults
.
num_attempts
max_attempts
:
res
ponse
.
max_attempts
,
num_attempts
:
res
ponse
.
num_attempts
};
callIfExists
(
child
,
'handleSubmit'
,
result
,
options
);
});
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
res
ults
.
max_attempts
);
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
res
ults
.
num_attempts
);
$
(
'.attempts'
,
element
).
data
(
'max_attempts'
,
res
ponse
.
max_attempts
);
$
(
'.attempts'
,
element
).
data
(
'num_attempts'
,
res
ponse
.
num_attempts
);
mentoring
.
renderAttempts
();
// Messages should only be displayed upon hitting 'submit', not on page reload
mentoring
.
setContent
(
messagesDOM
,
res
ults
.
message
);
mentoring
.
setContent
(
messagesDOM
,
res
ponse
.
message
);
if
(
messagesDOM
.
html
().
trim
())
{
messagesDOM
.
prepend
(
'<div class="title1">'
+
gettext
(
'Feedback'
)
+
'</div>'
);
messagesDOM
.
show
();
...
...
@@ -32,23 +32,30 @@ function MentoringStandardView(runtime, element, mentoring) {
submitDOM
.
attr
(
'disabled'
,
'disabled'
);
}
function
submit
()
{
var
success
=
true
;
function
calculate_results
(
handler_name
)
{
var
data
=
{};
var
children
=
mentoring
.
children
;
for
(
var
i
=
0
;
i
<
children
.
length
;
i
++
)
{
var
child
=
children
[
i
];
if
(
child
&&
child
.
name
!==
undefined
&&
typeof
(
child
.
submit
)
!==
"undefined"
)
{
data
[
child
.
name
]
=
child
.
submit
();
if
(
child
&&
child
.
name
!==
undefined
&&
typeof
(
child
[
handler_name
]
)
!==
"undefined"
)
{
data
[
child
.
name
]
=
child
[
handler_name
]
();
}
}
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
'submit'
);
var
handlerUrl
=
runtime
.
handlerUrl
(
element
,
handler_name
);
if
(
submitXHR
)
{
submitXHR
.
abort
();
}
submitXHR
=
$
.
post
(
handlerUrl
,
JSON
.
stringify
(
data
)).
success
(
handleSubmitResults
);
}
function
get_results
()
{
calculate_results
(
'get_results'
);
}
function
submit
()
{
calculate_results
(
'submit'
)
}
function
clearResults
()
{
messagesDOM
.
empty
().
hide
();
...
...
@@ -68,6 +75,8 @@ function MentoringStandardView(runtime, element, mentoring) {
submitDOM
=
$
(
element
).
find
(
'.submit .input-main'
);
submitDOM
.
bind
(
'click'
,
submit
);
submitDOM
.
show
();
// Not used in standard mode.
$
(
element
).
find
(
'.review-link'
).
hide
();
var
options
=
{
onChange
:
onChange
...
...
problem_builder/public/js/questionnaire.js
View file @
22e12f6d
...
...
@@ -97,23 +97,24 @@ function MCQBlock(runtime, element) {
}
},
handleSubmit
:
function
(
result
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
handleReview
:
function
(
result
){
$
(
'.choice input[value="'
+
result
.
submission
+
'"]'
,
element
).
prop
(
'checked'
,
true
);
$
(
'.choice input'
,
element
).
prop
(
'disabled'
,
true
);
},
handleSubmit
:
function
(
result
)
{
mentoring
=
this
.
mentoring
;
var
messageView
=
MessageView
(
element
,
mentoring
);
messageView
.
clearResult
();
var
choiceInputs
=
$
(
'.choice input'
,
element
);
var
choiceInputs
=
$
(
'.choice
-selector
input'
,
element
);
$
.
each
(
choiceInputs
,
function
(
index
,
choiceInput
)
{
var
choiceInputDOM
=
$
(
choiceInput
);
var
choiceDOM
=
choiceInputDOM
.
closest
(
'.choice'
);
var
choiceResultDOM
=
$
(
'.choice-result'
,
choiceDOM
);
var
choiceTipsDOM
=
$
(
'.choice-tips'
,
choiceDOM
);
var
choiceTipsCloseDOM
;
if
(
result
.
status
===
"correct"
&&
choiceInputDOM
.
val
()
===
result
.
submission
)
{
choiceDOM
.
addClass
(
'correct'
);
...
...
@@ -129,7 +130,6 @@ function MCQBlock(runtime, element) {
messageView
.
showMessage
(
choiceTipsDOM
);
}
choiceTipsCloseDOM
=
$
(
'.close'
,
choiceTipsDOM
);
choiceResultDOM
.
off
(
'click'
).
on
(
'click'
,
function
()
{
if
(
choiceTipsDOM
.
html
()
!==
''
)
{
messageView
.
showMessage
(
choiceTipsDOM
);
...
...
@@ -178,9 +178,14 @@ function MRQBlock(runtime, element) {
return
checkedValues
;
},
handleReview
:
function
(
result
)
{
$
.
each
(
result
.
submissions
,
function
(
index
,
value
)
{
$
(
'input[type="checkbox"][value="'
+
value
+
'"]'
).
prop
(
'checked'
,
true
)
});
$
(
'input'
,
element
).
prop
(
'disabled'
,
true
);
},
handleSubmit
:
function
(
result
,
options
)
{
if
(
this
.
mode
===
'assessment'
)
return
;
mentoring
=
this
.
mentoring
;
...
...
@@ -193,14 +198,13 @@ function MRQBlock(runtime, element) {
var
questionnaireDOM
=
$
(
'fieldset.questionnaire'
,
element
);
var
data
=
questionnaireDOM
.
data
();
var
hide_results
=
(
data
.
hide_results
===
'True'
)
?
true
:
false
;
var
hide_results
=
(
data
.
hide_results
===
'True'
);
$
.
each
(
result
.
choices
,
function
(
index
,
choice
)
{
var
choiceInputDOM
=
$
(
'.choice input[value='
+
choice
.
value
+
']'
,
element
);
var
choiceDOM
=
choiceInputDOM
.
closest
(
'.choice'
);
var
choiceResultDOM
=
$
(
'.choice-result'
,
choiceDOM
);
var
choiceTipsDOM
=
$
(
'.choice-tips'
,
choiceDOM
);
var
choiceTipsCloseDOM
;
/* show hint if checked or max_attempts is disabled */
if
(
!
hide_results
&&
...
...
@@ -215,7 +219,6 @@ function MRQBlock(runtime, element) {
mentoring
.
setContent
(
choiceTipsDOM
,
choice
.
tips
);
choiceTipsCloseDOM
=
$
(
'.close'
,
choiceTipsDOM
);
choiceResultDOM
.
off
(
'click'
).
on
(
'click'
,
function
()
{
messageView
.
showMessage
(
choiceTipsDOM
);
});
...
...
problem_builder/templates/html/mentoring.html
View file @
22e12f6d
...
...
@@ -26,7 +26,11 @@
data-partially_correct_answer=
"{{ self.score.4|length }}"
data-max_attempts=
"{{ self.max_attempts }}"
data-num_attempts=
"{{ self.num_attempts }}"
data-assessment_message=
"{{ self.assessment_message }}"
>
data-extended_feedback=
"{%if self.extended_feedback %}True{% endif %}"
data-assessment_message=
"{{ self.assessment_message }}"
data-correct=
"{{ self.correct_json }}"
data-incorrect=
"{{ self.incorrect_json }}"
data-partial=
"{{ self.partial_json }}"
>
</div>
<div
class=
"assessment-messages"
></div>
...
...
@@ -49,4 +53,5 @@
{% endif %}
<div
class=
"messages"
></div>
</div>
<div
class=
"review-link"
><a
href=
"#"
>
Review final grade
</a></div>
</div>
problem_builder/templates/html/mentoring_grade.html
View file @
22e12f6d
...
...
@@ -22,6 +22,7 @@
<
/p
>
<%=
runDetails
(
'correct'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-partially-correct icon-ok fa fa-check"
><
/span
>
<
div
class
=
"results-section"
>
<
p
>
...
...
@@ -35,6 +36,7 @@
<
/p
>
<%=
runDetails
(
'partial'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
<
span
class
=
"assessment-checkmark icon-2x checkmark-incorrect icon-exclamation fa fa-exclamation"
><
/span
>
<
div
class
=
"results-section"
>
<
p
>
...
...
@@ -48,4 +50,5 @@
<
/p
>
<%=
runDetails
(
'incorrect'
)
%>
<
/div
>
<
div
class
=
"clear"
><
/div
>
</script>
problem_builder/templates/html/mentoring_review_questions.html
View file @
22e12f6d
...
...
@@ -2,7 +2,7 @@
<%
var
q
,
last_question
;
%>
<
ul
class
=
"review-list <%= label %>-list"
>
<%
for
(
var
question
in
questions
)
{{
q
=
questions
[
question
];
last_question
=
question
==
questions
.
length
-
1
;
%>
<
li
><
a
href
=
"#"
class
=
"question-link"
data
-
name
=
"<%= q.id %>"
><=
gettext
(
"Question {number}"
,
{
number
:
q
.
number
})
=
><
/a></
li
>
<
li
><
a
href
=
"#"
class
=
"question-link"
data
-
step
=
"<%= q.number %>"
><%=
_
.
template
(
gettext
(
"Question {number}"
),
{
number
:
q
.
number
},
{
interpolate
:
/
\{(
.+
?)\}
/g
})
%
><
/a></
li
>
<%
}}
%>
<
/ul
>
</script>
problem_builder/templates/xml/mentoring_assessment.xml
View file @
22e12f6d
<problem-builder
display_name=
"Mentoring Assessment Example"
weight=
"1"
mode=
"assessment"
max_attempts=
"
10
"
>
<problem-builder
display_name=
"Mentoring Assessment Example"
weight=
"1"
mode=
"assessment"
max_attempts=
"
2"
extended_feedback=
"true
"
>
<html_demo>
<p>
This paragraph is shared between
<strong>
all
</strong>
questions.
</p>
<p>
Please answer the questions below.
</p>
</html_demo>
<pb-answer
name=
"goal"
question=
"What is your goal?"
>
</pb-answer>
<pb-answer
name=
"goal"
question=
"What is your goal?"
/>
<pb-mcq
name=
"mcq_1_1"
question=
"Do you like this MCQ?"
correct_choices=
"yes"
>
<pb-mcq
name=
"mcq_1_1"
question=
"Do you like this MCQ?"
correct_choices=
'["yes"]'
>
<pb-choice
value=
"yes"
>
Yes
</pb-choice>
<pb-choice
value=
"maybenot"
>
Maybe not
</pb-choice>
<pb-choice
value=
"understand"
>
I don't understand
</pb-choice>
<pb-tip
values=
"yes"
>
Great!
</pb-tip>
<pb-tip
values=
"maybenot"
>
Ah, damn.
</pb-tip>
<pb-tip
values=
"understand"
><div
id=
"test-custom-html"
>
Really?
</div></pb-tip>
<pb-tip
values=
'["yes"]'
>
Great!
</pb-tip>
<pb-tip
values=
'["maybenot"]'
>
Ah, damn.
</pb-tip>
<pb-tip
values=
'["understand"]'
><div
id=
"test-custom-html"
>
Really?
</div></pb-tip>
</pb-mcq>
<pb-rating
name=
"mcq_1_2"
low=
"Not good at all"
high=
"Extremely good"
question=
"How much do you rate this MCQ?"
correct_choices=
"4,5"
>
<pb-rating
name=
"mcq_1_2"
low=
"Not good at all"
high=
"Extremely good"
question=
"How much do you rate this MCQ?"
correct_choices=
'["4","5"]'
>
<pb-choice
value=
"notwant"
>
I don't want to rate it
</pb-choice>
<pb-tip
values=
"4,5"
>
I love good grades.
</pb-tip>
<pb-tip
values=
"1,2,3"
>
Will do better next time...
</pb-tip>
<pb-tip
values=
"notwant"
>
Your loss!
</pb-tip>
<pb-tip
values=
'["4","5"]'
>
I love good grades.
</pb-tip>
<pb-tip
values=
'["1","2", "3"]'
>
Will do better next time...
</pb-tip>
<pb-tip
values=
'["notwant"]'
>
Your loss!
</pb-tip>
</pb-rating>
<pb-mrq
name=
"mrq_1_1"
question=
"What do you like in this MRQ?"
required_choices=
"gracefulness,elegance,beauty"
>
<pb-mrq
name=
"mrq_1_1"
question=
"What do you like in this MRQ?"
required_choices=
'["gracefulness","elegance","beauty"]'
>
<pb-choice
value=
"elegance"
>
Its elegance
</pb-choice>
<pb-choice
value=
"beauty"
>
Its beauty
</pb-choice>
<pb-choice
value=
"gracefulness"
>
Its gracefulness
</pb-choice>
<pb-choice
value=
"bugs"
>
Its bugs
</pb-choice>
<pb-tip
values=
"gracefulness"
>
This MRQ is indeed very graceful
</pb-tip>
<pb-tip
values=
"elegance,beauty"
>
This is something everyone has to like about this MRQ
</pb-tip>
<pb-tip
values=
"bugs"
>
Nah, there aren't any!
</pb-tip>
<pb-tip
values=
'["gracefulness"]'
>
This MRQ is indeed very graceful
</pb-tip>
<pb-tip
values=
'["elegance","beauty"]'
>
This is something everyone has to like about this MRQ
</pb-tip>
<pb-tip
values=
'["bugs"]'
>
Nah, there aren't any!
</pb-tip>
</pb-mrq>
<pb-message
type=
"on-assessment-review"
>
<html>
Assessment additional feedback message text
</html>
</pb-message>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment