Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx
edx-platform
Commits
1d214731
Commit
1d214731
authored
12 years ago
by
Александр
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-alex-vsepr-mitx'
parents
638d0653
9558629e
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
255 additions
and
5 deletions
+255
-5
common/lib/capa/capa/capa_problem.py
+6
-2
common/lib/capa/capa/chem/chemtools.py
+136
-0
common/lib/capa/capa/inputtypes.py
+53
-2
common/lib/capa/capa/responsetypes.py
+2
-1
common/lib/capa/capa/templates/vsepr_input.html
+58
-0
No files found.
common/lib/capa/capa/capa_problem.py
View file @
1d214731
...
...
@@ -32,6 +32,8 @@ from xml.sax.saxutils import unescape
import
chem
import
chem.chemcalc
import
chem.chemtools
import
calc
from
correctmap
import
CorrectMap
import
eia
...
...
@@ -57,7 +59,8 @@ entry_types = ['textline',
'filesubmission'
,
'javascriptinput'
,
'crystallography'
,
'chemicalequationinput'
,]
'chemicalequationinput'
,
'vsepr_input'
]
# extra things displayed after "show answers" is pressed
solution_types
=
[
'solution'
]
...
...
@@ -77,7 +80,8 @@ global_context = {'random': random,
'scipy'
:
scipy
,
'calc'
:
calc
,
'eia'
:
eia
,
'chemcalc'
:
chem
.
chemcalc
}
'chemcalc'
:
chem
.
chemcalc
,
'chemtools'
:
chem
.
chemtools
}
# These should be removed from HTML output, including all subelements
html_problem_semantics
=
[
"codeparam"
,
"responseparam"
,
"answer"
,
"script"
,
"hintgroup"
]
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/chem/chemtools.py
0 → 100644
View file @
1d214731
from
collections
import
OrderedDict
import
json
import
unittest
def
vsepr_parse_user_answer
(
user_input
):
d
=
OrderedDict
(
json
.
loads
(
user_input
))
d
[
'atoms'
]
=
OrderedDict
(
sorted
(
d
[
'atoms'
]
.
items
()))
return
d
def
vsepr_build_correct_answer
(
geometry
,
atoms
):
correct_answer
=
OrderedDict
()
correct_answer
[
'geometry'
]
=
geometry
correct_answer
[
'atoms'
]
=
OrderedDict
(
sorted
(
atoms
.
items
()))
return
correct_answer
def
vsepr_grade
(
user_input
,
correct_answer
,
ignore_p_order
=
False
,
ignore_a_order
=
False
,
ignore_e_order
=
False
):
""" Flags ignore_(a,p,e)_order are for checking order in axial, perepherial or equatorial positions.
Allowed cases:
c0, a, e
c0, p
Not implemented and not tested cases when p with a or e (no need for now)
"""
# print user_input, type(user_input)
# print correct_answer, type(correct_answer)
if
user_input
[
'geometry'
]
!=
correct_answer
[
'geometry'
]:
return
False
if
user_input
[
'atoms'
][
'c0'
]
!=
correct_answer
[
'atoms'
][
'c0'
]:
return
False
# not order-aware comparisons
for
ignore
in
[(
ignore_p_order
,
'p'
),
(
ignore_e_order
,
'e'
),
(
ignore_a_order
,
'a'
)]:
if
ignore
[
0
]:
# collecting atoms:
a_user
=
[
v
for
k
,
v
in
user_input
[
'atoms'
]
.
items
()
if
k
.
startswith
(
ignore
[
1
])]
a_correct
=
[
v
for
k
,
v
in
correct_answer
[
'atoms'
]
.
items
()
if
k
.
startswith
(
ignore
[
1
])]
# print ignore[0], ignore[1], a_user, a_correct
if
len
(
a_user
)
!=
len
(
a_correct
):
return
False
if
sorted
(
a_user
)
!=
sorted
(
a_correct
):
return
False
# order-aware comparisons
for
ignore
in
[(
ignore_p_order
,
'p'
),
(
ignore_e_order
,
'e'
),
(
ignore_a_order
,
'a'
)]:
if
not
ignore
[
0
]:
# collecting atoms:
a_user
=
[
v
for
k
,
v
in
user_input
[
'atoms'
]
.
items
()
if
k
.
startswith
(
ignore
[
1
])]
a_correct
=
[
v
for
k
,
v
in
correct_answer
[
'atoms'
]
.
items
()
if
k
.
startswith
(
ignore
[
1
])]
# print '2nd', ignore[0], ignore[1], a_user, a_correct
if
len
(
a_user
)
!=
len
(
a_correct
):
return
False
if
len
(
a_correct
)
==
0
:
continue
if
a_user
!=
a_correct
:
return
False
return
True
class
Test_Grade
(
unittest
.
TestCase
):
''' test grade function '''
def
test_incorrect_geometry
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX4E0"
,
atoms
=
{
"c0"
:
"N"
,
"p0"
:
"H"
,
"p1"
:
"(ep)"
,
"p2"
:
"H"
,
"p3"
:
"H"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX3E0","atoms":{"c0":"B","p0":"F","p1":"B","p2":"F"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
))
def
test_incorrect_positions
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX4E0"
,
atoms
=
{
"c0"
:
"N"
,
"p0"
:
"H"
,
"p1"
:
"(ep)"
,
"p2"
:
"H"
,
"p3"
:
"H"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX4E0","atoms":{"c0":"B","p0":"F","p1":"B","p2":"F"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
))
def
test_correct_answer
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX4E0"
,
atoms
=
{
"c0"
:
"N"
,
"p0"
:
"H"
,
"p1"
:
"(ep)"
,
"p2"
:
"H"
,
"p3"
:
"H"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX4E0","atoms":{"c0":"N","p0":"H","p1":"(ep)","p2":"H", "p3":"H"}}'
)
self
.
assertTrue
(
vsepr_grade
(
user_answer
,
correct_answer
))
def
test_incorrect_position_order_p
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX4E0"
,
atoms
=
{
"c0"
:
"N"
,
"p0"
:
"H"
,
"p1"
:
"(ep)"
,
"p2"
:
"H"
,
"p3"
:
"H"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX4E0","atoms":{"c0":"N","p0":"H","p1":"H","p2":"(ep)", "p3":"H"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
))
def
test_correct_position_order_with_ignore_p_order
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX4E0"
,
atoms
=
{
"c0"
:
"N"
,
"p0"
:
"H"
,
"p1"
:
"(ep)"
,
"p2"
:
"H"
,
"p3"
:
"H"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX4E0","atoms":{"c0":"N","p0":"H","p1":"H","p2":"(ep)", "p3":"H"}}'
)
self
.
assertTrue
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_p_order
=
True
))
def
test_incorrect_position_order_ae
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"test"
,
"a1"
:
"(ep)"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"(ep)"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"test","a1":"(ep)","e0":"H","e1":"(ep)","e2":"(ep)","e3":"(ep)"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
))
def
test_correct_position_order_with_ignore_a_order_not_e
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"(ep)"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"test","a1":"(ep)","e0":"H","e1":"H","e2":"(ep)","e3":"(ep)"}}'
)
self
.
assertTrue
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_a_order
=
True
))
def
test_incorrect_position_order_with_ignore_a_order_not_e
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"H"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"test","a1":"(ep)","e0":"H","e1":"H","e2":"(ep)","e3":"H"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_a_order
=
True
))
def
test_correct_position_order_with_ignore_e_order_not_a
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"H"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"(ep)","a1":"test","e0":"H","e1":"H","e2":"(ep)","e3":"H"}}'
)
self
.
assertTrue
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_e_order
=
True
))
def
test_incorrect_position_order_with_ignore_e_order__not_a
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"H"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"test","a1":"(ep)","e0":"H","e1":"H","e2":"(ep)","e3":"H"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_e_order
=
True
))
def
test_correct_position_order_with_ignore_ae_order
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"H"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"Br","a0":"test","a1":"(ep)","e0":"H","e1":"H","e2":"(ep)","e3":"H"}}'
)
self
.
assertTrue
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_e_order
=
True
,
ignore_a_order
=
True
))
def
test_incorrect_c0
(
self
):
correct_answer
=
vsepr_build_correct_answer
(
geometry
=
"AX6E0"
,
atoms
=
{
"c0"
:
"Br"
,
"a0"
:
"(ep)"
,
"a1"
:
"test"
,
"e0"
:
"H"
,
"e1"
:
"H"
,
"e2"
:
"H"
,
"e3"
:
"(ep)"
})
user_answer
=
vsepr_parse_user_answer
(
u'{"geometry":"AX6E0","atoms":{"c0":"H","a0":"test","a1":"(ep)","e0":"H","e1":"H","e2":"(ep)","e3":"H"}}'
)
self
.
assertFalse
(
vsepr_grade
(
user_answer
,
correct_answer
,
ignore_e_order
=
True
,
ignore_a_order
=
True
))
def
suite
():
testcases
=
[
Test_Grade
]
suites
=
[]
for
testcase
in
testcases
:
suites
.
append
(
unittest
.
TestLoader
()
.
loadTestsFromTestCase
(
testcase
))
return
unittest
.
TestSuite
(
suites
)
if
__name__
==
"__main__"
:
unittest
.
TextTestRunner
(
verbosity
=
2
)
.
run
(
suite
())
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/inputtypes.py
View file @
1d214731
...
...
@@ -707,7 +707,7 @@ def imageinput(element, value, status, render_template, msg=''):
_reg
(
imageinput
)
#-----------------------------------------------------------------------------
def
crystallography
(
element
,
value
,
status
,
render_template
,
msg
=
''
):
eid
=
element
.
get
(
'id'
)
if
eid
is
None
:
...
...
@@ -740,18 +740,69 @@ def crystallography(element, value, status, render_template, msg=''):
}
html
=
render_template
(
"crystallography.html"
,
context
)
try
:
xhtml
=
etree
.
XML
(
html
)
except
Exception
as
err
:
# TODO: needs to be self.system.DEBUG - but can't access system
if
True
:
log
.
debug
(
'[inputtypes.
textline
] failed to parse XML for:
\n
%
s'
%
html
)
log
.
debug
(
'[inputtypes.
crystallography
] failed to parse XML for:
\n
%
s'
%
html
)
raise
return
xhtml
_reg
(
crystallography
)
def
vsepr_input
(
element
,
value
,
status
,
render_template
,
msg
=
''
):
eid
=
element
.
get
(
'id'
)
if
eid
is
None
:
msg
=
'cryst has no id: it probably appears outside of a known response type'
msg
+=
"
\n
See problem XML source line
%
s"
%
getattr
(
element
,
'sourceline'
,
'<unavailable>'
)
raise
Exception
(
msg
)
height
=
element
.
get
(
'height'
)
width
=
element
.
get
(
'width'
)
display_file
=
element
.
get
(
'display_file'
)
count
=
int
(
eid
.
split
(
'_'
)[
-
2
])
-
1
# HACK
size
=
element
.
get
(
'size'
)
# if specified, then textline is hidden and id is stored in div of name given by hidden
hidden
=
element
.
get
(
'hidden'
,
''
)
# Escape answers with quotes, so they don't crash the system!
escapedict
=
{
'"'
:
'"'
}
value
=
saxutils
.
escape
(
value
,
escapedict
)
molecules
=
element
.
get
(
'molecules'
)
geometries
=
element
.
get
(
'geometries'
)
context
=
{
'id'
:
eid
,
'value'
:
value
,
'state'
:
status
,
'count'
:
count
,
'size'
:
size
,
'msg'
:
msg
,
'hidden'
:
hidden
,
'inline'
:
element
.
get
(
'inline'
,
''
),
'width'
:
width
,
'height'
:
height
,
'display_file'
:
display_file
,
'molecules'
:
molecules
,
'geometries'
:
geometries
,
}
html
=
render_template
(
"vsepr_input.html"
,
context
)
try
:
xhtml
=
etree
.
XML
(
html
)
except
Exception
as
err
:
# TODO: needs to be self.system.DEBUG - but can't access system
if
True
:
log
.
debug
(
'[inputtypes.vsepr_input] failed to parse XML for:
\n
%
s'
%
html
)
raise
return
xhtml
_reg
(
vsepr_input
)
#--------------------------------------------------------------------------------
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/responsetypes.py
View file @
1d214731
...
...
@@ -867,7 +867,8 @@ def sympy_check2():
</customresponse>"""
}]
response_tag
=
'customresponse'
allowed_inputfields
=
[
'textline'
,
'textbox'
,
'crystallography'
,
'chemicalequationinput'
]
allowed_inputfields
=
[
'textline'
,
'textbox'
,
'crystallography'
,
'chemicalequationinput'
,
'vsepr_input'
]
def
setup_response
(
self
):
xml
=
self
.
xml
...
...
This diff is collapsed.
Click to expand it.
common/lib/capa/capa/templates/vsepr_input.html
0 → 100644
View file @
1d214731
<
%
doinline =
"inline"
if
inline
else
""
%
>
<section
id=
"textinput_${id}"
class=
"textinput ${doinline}"
>
<table><tr><td
height=
'600'
>
<div
id=
"vsepr_div_${id}"
style=
"position:relative;"
data-molecules=
"${molecules}"
data-geometries=
"${geometries}"
>
<canvas
id=
"vsepr_canvas_${id}"
width=
"${width}"
height=
"${height}"
>
</canvas>
</div>
</td><td
valign =
'top'
>
<select
class=
"molecule_select"
id=
"molecule_select_${id}"
size=
"18"
>
</select>
</td></tr></table>
<div
class=
"script_placeholder"
data-src=
"/static/js/vsepr/vsepr.js"
></div>
% if state == 'unsubmitted':
<div
class=
"unanswered ${doinline}"
id=
"status_${id}"
>
% elif state == 'correct':
<div
class=
"correct ${doinline}"
id=
"status_${id}"
>
% elif state == 'incorrect':
<div
class=
"incorrect ${doinline}"
id=
"status_${id}"
>
% elif state == 'incomplete':
<div
class=
"incorrect ${doinline}"
id=
"status_${id}"
>
% endif
% if hidden:
<div
style=
"display:none;"
name=
"${hidden}"
inputid=
"input_${id}"
/>
% endif
<input
type=
"text"
name=
"input_${id}"
id=
"input_${id}"
value=
"${value}"
%
if
size:
size=
"${size}"
%
endif
%
if
hidden:
style=
"display:none;"
%
endif
/>
<p
class=
"status"
>
% if state == 'unsubmitted':
unanswered
% elif state == 'correct':
correct
% elif state == 'incorrect':
incorrect
% elif state == 'incomplete':
incomplete
% endif
</p>
<p
id=
"answer_${id}"
class=
"answer"
></p>
% if msg:
<span
class=
"message"
>
${msg|n}
</span>
% endif
% if state in ['unsubmitted', 'correct', 'incorrect', 'incomplete'] or hidden:
</div>
% endif
</section>
This diff is collapsed.
Click to expand it.
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