From 7fcf04eaf5aab47bb8d3030ab5e2aec5b4c5aabf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Fri, 2 Nov 2012 14:14:13 +0200
Subject: [PATCH 01/13] add shapely
---
requirements.txt | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 28b12404a1..ac50bd1691 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,11 +8,11 @@ lxml
boto
mako
python-memcached
-python-openid
+python-openid
path.py
django_debug_toolbar
fs
-beautifulsoup
+beautifulsoup
beautifulsoup4
feedparser
requests
@@ -37,7 +37,7 @@ django-jasmine
django-keyedcache
django-mako
django-masquerade
-django-openid-auth
+django-openid-auth
django-robots
django-ses
django-storages
@@ -54,3 +54,4 @@ dogstatsd-python
# Taking out MySQL-python for now because it requires mysql to be installed, so breaks updates on content folks' envs.
# MySQL-python
sphinx
+Shapely
From 88c98b9a56b42b0541e8ab1dc7be6ad05ce6a5a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Fri, 2 Nov 2012 14:16:37 +0200
Subject: [PATCH 02/13] added multiple regions support
---
common/lib/capa/capa/responsetypes.py | 63 +++++++++++++++++----------
1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index b990c489b3..85efd70cee 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -23,6 +23,7 @@ import abc
import os
import subprocess
import xml.sax.saxutils as saxutils
+from shapely.geometry import Polygon, Point
# specific library imports
from calc import evaluator, UndefinedVariable
@@ -1720,12 +1721,20 @@ class ImageResponse(LoncapaResponse):
Lon-CAPA requires that each has a inside it. That
doesn't make sense to me (Ike). Instead, let's have it such that
should contain one or more stanzas. Each should specify
- a rectangle, given as an attribute, defining the correct answer.
+ a rectangle(s) or region(s), given as an attribute, defining the correct answer.
+
+ Rectangle(s) are more prioritized over regions due to simplicity and backward compatibility.
+ In this example regions will be ignored:
+
+
+ Regions is list of lists [region1, region2, region3, ...] where regionN is ordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
"""
snippets = [{'snippet': '''
-
+
+
+
'''}]
response_tag = 'imageresponse'
@@ -1733,7 +1742,7 @@ class ImageResponse(LoncapaResponse):
def setup_response(self):
self.ielements = self.inputfields
- self.answer_ids = [ie.get('id') for ie in self.ielements]
+ self.answer_ids = [ie.get('id') for ie in self.ielements]
def get_score(self, student_answers):
correct_map = CorrectMap()
@@ -1743,7 +1752,7 @@ class ImageResponse(LoncapaResponse):
given = student_answers[aid] # this should be a string of the form '[x,y]'
correct_map.set(aid, 'incorrect')
- if not given: # No answer to parse. Mark as incorrect and move on
+ if not given: # No answer to parse. Mark as incorrect and move on
continue
# parse given answer
@@ -1753,29 +1762,37 @@ class ImageResponse(LoncapaResponse):
'error grading %s (input=%s)' % (aid, given))
(gx, gy) = [int(x) for x in m.groups()]
- # Check whether given point lies in any of the solution rectangles
- solution_rectangles = expectedset[aid].split(';')
- for solution_rectangle in solution_rectangles:
- # parse expected answer
- # TODO: Compile regexp on file load
- m = re.match('[\(\[]([0-9]+),([0-9]+)[\)\]]-[\(\[]([0-9]+),([0-9]+)[\)\]]',
- solution_rectangle.strip().replace(' ', ''))
- if not m:
- msg = 'Error in problem specification! cannot parse rectangle in %s' % (
- etree.tostring(self.ielements[aid], pretty_print=True))
- raise Exception('[capamodule.capa.responsetypes.imageinput] ' + msg)
- (llx, lly, urx, ury) = [int(x) for x in m.groups()]
-
- # answer is correct if (x,y) is within the specified rectangle
- if (llx <= gx <= urx) and (lly <= gy <= ury):
- correct_map.set(aid, 'correct')
- break
+ rectangles, regions = expectedset
+ if rectangles[aid]: # rectangles part - for backward compatibility
+ # Check whether given point lies in any of the solution rectangles
+ solution_rectangles = rectangles[aid].split(';')
+ for solution_rectangle in solution_rectangles:
+ # parse expected answer
+ # TODO: Compile regexp on file load
+ m = re.match('[\(\[]([0-9]+),([0-9]+)[\)\]]-[\(\[]([0-9]+),([0-9]+)[\)\]]',
+ solution_rectangle.strip().replace(' ', ''))
+ if not m:
+ msg = 'Error in problem specification! cannot parse rectangle in %s' % (
+ etree.tostring(self.ielements[aid], pretty_print=True))
+ raise Exception('[capamodule.capa.responsetypes.imageinput] ' + msg)
+ (llx, lly, urx, ury) = [int(x) for x in m.groups()]
+ # answer is correct if (x,y) is within the specified rectangle
+ if (llx <= gx <= urx) and (lly <= gy <= ury):
+ correct_map.set(aid, 'correct')
+ break
+ else: # rectangles are more prioretized for same id
+ if regions[aid]:
+ parsed_region = json.loads(regions[aid])
+ for region in parsed_region:
+ if Polygon(region).contains(Point(gx, gy)):
+ correct_map.set(aid, 'correct')
+ break
return correct_map
def get_answers(self):
- return dict([(ie.get('id'), ie.get('rectangle')) for ie in self.ielements])
-
+ return (dict([(ie.get('id'), ie.get('rectangle')) for ie in self.ielements]),
+ dict([(ie.get('id'), ie.get('regions')) for ie in self.ielements]))
#-----------------------------------------------------------------------------
# TEMPORARY: List of all response subclasses
# FIXME: To be replaced by auto-registration
From 406e7e29c04bcbe829752a802f675f22c1a8629b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Fri, 2 Nov 2012 17:25:18 +0200
Subject: [PATCH 03/13] added geos to requirements
---
brew-formulas.txt | 15 ++++++++-------
create-dev-env.sh | 2 +-
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/brew-formulas.txt b/brew-formulas.txt
index b5b555e2a0..e06829a43a 100644
--- a/brew-formulas.txt
+++ b/brew-formulas.txt
@@ -1,10 +1,11 @@
-readline
-sqlite
-gdbm
-pkg-config
-gfortran
-python
-yuicompressor
+readline
+sqlite
+gdbm
+pkg-config
+gfortran
+python
+yuicompressor
node
graphviz
mysql
+geos
diff --git a/create-dev-env.sh b/create-dev-env.sh
index e481d3fd5e..2a7f68d3b0 100755
--- a/create-dev-env.sh
+++ b/create-dev-env.sh
@@ -99,7 +99,7 @@ NUMPY_VER="1.6.2"
SCIPY_VER="0.10.1"
BREW_FILE="$BASE/mitx/brew-formulas.txt"
LOG="/var/tmp/install-$(date +%Y%m%d-%H%M%S).log"
-APT_PKGS="pkg-config curl git python-virtualenv build-essential python-dev gfortran liblapack-dev libfreetype6-dev libpng12-dev libxml2-dev libxslt-dev yui-compressor nodejs npm graphviz graphviz-dev mysql-server libmysqlclient-dev"
+APT_PKGS="pkg-config curl git python-virtualenv build-essential python-dev gfortran liblapack-dev libfreetype6-dev libpng12-dev libxml2-dev libxslt-dev yui-compressor nodejs npm graphviz graphviz-dev mysql-server libmysqlclient-dev geos"
if [[ $EUID -eq 0 ]]; then
error "This script should not be run using sudo or as the root user"
From 03c338d6e3a64235fbc8f7f32da060b5922d704d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Mon, 26 Nov 2012 18:26:16 +0200
Subject: [PATCH 04/13] added geos library for linux
---
create-dev-env.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/create-dev-env.sh b/create-dev-env.sh
index 2a7f68d3b0..5edc765e4f 100755
--- a/create-dev-env.sh
+++ b/create-dev-env.sh
@@ -99,7 +99,7 @@ NUMPY_VER="1.6.2"
SCIPY_VER="0.10.1"
BREW_FILE="$BASE/mitx/brew-formulas.txt"
LOG="/var/tmp/install-$(date +%Y%m%d-%H%M%S).log"
-APT_PKGS="pkg-config curl git python-virtualenv build-essential python-dev gfortran liblapack-dev libfreetype6-dev libpng12-dev libxml2-dev libxslt-dev yui-compressor nodejs npm graphviz graphviz-dev mysql-server libmysqlclient-dev geos"
+APT_PKGS="pkg-config curl git python-virtualenv build-essential python-dev gfortran liblapack-dev libfreetype6-dev libpng12-dev libxml2-dev libxslt-dev yui-compressor nodejs npm graphviz graphviz-dev mysql-server libmysqlclient-dev libgeos-dev"
if [[ $EUID -eq 0 ]]; then
error "This script should not be run using sudo or as the root user"
From b4fb0cc463fa58ba4a8bc2f0bbef5da1a7c9e0cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Mon, 26 Nov 2012 18:34:38 +0200
Subject: [PATCH 05/13] fixed line length
---
common/lib/capa/capa/responsetypes.py | 41 +++++++++++++++++----------
1 file changed, 26 insertions(+), 15 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index 85efd70cee..730cdb12cf 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1718,23 +1718,33 @@ class ImageResponse(LoncapaResponse):
which produces an [x,y] coordinate pair. The click is correct if it falls
within a region specified. This region is a union of rectangles.
- Lon-CAPA requires that each has a inside it. That
- doesn't make sense to me (Ike). Instead, let's have it such that
- should contain one or more stanzas. Each should specify
- a rectangle(s) or region(s), given as an attribute, defining the correct answer.
+ Lon-CAPA requires that each has a inside it.
+ That doesn't make sense to me (Ike). Instead, let's have it such that
+ should contain one or more stanzas.
+ Each should specify a rectangle(s) or region(s), given as an
+ attribute, defining the correct answer.
- Rectangle(s) are more prioritized over regions due to simplicity and backward compatibility.
- In this example regions will be ignored:
-
+ Rectangle(s) are more prioritized over regions due to simplicity and
+ backward compatibility. In this example regions will be ignored:
+
- Regions is list of lists [region1, region2, region3, ...] where regionN is ordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
+ Regions is list of lists [region1, region2, region3, ...] where regionN
+ is ordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
"""
snippets = [{'snippet': '''
-
-
-
-
-
+
+
+
+
+
'''}]
response_tag = 'imageresponse'
@@ -1748,8 +1758,9 @@ class ImageResponse(LoncapaResponse):
correct_map = CorrectMap()
expectedset = self.get_answers()
- for aid in self.answer_ids: # loop through IDs of fields in our stanza
- given = student_answers[aid] # this should be a string of the form '[x,y]'
+ for aid in self.answer_ids: # loop through IDs of
+ # fields in our stanza
+ given = student_answers[aid] # this should be a string of the form '[x,y]'
correct_map.set(aid, 'incorrect')
if not given: # No answer to parse. Mark as incorrect and move on
From 647d1514aa7d4433595943727ecec57ddd4a71f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?=
Date: Mon, 26 Nov 2012 18:38:46 +0200
Subject: [PATCH 06/13] support both regions and rectangles
---
common/lib/capa/capa/responsetypes.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index 730cdb12cf..74c801b450 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1724,14 +1724,15 @@ class ImageResponse(LoncapaResponse):
Each should specify a rectangle(s) or region(s), given as an
attribute, defining the correct answer.
- Rectangle(s) are more prioritized over regions due to simplicity and
- backward compatibility. In this example regions will be ignored:
Regions is list of lists [region1, region2, region3, ...] where regionN
is ordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
+
+ Returns:
+ True, if click is inside any region or rectangle. Otherwise False.
"""
snippets = [{'snippet': '''
Date: Tue, 27 Nov 2012 13:09:58 +0200
Subject: [PATCH 07/13] support for single list syntax in regions
---
common/lib/capa/capa/responsetypes.py | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index 74c801b450..000d77d655 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1758,9 +1758,10 @@ class ImageResponse(LoncapaResponse):
def get_score(self, student_answers):
correct_map = CorrectMap()
expectedset = self.get_answers()
-
+ # import ipdb; ipdb.set_trace()
for aid in self.answer_ids: # loop through IDs of
# fields in our stanza
+ # import ipdb; ipdb.set_trace()
given = student_answers[aid] # this should be a string of the form '[x,y]'
correct_map.set(aid, 'incorrect')
@@ -1795,10 +1796,16 @@ class ImageResponse(LoncapaResponse):
break
if regions[aid]:
parsed_region = json.loads(regions[aid])
- for region in parsed_region:
- if Polygon(region).contains(Point(gx, gy)):
- correct_map.set(aid, 'correct')
- break
+ if parsed_region:
+ if type(parsed_region[0][0]) != list:
+ # we have [[1,2],[3,4],[5,6] - single region
+ # instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]
+ # or [[[1,2],[3,4],[5,6]] - multiple regions syntax
+ parsed_region = [parsed_region]
+ for region in parsed_region:
+ if Polygon(region).contains(Point(gx, gy)):
+ correct_map.set(aid, 'correct')
+ break
return correct_map
def get_answers(self):
From 5723672e490c4d19ddedbcb6e6ea40cce2e6f822 Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 13:16:47 +0200
Subject: [PATCH 08/13] speed improvements
---
common/lib/capa/capa/responsetypes.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index 000d77d655..aeabacef59 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1794,7 +1794,9 @@ class ImageResponse(LoncapaResponse):
if (llx <= gx <= urx) and (lly <= gy <= ury):
correct_map.set(aid, 'correct')
break
- if regions[aid]:
+ # import ipdb; ipdb.set_trace()
+ if correct_map[aid]['correctness'] != 'correct' and regions[aid]:
+ import ipdb; ipdb.set_trace()
parsed_region = json.loads(regions[aid])
if parsed_region:
if type(parsed_region[0][0]) != list:
From 72f9358f64d055d46466e1adf836f368aaf4182d Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 13:18:37 +0200
Subject: [PATCH 09/13] tests for imageresponse regions
---
.../capa/tests/test_files/imageresponse.xml | 19 ++++++++++++
.../lib/capa/capa/tests/test_responsetypes.py | 30 ++++++++++++++++++-
2 files changed, 48 insertions(+), 1 deletion(-)
diff --git a/common/lib/capa/capa/tests/test_files/imageresponse.xml b/common/lib/capa/capa/tests/test_files/imageresponse.xml
index 34dba37e3b..c4590b211f 100644
--- a/common/lib/capa/capa/tests/test_files/imageresponse.xml
+++ b/common/lib/capa/capa/tests/test_files/imageresponse.xml
@@ -18,4 +18,23 @@ Hello
Use conservation of energy.
+
+
+
+
+
+
+
+Click on either of the two positions as discussed previously.
+
+Click on either of the two positions as discussed previously.
+
+
+Click on either of the two positions as discussed previously.
+
+Use conservation of energy.
+
+
+
+
diff --git a/common/lib/capa/capa/tests/test_responsetypes.py b/common/lib/capa/capa/tests/test_responsetypes.py
index bcac555b5e..3564ad3fe8 100644
--- a/common/lib/capa/capa/tests/test_responsetypes.py
+++ b/common/lib/capa/capa/tests/test_responsetypes.py
@@ -52,24 +52,52 @@ class ImageResponseTest(unittest.TestCase):
def test_ir_grade(self):
imageresponse_file = os.path.dirname(__file__) + "/test_files/imageresponse.xml"
test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=test_system)
- correct_answers = {'1_2_1': '(490,11)-(556,98)',
+ # testing regions only
+ correct_answers = {
+ #regions
+ '1_2_1': '(490,11)-(556,98)',
'1_2_2': '(242,202)-(296,276)',
'1_2_3': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_4': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_5': '(490,11)-(556,98);(242,202)-(296,276)',
+ #testing regions and rectanges
+ '1_3_1': '(490,11)-(556,98)',
+ '1_3_2': '(242,202)-(296,276)',
+ '1_3_3': '(490,11)-(556,98);(242,202)-(296,276)',
+ '1_3_4': '(490,11)-(556,98);(242,202)-(296,276)',
+ '1_3_5': '(490,11)-(556,98);(242,202)-(296,276)',
}
test_answers = {'1_2_1': '[500,20]',
'1_2_2': '[250,300]',
'1_2_3': '[500,20]',
'1_2_4': '[250,250]',
'1_2_5': '[10,10]',
+
+ '1_3_1': '[500,20]',
+ '1_3_2': '[15,15]',
+ '1_3_3': '[500,20]',
+ '1_3_4': '[115,115]',
+ '1_3_5': '[15,15]',
+ '1_3_6': '[20,20]',
+ '1_3_7': '[20,20]',
}
+
+ # regions
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_1'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_2'), 'incorrect')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_3'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_4'), 'correct')
self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_2_5'), 'incorrect')
+ # regions and rectangles
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_1'), 'correct')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_2'), 'correct')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_3'), 'incorrect')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_4'), 'correct')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_5'), 'correct')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_6'), 'incorrect')
+ self.assertEquals(test_lcp.grade_answers(test_answers).get_correctness('1_3_7'), 'correct')
+
class SymbolicResponseTest(unittest.TestCase):
def test_sr_grade(self):
From adef5d6f7eb1af0f12fdfc543d3822ad91253cad Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 14:11:11 +0200
Subject: [PATCH 10/13] polygon is created from points via convex_hull
---
common/lib/capa/capa/responsetypes.py | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index aeabacef59..ae5d764568 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -23,7 +23,7 @@ import abc
import os
import subprocess
import xml.sax.saxutils as saxutils
-from shapely.geometry import Polygon, Point
+from shapely.geometry import Point, MultiPoint
# specific library imports
from calc import evaluator, UndefinedVariable
@@ -1796,16 +1796,20 @@ class ImageResponse(LoncapaResponse):
break
# import ipdb; ipdb.set_trace()
if correct_map[aid]['correctness'] != 'correct' and regions[aid]:
- import ipdb; ipdb.set_trace()
+ # import ipdb; ipdb.set_trace()
parsed_region = json.loads(regions[aid])
if parsed_region:
if type(parsed_region[0][0]) != list:
- # we have [[1,2],[3,4],[5,6] - single region
- # instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]
- # or [[[1,2],[3,4],[5,6]] - multiple regions syntax
+ # we have [[1,2],[3,4],[5,6]] - single region
+ # instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]]
+ # or [[[1,2],[3,4],[5,6]]] - multiple regions syntax
parsed_region = [parsed_region]
+ # if aid =='1_3_6':
+ # import ipdb; ipdb.set_trace()
for region in parsed_region:
- if Polygon(region).contains(Point(gx, gy)):
+ polygon = MultiPoint(region).convex_hull
+ if (polygon.type == 'Polygon' and
+ polygon.contains(Point(gx, gy))):
correct_map.set(aid, 'correct')
break
return correct_map
From 896e922858adfbda5e3117186d154292c1865103 Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 14:27:19 +0200
Subject: [PATCH 11/13] cleaning and documeting
---
common/lib/capa/capa/responsetypes.py | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py
index ae5d764568..20e7c43577 100644
--- a/common/lib/capa/capa/responsetypes.py
+++ b/common/lib/capa/capa/responsetypes.py
@@ -1726,10 +1726,14 @@ class ImageResponse(LoncapaResponse):
+ regions="[[[10,10], [20,30], [40, 10]], [[100,100], [120,130], [110,150]]]"/>
Regions is list of lists [region1, region2, region3, ...] where regionN
- is ordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
+ is disordered list of points: [[1,1], [100,100], [50,50], [20, 70]].
+
+ If there is only one region in the list, simpler notation can be used:
+ regions="[[10,10], [30,30], [10, 30], [30, 10]]" (without explicitly
+ setting outer list)
Returns:
True, if click is inside any region or rectangle. Otherwise False.
@@ -1743,9 +1747,9 @@ class ImageResponse(LoncapaResponse):
rectangle="(10,10)-(20,30);(12,12)-(40,60)" />
+ regions="[[[10,10], [20,30], [40, 10]], [[100,100], [120,130], [110,150]]]"/>
+ regions="[[[10,10], [20,30], [40, 10]], [[100,100], [120,130], [110,150]]]"/>
'''}]
response_tag = 'imageresponse'
@@ -1758,16 +1762,12 @@ class ImageResponse(LoncapaResponse):
def get_score(self, student_answers):
correct_map = CorrectMap()
expectedset = self.get_answers()
- # import ipdb; ipdb.set_trace()
for aid in self.answer_ids: # loop through IDs of
# fields in our stanza
- # import ipdb; ipdb.set_trace()
given = student_answers[aid] # this should be a string of the form '[x,y]'
-
correct_map.set(aid, 'incorrect')
if not given: # No answer to parse. Mark as incorrect and move on
continue
-
# parse given answer
m = re.match('\[([0-9]+),([0-9]+)]', given.strip().replace(' ', ''))
if not m:
@@ -1794,9 +1794,7 @@ class ImageResponse(LoncapaResponse):
if (llx <= gx <= urx) and (lly <= gy <= ury):
correct_map.set(aid, 'correct')
break
- # import ipdb; ipdb.set_trace()
if correct_map[aid]['correctness'] != 'correct' and regions[aid]:
- # import ipdb; ipdb.set_trace()
parsed_region = json.loads(regions[aid])
if parsed_region:
if type(parsed_region[0][0]) != list:
@@ -1804,8 +1802,6 @@ class ImageResponse(LoncapaResponse):
# instead of [[[1,2],[3,4],[5,6], [[1,2],[3,4],[5,6]]]
# or [[[1,2],[3,4],[5,6]]] - multiple regions syntax
parsed_region = [parsed_region]
- # if aid =='1_3_6':
- # import ipdb; ipdb.set_trace()
for region in parsed_region:
polygon = MultiPoint(region).convex_hull
if (polygon.type == 'Polygon' and
From 95151ad33a6deabba01c9b1cfab7acd789a2537c Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 14:27:44 +0200
Subject: [PATCH 12/13] tests updates for imageresponse
---
.../capa/tests/test_files/imageresponse.xml | 4 ++--
.../lib/capa/capa/tests/test_responsetypes.py | 18 ++++++++++--------
2 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/common/lib/capa/capa/tests/test_files/imageresponse.xml b/common/lib/capa/capa/tests/test_files/imageresponse.xml
index c4590b211f..41c9f01218 100644
--- a/common/lib/capa/capa/tests/test_files/imageresponse.xml
+++ b/common/lib/capa/capa/tests/test_files/imageresponse.xml
@@ -22,14 +22,14 @@ Hello
-
+
Click on either of the two positions as discussed previously.
Click on either of the two positions as discussed previously.
-
+
Click on either of the two positions as discussed previously.
Use conservation of energy.
diff --git a/common/lib/capa/capa/tests/test_responsetypes.py b/common/lib/capa/capa/tests/test_responsetypes.py
index 3564ad3fe8..be734bdb88 100644
--- a/common/lib/capa/capa/tests/test_responsetypes.py
+++ b/common/lib/capa/capa/tests/test_responsetypes.py
@@ -54,18 +54,20 @@ class ImageResponseTest(unittest.TestCase):
test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=test_system)
# testing regions only
correct_answers = {
- #regions
- '1_2_1': '(490,11)-(556,98)',
+ #regions
+ '1_2_1': '(490,11)-(556,98)',
'1_2_2': '(242,202)-(296,276)',
'1_2_3': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_4': '(490,11)-(556,98);(242,202)-(296,276)',
'1_2_5': '(490,11)-(556,98);(242,202)-(296,276)',
#testing regions and rectanges
- '1_3_1': '(490,11)-(556,98)',
- '1_3_2': '(242,202)-(296,276)',
- '1_3_3': '(490,11)-(556,98);(242,202)-(296,276)',
- '1_3_4': '(490,11)-(556,98);(242,202)-(296,276)',
- '1_3_5': '(490,11)-(556,98);(242,202)-(296,276)',
+ '1_3_1': 'rectangle="(490,11)-(556,98)" regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_2': 'rectangle="(490,11)-(556,98)" regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_3': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_4': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_5': 'regions="[[[10,10], [20,10], [20, 30]]]"',
+ '1_3_6': 'regions="[[10,10], [30,30], [15, 15]]"',
+ '1_3_7': 'regions="[[10,10], [30,30], [10, 30], [30, 10]]"',
}
test_answers = {'1_2_1': '[500,20]',
'1_2_2': '[250,300]',
@@ -79,7 +81,7 @@ class ImageResponseTest(unittest.TestCase):
'1_3_4': '[115,115]',
'1_3_5': '[15,15]',
'1_3_6': '[20,20]',
- '1_3_7': '[20,20]',
+ '1_3_7': '[20,15]',
}
# regions
From b0a85fde540941be127f317cd4f131ecbbdc4ddb Mon Sep 17 00:00:00 2001
From: Alexander Kryklia
Date: Tue, 27 Nov 2012 14:29:04 +0200
Subject: [PATCH 13/13] fix line widths
---
.../lib/capa/capa/tests/test_responsetypes.py | 55 ++++++++++---------
1 file changed, 29 insertions(+), 26 deletions(-)
diff --git a/common/lib/capa/capa/tests/test_responsetypes.py b/common/lib/capa/capa/tests/test_responsetypes.py
index be734bdb88..9eecef3986 100644
--- a/common/lib/capa/capa/tests/test_responsetypes.py
+++ b/common/lib/capa/capa/tests/test_responsetypes.py
@@ -54,34 +54,37 @@ class ImageResponseTest(unittest.TestCase):
test_lcp = lcp.LoncapaProblem(open(imageresponse_file).read(), '1', system=test_system)
# testing regions only
correct_answers = {
- #regions
- '1_2_1': '(490,11)-(556,98)',
- '1_2_2': '(242,202)-(296,276)',
- '1_2_3': '(490,11)-(556,98);(242,202)-(296,276)',
- '1_2_4': '(490,11)-(556,98);(242,202)-(296,276)',
- '1_2_5': '(490,11)-(556,98);(242,202)-(296,276)',
- #testing regions and rectanges
- '1_3_1': 'rectangle="(490,11)-(556,98)" regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
- '1_3_2': 'rectangle="(490,11)-(556,98)" regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
- '1_3_3': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
- '1_3_4': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
- '1_3_5': 'regions="[[[10,10], [20,10], [20, 30]]]"',
- '1_3_6': 'regions="[[10,10], [30,30], [15, 15]]"',
- '1_3_7': 'regions="[[10,10], [30,30], [10, 30], [30, 10]]"',
+ #regions
+ '1_2_1': '(490,11)-(556,98)',
+ '1_2_2': '(242,202)-(296,276)',
+ '1_2_3': '(490,11)-(556,98);(242,202)-(296,276)',
+ '1_2_4': '(490,11)-(556,98);(242,202)-(296,276)',
+ '1_2_5': '(490,11)-(556,98);(242,202)-(296,276)',
+ #testing regions and rectanges
+ '1_3_1': 'rectangle="(490,11)-(556,98)" \
+ regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_2': 'rectangle="(490,11)-(556,98)" \
+ regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_3': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_4': 'regions="[[[10,10], [20,10], [20, 30]], [[100,100], [120,100], [120,150]]]"',
+ '1_3_5': 'regions="[[[10,10], [20,10], [20, 30]]]"',
+ '1_3_6': 'regions="[[10,10], [30,30], [15, 15]]"',
+ '1_3_7': 'regions="[[10,10], [30,30], [10, 30], [30, 10]]"',
}
- test_answers = {'1_2_1': '[500,20]',
- '1_2_2': '[250,300]',
- '1_2_3': '[500,20]',
- '1_2_4': '[250,250]',
- '1_2_5': '[10,10]',
+ test_answers = {
+ '1_2_1': '[500,20]',
+ '1_2_2': '[250,300]',
+ '1_2_3': '[500,20]',
+ '1_2_4': '[250,250]',
+ '1_2_5': '[10,10]',
- '1_3_1': '[500,20]',
- '1_3_2': '[15,15]',
- '1_3_3': '[500,20]',
- '1_3_4': '[115,115]',
- '1_3_5': '[15,15]',
- '1_3_6': '[20,20]',
- '1_3_7': '[20,15]',
+ '1_3_1': '[500,20]',
+ '1_3_2': '[15,15]',
+ '1_3_3': '[500,20]',
+ '1_3_4': '[115,115]',
+ '1_3_5': '[15,15]',
+ '1_3_6': '[20,20]',
+ '1_3_7': '[20,15]',
}
# regions