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
daa6705a
Commit
daa6705a
authored
Jun 07, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Cleaning up pep8 issues, including extraneous imports
parent
d97d2fdc
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
85 additions
and
89 deletions
+85
-89
common/lib/capa/capa_problem.py
+85
-89
No files found.
common/lib/capa/capa_problem.py
View file @
daa6705a
...
...
@@ -12,7 +12,6 @@ import logging
import
math
import
numpy
import
os
import
os.path
import
random
import
re
import
scipy
...
...
@@ -20,58 +19,58 @@ import struct
from
lxml
import
etree
from
lxml.etree
import
Element
from
xml.sax.saxutils
import
escape
,
unescape
from
xml.sax.saxutils
import
unescape
from
util
import
contextualize_text
import
inputtypes
from
responsetypes
import
NumericalResponse
,
FormulaResponse
,
CustomResponse
,
SchematicResponse
,
MultipleChoiceResponse
,
StudentInputError
,
TrueFalseResponse
,
ExternalResponse
,
ImageResponse
,
OptionResponse
,
SymbolicResponse
from
responsetypes
import
NumericalResponse
,
FormulaResponse
,
CustomResponse
,
SchematicResponse
,
MultipleChoiceResponse
,
TrueFalseResponse
,
ExternalResponse
,
ImageResponse
,
OptionResponse
,
SymbolicResponse
import
calc
import
eia
log
=
logging
.
getLogger
(
__name__
)
response_types
=
{
'numericalresponse'
:
NumericalResponse
,
'formularesponse'
:
FormulaResponse
,
'customresponse'
:
CustomResponse
,
'schematicresponse'
:
SchematicResponse
,
'externalresponse'
:
ExternalResponse
,
'multiplechoiceresponse'
:
MultipleChoiceResponse
,
'truefalseresponse'
:
TrueFalseResponse
,
'imageresponse'
:
ImageResponse
,
'optionresponse'
:
OptionResponse
,
'symbolicresponse'
:
SymbolicResponse
,
response_types
=
{
'numericalresponse'
:
NumericalResponse
,
'formularesponse'
:
FormulaResponse
,
'customresponse'
:
CustomResponse
,
'schematicresponse'
:
SchematicResponse
,
'externalresponse'
:
ExternalResponse
,
'multiplechoiceresponse'
:
MultipleChoiceResponse
,
'truefalseresponse'
:
TrueFalseResponse
,
'imageresponse'
:
ImageResponse
,
'optionresponse'
:
OptionResponse
,
'symbolicresponse'
:
SymbolicResponse
,
}
entry_types
=
[
'textline'
,
'schematic'
,
'choicegroup'
,
'textbox'
,
'imageinput'
,
'optioninput'
]
entry_types
=
[
'textline'
,
'schematic'
,
'choicegroup'
,
'textbox'
,
'imageinput'
,
'optioninput'
]
solution_types
=
[
'solution'
]
# extra things displayed after "show answers" is pressed
response_properties
=
[
"responseparam"
,
"answer"
]
# these get captured as student responses
# How to convert from original XML to HTML
# We should do this with xlst later
html_transforms
=
{
'problem'
:
{
'tag'
:
'div'
},
"numericalresponse"
:
{
'tag'
:
'span'
},
"customresponse"
:
{
'tag'
:
'span'
},
"externalresponse"
:
{
'tag'
:
'span'
},
"schematicresponse"
:
{
'tag'
:
'span'
},
"formularesponse"
:
{
'tag'
:
'span'
},
"symbolicresponse"
:
{
'tag'
:
'span'
},
"multiplechoiceresponse"
:
{
'tag'
:
'span'
},
"text"
:
{
'tag'
:
'span'
},
"math"
:
{
'tag'
:
'span'
},
html_transforms
=
{
'problem'
:
{
'tag'
:
'div'
},
"numericalresponse"
:
{
'tag'
:
'span'
},
"customresponse"
:
{
'tag'
:
'span'
},
"externalresponse"
:
{
'tag'
:
'span'
},
"schematicresponse"
:
{
'tag'
:
'span'
},
"formularesponse"
:
{
'tag'
:
'span'
},
"symbolicresponse"
:
{
'tag'
:
'span'
},
"multiplechoiceresponse"
:
{
'tag'
:
'span'
},
"text"
:
{
'tag'
:
'span'
},
"math"
:
{
'tag'
:
'span'
},
}
global_context
=
{
'random'
:
random
,
'numpy'
:
numpy
,
'math'
:
math
,
'scipy'
:
scipy
,
'calc'
:
calc
,
'eia'
:
eia
}
global_context
=
{
'random'
:
random
,
'numpy'
:
numpy
,
'math'
:
math
,
'scipy'
:
scipy
,
'calc'
:
calc
,
'eia'
:
eia
}
# These should be removed from HTML output, including all subelements
html_problem_semantics
=
[
"responseparam"
,
"answer"
,
"script"
]
# These should be removed from HTML output, but keeping subelements
html_skip
=
[
"numericalresponse"
,
"customresponse"
,
"schematicresponse"
,
"formularesponse"
,
"text"
,
"externalresponse"
,
'symbolicresponse'
]
html_skip
=
[
"numericalresponse"
,
"customresponse"
,
"schematicresponse"
,
"formularesponse"
,
"text"
,
"externalresponse"
,
'symbolicresponse'
]
# removed in MC
## These should be transformed
...
...
@@ -82,6 +81,7 @@ html_skip = ["numericalresponse", "customresponse", "schematicresponse", "formul
# "solution":inputtypes.solution.render,
# }
class
LoncapaProblem
(
object
):
def
__init__
(
self
,
fileobject
,
id
,
state
=
None
,
seed
=
None
,
system
=
None
):
## Initialize class variables from state
...
...
@@ -107,22 +107,22 @@ class LoncapaProblem(object):
# TODO: Does this deplete the Linux entropy pool? Is this fast enough?
if
not
self
.
seed
:
self
.
seed
=
struct
.
unpack
(
'i'
,
os
.
urandom
(
4
))[
0
]
self
.
seed
=
struct
.
unpack
(
'i'
,
os
.
urandom
(
4
))[
0
]
## Parse XML file
if
getattr
(
system
,
'DEBUG'
,
False
):
if
getattr
(
system
,
'DEBUG'
,
False
):
log
.
info
(
"[courseware.capa.capa_problem.lcp.init] fileobject =
%
s"
%
fileobject
)
file_text
=
fileobject
.
read
()
self
.
fileobject
=
fileobject
# save it, so we can use for debugging information later
# Convert startouttext and endouttext to proper <text></text>
# TODO: Do with XML operations
file_text
=
re
.
sub
(
"startouttext
\
s*/"
,
"text"
,
file_text
)
file_text
=
re
.
sub
(
"endouttext
\
s*/"
,
"/text"
,
file_text
)
file_text
=
re
.
sub
(
"startouttext
\
s*/"
,
"text"
,
file_text
)
file_text
=
re
.
sub
(
"endouttext
\
s*/"
,
"/text"
,
file_text
)
self
.
tree
=
etree
.
XML
(
file_text
)
self
.
preprocess_problem
(
self
.
tree
,
correct_map
=
self
.
correct_map
,
answer_map
=
self
.
student_answers
)
self
.
preprocess_problem
(
self
.
tree
,
correct_map
=
self
.
correct_map
,
answer_map
=
self
.
student_answers
)
self
.
context
=
self
.
extract_context
(
self
.
tree
,
seed
=
self
.
seed
)
for
response
in
self
.
tree
.
xpath
(
'//'
+
"|//"
.
join
(
response_types
)):
for
response
in
self
.
tree
.
xpath
(
'//'
+
"|//"
.
join
(
response_types
)):
responder
=
response_types
[
response
.
tag
](
response
,
self
.
context
,
self
.
system
)
responder
.
preprocess_response
()
...
...
@@ -133,10 +133,10 @@ class LoncapaProblem(object):
''' Stored per-user session data neeeded to:
1) Recreate the problem
2) Populate any student answers. '''
return
{
'seed'
:
self
.
seed
,
'student_answers'
:
self
.
student_answers
,
'correct_map'
:
self
.
correct_map
,
'done'
:
self
.
done
}
return
{
'seed'
:
self
.
seed
,
'student_answers'
:
self
.
student_answers
,
'correct_map'
:
self
.
correct_map
,
'done'
:
self
.
done
}
def
get_max_score
(
self
):
'''
...
...
@@ -144,20 +144,20 @@ class LoncapaProblem(object):
'''
sum
=
0
for
et
in
entry_types
:
sum
=
sum
+
self
.
tree
.
xpath
(
'count(//'
+
et
+
')'
)
sum
=
sum
+
self
.
tree
.
xpath
(
'count(//'
+
et
+
')'
)
return
int
(
sum
)
def
get_score
(
self
):
correct
=
0
correct
=
0
for
key
in
self
.
correct_map
:
if
self
.
correct_map
[
key
]
==
u'correct'
:
correct
+=
1
if
(
not
self
.
student_answers
)
or
len
(
self
.
student_answers
)
==
0
:
return
{
'score'
:
0
,
'total'
:
self
.
get_max_score
()}
if
(
not
self
.
student_answers
)
or
len
(
self
.
student_answers
)
==
0
:
return
{
'score'
:
0
,
'total'
:
self
.
get_max_score
()}
else
:
return
{
'score'
:
correct
,
'total'
:
self
.
get_max_score
()}
return
{
'score'
:
correct
,
'total'
:
self
.
get_max_score
()}
def
grade_answers
(
self
,
answers
):
'''
...
...
@@ -168,7 +168,6 @@ class LoncapaProblem(object):
Thus, for example, input_ID123 -> ID123, and input_fromjs_ID123 -> fromjs_ID123
'''
self
.
student_answers
=
answers
context
=
self
.
extract_context
(
self
.
tree
)
self
.
correct_map
=
dict
()
problems_simple
=
self
.
extract_problems
(
self
.
tree
)
for
response
in
problems_simple
:
...
...
@@ -183,7 +182,6 @@ class LoncapaProblem(object):
not included. Called by "show answers" button JSON request
(see capa_module)
"""
context
=
self
.
extract_context
(
self
.
tree
)
answer_map
=
dict
()
problems_simple
=
self
.
extract_problems
(
self
.
tree
)
# purified (flat) XML tree of just response queries
for
response
in
problems_simple
:
...
...
@@ -192,14 +190,14 @@ class LoncapaProblem(object):
answer_map
.
update
(
results
)
# dict of (id,correct_answer)
# example for the following: <textline size="5" correct_answer="saturated" />
for
entry
in
problems_simple
.
xpath
(
"//"
+
"|//"
.
join
(
response_properties
+
entry_types
)):
for
entry
in
problems_simple
.
xpath
(
"//"
+
"|//"
.
join
(
response_properties
+
entry_types
)):
answer
=
entry
.
get
(
'correct_answer'
)
# correct answer, when specified elsewhere, eg in a textline
if
answer
:
answer_map
[
entry
.
get
(
'id'
)]
=
contextualize_text
(
answer
,
self
.
context
)
# include solutions from <solution>...</solution> stanzas
# Tentative merge; we should figure out how we want to handle hints and solutions
for
entry
in
self
.
tree
.
xpath
(
"//"
+
"|//"
.
join
(
solution_types
)):
for
entry
in
self
.
tree
.
xpath
(
"//"
+
"|//"
.
join
(
solution_types
)):
answer
=
etree
.
tostring
(
entry
)
if
answer
:
answer_map
[
entry
.
get
(
'id'
)]
=
answer
...
...
@@ -211,7 +209,6 @@ class LoncapaProblem(object):
the dicts returned by grade_answers and get_question_answers. (Though
get_question_answers may only return a subset of these."""
answer_ids
=
[]
context
=
self
.
extract_context
(
self
.
tree
)
problems_simple
=
self
.
extract_problems
(
self
.
tree
)
for
response
in
problems_simple
:
responder
=
response_types
[
response
.
tag
](
response
,
self
.
context
)
...
...
@@ -223,10 +220,8 @@ class LoncapaProblem(object):
return
answer_ids
# ======= Private ========
def
extract_context
(
self
,
tree
,
seed
=
struct
.
unpack
(
'i'
,
os
.
urandom
(
4
))[
0
]):
# private
def
extract_context
(
self
,
tree
,
seed
=
struct
.
unpack
(
'i'
,
os
.
urandom
(
4
))[
0
]):
# private
'''
Extract content of <script>...</script> from the problem.xml file, and exec it in the
context of this problem. Provides ability to randomize problems, and also set
...
...
@@ -235,7 +230,7 @@ class LoncapaProblem(object):
Problem XML goes to Python execution context. Runs everything in script tags
'''
random
.
seed
(
self
.
seed
)
context
=
{
'global_context'
:
global_context
}
# save global context in here also
context
=
{
'global_context'
:
global_context
}
# save global context in here also
context
.
update
(
global_context
)
# initialize context to have stuff in global_context
context
[
'__builtins__'
]
=
globals
()[
'__builtins__'
]
# put globals there also
context
[
'the_lcp'
]
=
self
# pass instance of LoncapaProblem in
...
...
@@ -244,12 +239,14 @@ class LoncapaProblem(object):
for
script
in
tree
.
findall
(
'.//script'
):
stype
=
script
.
get
(
'type'
)
if
stype
:
if
'javascript'
in
stype
:
continue
# skip javascript
if
'perl'
in
stype
:
continue
# skip perl
if
'javascript'
in
stype
:
continue
# skip javascript
if
'perl'
in
stype
:
continue
# skip perl
# TODO: evaluate only python
code
=
script
.
text
XMLESC
=
{
"'"
:
"'"
,
"""
:
'"'
}
code
=
unescape
(
code
,
XMLESC
)
code
=
unescape
(
code
,
XMLESC
)
try
:
exec
code
in
context
,
context
# use "context" for global context; thus defs in code are global within code
except
Exception
:
...
...
@@ -296,36 +293,36 @@ class LoncapaProblem(object):
# do the rendering
# This should be broken out into a helper function
# that handles all input objects
render_object
=
inputtypes
.
SimpleInput
(
system
=
self
.
system
,
xml
=
problemtree
,
state
=
{
'value'
:
value
,
render_object
=
inputtypes
.
SimpleInput
(
system
=
self
.
system
,
xml
=
problemtree
,
state
=
{
'value'
:
value
,
'status'
:
status
,
'id'
:
problemtree
.
get
(
'id'
),
'feedback'
:{
'message'
:
msg
}
'id'
:
problemtree
.
get
(
'id'
),
'feedback'
:
{
'message'
:
msg
}
},
use
=
'capa_input'
)
return
render_object
.
get_html
()
#
function(problemtree, value, status, msg) # render the special response (textline, schematic,...)
use
=
'capa_input'
)
return
render_object
.
get_html
()
#
function(problemtree, value, status, msg) # render the special response (textline, schematic,...)
tree
=
Element
(
problemtree
.
tag
)
tree
=
Element
(
problemtree
.
tag
)
for
item
in
problemtree
:
subitems
=
self
.
extract_html
(
item
)
if
subitems
is
not
None
:
for
subitem
in
subitems
:
tree
.
append
(
subitem
)
for
(
key
,
value
)
in
problemtree
.
items
():
for
(
key
,
value
)
in
problemtree
.
items
():
tree
.
set
(
key
,
value
)
tree
.
text
=
problemtree
.
text
tree
.
tail
=
problemtree
.
tail
tree
.
text
=
problemtree
.
text
tree
.
tail
=
problemtree
.
tail
if
problemtree
.
tag
in
html_transforms
:
tree
.
tag
=
html_transforms
[
problemtree
.
tag
][
'tag'
]
tree
.
tag
=
html_transforms
[
problemtree
.
tag
][
'tag'
]
# Reset attributes. Otherwise, we get metadata in HTML
# (e.g. answers)
# TODO: We should remove and not zero them.
# I'm not sure how to do that quickly with lxml
for
k
in
tree
.
keys
():
tree
.
set
(
k
,
""
)
tree
.
set
(
k
,
""
)
# TODO: Fix. This loses Element().tail
#if problemtree.tag in html_skip:
...
...
@@ -340,58 +337,57 @@ class LoncapaProblem(object):
In-place transformation
'''
response_id
=
1
for
response
in
tree
.
xpath
(
'//'
+
"|//"
.
join
(
response_types
)):
response_id_str
=
self
.
problem_id
+
"_"
+
str
(
response_id
)
response
.
attrib
[
'id'
]
=
response_id_str
for
response
in
tree
.
xpath
(
'//'
+
"|//"
.
join
(
response_types
)):
response_id_str
=
self
.
problem_id
+
"_"
+
str
(
response_id
)
response
.
attrib
[
'id'
]
=
response_id_str
if
response_id
not
in
correct_map
:
correct
=
'unsubmitted'
response
.
attrib
[
'state'
]
=
correct
response_id
=
response_id
+
1
answer_id
=
1
for
entry
in
tree
.
xpath
(
"|"
.
join
([
'//'
+
response
.
tag
+
'[@id=$id]//'
+
x
for
x
in
(
entry_types
+
solution_types
)]),
for
entry
in
tree
.
xpath
(
"|"
.
join
([
'//'
+
response
.
tag
+
'[@id=$id]//'
+
x
for
x
in
(
entry_types
+
solution_types
)]),
id
=
response_id_str
):
# assign one answer_id for each entry_type or solution_type
entry
.
attrib
[
'response_id'
]
=
str
(
response_id
)
entry
.
attrib
[
'answer_id'
]
=
str
(
answer_id
)
entry
.
attrib
[
'id'
]
=
"
%
s_
%
i_
%
i"
%
(
self
.
problem_id
,
response_id
,
answer_id
)
answer_id
=
answer_id
+
1
entry
.
attrib
[
'id'
]
=
"
%
s_
%
i_
%
i"
%
(
self
.
problem_id
,
response_id
,
answer_id
)
answer_id
=
answer_id
+
1
# <solution>...</solution> may not be associated with any specific response; give IDs for those separately
# TODO: We should make the namespaces consistent and unique (e.g. %s_problem_%i).
solution_id
=
1
for
solution
in
tree
.
findall
(
'.//solution'
):
solution
.
attrib
[
'id'
]
=
"
%
s_solution_
%
i"
%
(
self
.
problem_id
,
solution_id
)
solution
.
attrib
[
'id'
]
=
"
%
s_solution_
%
i"
%
(
self
.
problem_id
,
solution_id
)
solution_id
+=
1
def
extract_problems
(
self
,
problem_tree
):
''' Remove layout from the problem, and give a purified XML tree of just the problems '''
problem_tree
=
copy
.
deepcopy
(
problem_tree
)
tree
=
Element
(
'problem'
)
for
response
in
problem_tree
.
xpath
(
"//"
+
"|//"
.
join
(
response_types
)):
problem_tree
=
copy
.
deepcopy
(
problem_tree
)
tree
=
Element
(
'problem'
)
for
response
in
problem_tree
.
xpath
(
"//"
+
"|//"
.
join
(
response_types
)):
newresponse
=
copy
.
copy
(
response
)
for
e
in
newresponse
:
newresponse
.
remove
(
e
)
# copy.copy is needed to make xpath work right. Otherwise, it starts at the root
# of the tree. We should figure out if there's some work-around
for
e
in
copy
.
copy
(
response
)
.
xpath
(
"//"
+
"|//"
.
join
(
response_properties
+
entry_types
)):
for
e
in
copy
.
copy
(
response
)
.
xpath
(
"//"
+
"|//"
.
join
(
response_properties
+
entry_types
)):
newresponse
.
append
(
e
)
tree
.
append
(
newresponse
)
return
tree
if
__name__
==
'__main__'
:
problem_id
=
'simpleFormula'
if
__name__
==
'__main__'
:
problem_id
=
'simpleFormula'
filename
=
'simpleFormula.xml'
problem_id
=
'resistor'
problem_id
=
'resistor'
filename
=
'resistor.xml'
lcp
=
LoncapaProblem
(
filename
,
problem_id
)
context
=
lcp
.
extract_context
(
lcp
.
tree
)
problem
=
lcp
.
extract_problems
(
lcp
.
tree
)
print
lcp
.
grade_problems
({
'resistor_2_1'
:
'1.0'
,
'resistor_3_1'
:
'2.0'
})
print
lcp
.
grade_problems
({
'resistor_2_1'
:
'1.0'
,
'resistor_3_1'
:
'2.0'
})
#print lcp.grade_problems({'simpleFormula_2_1':'3*x^3'})
#numericalresponse(problem, context)
...
...
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