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
4fe22be5
Commit
4fe22be5
authored
May 21, 2012
by
David Ormsbee
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #59 from MITx/pmitros/modular-refactor
First part of courseware refactor
parents
fb146bf5
bb66cad6
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
148 additions
and
20 deletions
+148
-20
djangoapps/courseware/capa/capa_problem.py
+14
-5
djangoapps/courseware/capa/inputtypes.py
+121
-2
djangoapps/courseware/capa/responsetypes.py
+13
-13
No files found.
djangoapps/courseware/capa/capa_problem.py
View file @
4fe22be5
...
...
@@ -25,6 +25,7 @@ from mako.template import Template
from
util
import
contextualize_text
import
inputtypes
from
responsetypes
import
NumericalResponse
,
FormulaResponse
,
CustomResponse
,
SchematicResponse
,
MultipleChoiceResponse
,
StudentInputError
,
TrueFalseResponse
,
ExternalResponse
,
ImageResponse
,
OptionResponse
import
calc
...
...
@@ -166,7 +167,7 @@ class LoncapaProblem(object):
problems_simple
=
self
.
extract_problems
(
self
.
tree
)
for
response
in
problems_simple
:
grader
=
response_types
[
response
.
tag
](
response
,
self
.
context
,
self
.
system
)
results
=
grader
.
g
rad
e
(
answers
)
# call the responsetype instance to do the actual grading
results
=
grader
.
g
et_scor
e
(
answers
)
# call the responsetype instance to do the actual grading
self
.
correct_map
.
update
(
results
)
return
self
.
correct_map
...
...
@@ -239,7 +240,7 @@ class LoncapaProblem(object):
# used to be
# if problemtree.tag in html_special_response:
if
hasattr
(
inputtypes
,
problemtree
.
tag
):
if
problemtree
.
tag
in
inputtypes
.
get_input_xml_tags
(
):
# status is currently the answer for the problem ID for the input element,
# but it will turn into a dict containing both the answer and any associated message
# for the problem ID for the input element.
...
...
@@ -266,9 +267,17 @@ class LoncapaProblem(object):
# print "[courseware.capa.capa_problem.extract_html] msg = ",msg
# do the rendering
#render_function = html_special_response[problemtree.tag]
render_function
=
getattr
(
inputtypes
,
problemtree
.
tag
)
return
render_function
(
problemtree
,
value
,
status
,
msg
)
# render the special response (textline, schematic,...)
# 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
,
'status'
:
status
,
'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,...)
tree
=
Element
(
problemtree
.
tag
)
for
item
in
problemtree
:
...
...
djangoapps/courseware/capa/inputtypes.py
View file @
4fe22be5
...
...
@@ -32,8 +32,121 @@ from lxml import etree
from
mitxmako.shortcuts
import
render_to_string
def
get_input_xml_tags
():
''' Eventually, this will be for all registered input types '''
return
SimpleInput
.
get_xml_tags
()
class
SimpleInput
():
# XModule
''' Type for simple inputs -- plain HTML with a form element
State is a dictionary with optional keys:
* Value
* ID
* Status (answered, unanswered, unsubmitted)
* Feedback (dictionary containing keys for hints, errors, or other
feedback from previous attempt)
'''
xml_tags
=
{}
## Maps tags to functions
@classmethod
def
get_xml_tags
(
c
):
return
c
.
xml_tags
.
keys
()
@classmethod
def
get_uses
(
c
):
return
[
'capa_input'
,
'capa_transform'
]
def
get_html
(
self
):
return
self
.
xml_tags
[
self
.
tag
](
self
.
xml
,
self
.
value
,
self
.
status
,
self
.
msg
)
def
__init__
(
self
,
system
,
xml
,
item_id
=
None
,
track_url
=
None
,
state
=
None
,
use
=
'capa_input'
):
self
.
xml
=
xml
self
.
tag
=
xml
.
tag
if
not
state
:
state
=
{}
## ID should only come from one place.
## If it comes from multiple, we use state first, XML second, and parameter
## third. Since we don't make this guarantee, we can swap this around in
## the future if there's a more logical order.
if
item_id
:
self
.
id
=
item_id
if
xml
.
get
(
'id'
):
self
.
id
=
xml
.
get
(
'id'
)
if
'id'
in
state
:
self
.
id
=
state
[
'id'
]
self
.
system
=
system
self
.
value
=
''
if
'value'
in
state
:
self
.
value
=
state
[
'value'
]
self
.
msg
=
''
if
'feedback'
in
state
and
'message'
in
state
[
'feedback'
]:
self
.
msg
=
state
[
'feedback'
][
'message'
]
self
.
status
=
'unanswered'
if
'status'
in
state
:
self
.
status
=
state
[
'status'
]
## TODO
# class SimpleTransform():
# ''' Type for simple XML to HTML transforms. Examples:
# * Math tags, which go from LON-CAPA-style m-tags to MathJAX
# '''
# xml_tags = {} ## Maps tags to functions
# @classmethod
# def get_xml_tags(c):
# return c.xml_tags.keys()
# @classmethod
# def get_uses(c):
# return ['capa_transform']
# def get_html(self):
# return self.xml_tags[self.tag](self.xml, self.value, self.status, self.msg)
# def __init__(self, system, xml, item_id = None, track_url=None, state=None, use = 'capa_input'):
# self.xml = xml
# self.tag = xml.tag
# if not state:
# state = {}
# if item_id:
# self.id = item_id
# if xml.get('id'):
# self.id = xml.get('id')
# if 'id' in state:
# self.id = state['id']
# self.system = system
# self.value = ''
# if 'value' in state:
# self.value = state['value']
# self.msg = ''
# if 'feedback' in state and 'message' in state['feedback']:
# self.msg = state['feedback']['message']
# self.status = 'unanswered'
# if 'status' in state:
# self.status = state['status']
def
register_render_function
(
fn
,
names
=
None
,
cls
=
SimpleInput
):
if
names
==
None
:
SimpleInput
.
xml_tags
[
fn
.
__name__
]
=
fn
else
:
raise
NotImplementedError
def
wrapped
():
return
fn
return
wrapped
#-----------------------------------------------------------------------------
@register_render_function
def
optioninput
(
element
,
value
,
status
,
msg
=
''
):
'''
Select option input type.
...
...
@@ -67,7 +180,7 @@ def optioninput(element, value, status, msg=''):
return
etree
.
XML
(
html
)
#-----------------------------------------------------------------------------
@register_render_function
def
choicegroup
(
element
,
value
,
status
,
msg
=
''
):
'''
Radio button inputs: multiple choice or true/false
...
...
@@ -90,6 +203,7 @@ def choicegroup(element, value, status, msg=''):
html
=
render_to_string
(
"choicegroup.html"
,
context
)
return
etree
.
XML
(
html
)
@register_render_function
def
textline
(
element
,
value
,
state
,
msg
=
""
):
eid
=
element
.
get
(
'id'
)
count
=
int
(
eid
.
split
(
'_'
)[
-
2
])
-
1
# HACK
...
...
@@ -100,6 +214,7 @@ def textline(element, value, state, msg=""):
#-----------------------------------------------------------------------------
@register_render_function
def
js_textline
(
element
,
value
,
status
,
msg
=
''
):
'''
Plan: We will inspect element to figure out type
...
...
@@ -125,6 +240,7 @@ def js_textline(element, value, status, msg=''):
#-----------------------------------------------------------------------------
## TODO: Make a wrapper for <codeinput>
@register_render_function
def
textbox
(
element
,
value
,
status
,
msg
=
''
):
'''
The textbox is used for code input. The message is the return HTML string from
...
...
@@ -140,6 +256,7 @@ def textbox(element, value, status, msg=''):
return
etree
.
XML
(
html
)
#-----------------------------------------------------------------------------
@register_render_function
def
schematic
(
element
,
value
,
status
,
msg
=
''
):
eid
=
element
.
get
(
'id'
)
height
=
element
.
get
(
'height'
)
...
...
@@ -164,6 +281,7 @@ def schematic(element, value, status, msg=''):
#-----------------------------------------------------------------------------
### TODO: Move out of inputtypes
@register_render_function
def
math
(
element
,
value
,
status
,
msg
=
''
):
'''
This is not really an input type. It is a convention from Lon-CAPA, used for
...
...
@@ -198,6 +316,7 @@ def math(element, value, status, msg=''):
#-----------------------------------------------------------------------------
@register_render_function
def
solution
(
element
,
value
,
status
,
msg
=
''
):
'''
This is not really an input type. It is just a <span>...</span> which is given an ID,
...
...
@@ -218,6 +337,7 @@ def solution(element, value, status, msg=''):
#-----------------------------------------------------------------------------
@register_render_function
def
imageinput
(
element
,
value
,
status
,
msg
=
''
):
'''
Clickable image as an input field. Element should specify the image source, height, and width, eg
...
...
@@ -253,4 +373,3 @@ def imageinput(element, value, status, msg=''):
print
'[courseware.capa.inputtypes.imageinput] context='
,
context
html
=
render_to_string
(
"imageinput.html"
,
context
)
return
etree
.
XML
(
html
)
djangoapps/courseware/capa/responsetypes.py
View file @
4fe22be5
...
...
@@ -47,10 +47,10 @@ def compare_with_tolerance(v1, v2, tol):
return
abs
(
v1
-
v2
)
<=
tolerance
class
GenericResponse
(
object
):
__metaclass__
=
abc
.
ABCMeta
__metaclass__
=
abc
.
ABCMeta
# abc = Abstract Base Class
@abc.abstractmethod
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
pass
@abc.abstractmethod
...
...
@@ -61,7 +61,7 @@ class GenericResponse(object):
def
preprocess_response
(
self
):
pass
#Every response type needs methods "g
rad
e" and "get_answers"
#Every response type needs methods "g
et_scor
e" and "get_answers"
#-----------------------------------------------------------------------------
...
...
@@ -95,7 +95,7 @@ class MultipleChoiceResponse(GenericResponse):
raise
Exception
(
"should have exactly one choice group per multiplechoicceresponse"
)
self
.
answer_id
=
self
.
answer_id
[
0
]
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
if
self
.
answer_id
in
student_answers
and
student_answers
[
self
.
answer_id
]
in
self
.
correct_choices
:
return
{
self
.
answer_id
:
'correct'
}
else
:
...
...
@@ -132,7 +132,7 @@ class TrueFalseResponse(MultipleChoiceResponse):
else
:
choice
.
set
(
"name"
,
"choice_"
+
choice
.
get
(
"name"
))
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
correct
=
set
(
self
.
correct_choices
)
answers
=
set
(
student_answers
.
get
(
self
.
answer_id
,
[]))
...
...
@@ -162,7 +162,7 @@ class OptionResponse(GenericResponse):
print
'[courseware.capa.responsetypes.OR.init] answer_fields=
%
s'
%
(
self
.
answer_fields
)
self
.
context
=
context
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
cmap
=
{}
amap
=
self
.
get_answers
()
for
aid
in
amap
:
...
...
@@ -194,7 +194,7 @@ class NumericalResponse(GenericResponse):
except
Exception
,
err
:
self
.
answer_id
=
None
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
''' Display HTML for a numeric response '''
student_answer
=
student_answers
[
self
.
answer_id
]
try
:
...
...
@@ -300,7 +300,7 @@ def sympy_check2():
else
:
self
.
code
=
answer
.
text
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
'''
student_answers is a dict with everything from request.POST, but with the first part
of each key removed (the string before the first "_").
...
...
@@ -363,7 +363,7 @@ def sympy_check2():
print
"oops in customresponse (cfn) error
%
s"
%
err
# print "context = ",self.context
print
traceback
.
format_exc
()
if
settings
.
DEBUG
:
print
"[courseware.capa.responsetypes.customresponse.g
rad
e] ret = "
,
ret
if
settings
.
DEBUG
:
print
"[courseware.capa.responsetypes.customresponse.g
et_scor
e] ret = "
,
ret
if
type
(
ret
)
==
dict
:
correct
[
0
]
=
'correct'
if
ret
[
'ok'
]
else
'incorrect'
msg
=
ret
[
'msg'
]
...
...
@@ -428,7 +428,7 @@ class ExternalResponse(GenericResponse):
self
.
tests
=
xml
.
get
(
'answer'
)
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
submission
=
[
student_answers
[
k
]
for
k
in
sorted
(
self
.
answer_ids
)]
self
.
context
.
update
({
'submission'
:
submission
})
...
...
@@ -504,7 +504,7 @@ class FormulaResponse(GenericResponse):
self
.
case_sensitive
=
False
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
variables
=
self
.
samples
.
split
(
'@'
)[
0
]
.
split
(
','
)
numsamples
=
int
(
self
.
samples
.
split
(
'@'
)[
1
]
.
split
(
'#'
)[
1
])
sranges
=
zip
(
*
map
(
lambda
x
:
map
(
float
,
x
.
split
(
","
)),
...
...
@@ -566,7 +566,7 @@ class SchematicResponse(GenericResponse):
else
:
self
.
code
=
answer
.
text
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
from
capa_problem
import
global_context
submission
=
[
json
.
loads
(
student_answers
[
k
])
for
k
in
sorted
(
self
.
answer_ids
)]
self
.
context
.
update
({
'submission'
:
submission
})
...
...
@@ -605,7 +605,7 @@ class ImageResponse(GenericResponse):
self
.
ielements
=
xml
.
findall
(
'imageinput'
)
self
.
answer_ids
=
[
ie
.
get
(
'id'
)
for
ie
in
self
.
ielements
]
def
g
rad
e
(
self
,
student_answers
):
def
g
et_scor
e
(
self
,
student_answers
):
correct_map
=
{}
expectedset
=
self
.
get_answers
()
...
...
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