feat!: Removing sandbox folder from platform and installing it from p… (#30402)

* feat!: common/lib/sandbox-packages folder moved to a new library.
This commit is contained in:
Awais Qureshi
2022-06-01 16:02:13 +05:00
committed by GitHub
parent a27247d14d
commit 02e29168b2
19 changed files with 46 additions and 1476 deletions

View File

@@ -0,0 +1,26 @@
"""
Tests for codejail-includes package.
"""
import unittest
import eia
import loncapa
from verifiers import draganddrop
class TestCodeJailIncludes(unittest.TestCase):
""" tests for codejail includes"""
def test_loncapa(self):
random_integer = loncapa.lc_random(2, 60, 5)
assert random_integer <= 60
assert random_integer >= 2
def test_nested_list_and_list1(self):
assert draganddrop.PositionsCompare([[1, 2], 40]) == draganddrop.PositionsCompare([1, 3])
def test_Eia(self):
# Test cases. All of these should return True
assert eia.iseia(100) # 100 ohm resistor is EIA
assert not eia.iseia(101) # 101 is not

View File

@@ -15,18 +15,12 @@ CodeJail`__, with a few customized tweaks.
__ https://github.com/edx/codejail/blob/master/README.rst
1. At the instruction to install packages into the sandboxed code, you'll
1. At the instruction to install packages into the sandboxed code, you'll
need to install the requirements from requirements/edx-sandbox::
$ pip install -r requirements/edx-sandbox/base.txt
2. At the instruction to create the AppArmor profile, you'll need a line in
the profile for the sandbox packages. <EDXPLATFORM> is the full path to
your edx_platform repo::
<EDXPLATFORM>/common/lib/sandbox-packages/** r,
3. You can configure resource limits in settings.py. A CODE_JAIL setting is
2. You can configure resource limits in settings.py. A CODE_JAIL setting is
available, a dictionary. The "limits" key lets you adjust the limits for
CPU time, real time, and memory use. Setting any of them to zero disables
that limit::

View File

@@ -1 +0,0 @@
This directory is in the Python path for sandboxed Python execution.

View File

@@ -1,112 +0,0 @@
"""
Standard resistor values.
Commonly used for verifying electronic components in circuit classes are
standard values, or conversely, for generating realistic component
values in parameterized problems. For details, see:
http://en.wikipedia.org/wiki/Electronic_color_code
"""
# pylint: disable=invalid-name
# r is standard name for a resistor. We would like to use it as such.
import math
import numbers
E6 = [10, 15, 22, 33, 47, 68]
E12 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82]
E24 = [10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 11, 13, 16, 20,
24, 30, 36, 43, 51, 62, 75, 91]
E48 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 105,
127, 154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 110, 133,
162, 196, 237, 287, 348, 422, 511, 619, 750, 909, 115, 140, 169,
205, 249, 301, 365, 442, 536, 649, 787, 953]
E96 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 102,
124, 150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 105, 127,
154, 187, 226, 274, 332, 402, 487, 590, 715, 866, 107, 130, 158,
191, 232, 280, 340, 412, 499, 604, 732, 887, 110, 133, 162, 196,
237, 287, 348, 422, 511, 619, 750, 909, 113, 137, 165, 200, 243,
294, 357, 432, 523, 634, 768, 931, 115, 140, 169, 205, 249, 301,
365, 442, 536, 649, 787, 953, 118, 143, 174, 210, 255, 309, 374,
453, 549, 665, 806, 976]
E192 = [100, 121, 147, 178, 215, 261, 316, 383, 464, 562, 681, 825, 101,
123, 149, 180, 218, 264, 320, 388, 470, 569, 690, 835, 102, 124,
150, 182, 221, 267, 324, 392, 475, 576, 698, 845, 104, 126, 152,
184, 223, 271, 328, 397, 481, 583, 706, 856, 105, 127, 154, 187,
226, 274, 332, 402, 487, 590, 715, 866, 106, 129, 156, 189, 229,
277, 336, 407, 493, 597, 723, 876, 107, 130, 158, 191, 232, 280,
340, 412, 499, 604, 732, 887, 109, 132, 160, 193, 234, 284, 344,
417, 505, 612, 741, 898, 110, 133, 162, 196, 237, 287, 348, 422,
511, 619, 750, 909, 111, 135, 164, 198, 240, 291, 352, 427, 517,
626, 759, 920, 113, 137, 165, 200, 243, 294, 357, 432, 523, 634,
768, 931, 114, 138, 167, 203, 246, 298, 361, 437, 530, 642, 777,
942, 115, 140, 169, 205, 249, 301, 365, 442, 536, 649, 787, 953,
117, 142, 172, 208, 252, 305, 370, 448, 542, 657, 796, 965, 118,
143, 174, 210, 255, 309, 374, 453, 549, 665, 806, 976, 120, 145,
176, 213, 258, 312, 379, 459, 556, 673, 816, 988]
def iseia(r, valid_types=(E6, E12, E24)):
'''
Check if a component is a valid EIA value.
By default, check 5% component values
'''
# Step 1: Discount things which are not numbers
if not isinstance(r, numbers.Number) or \
r < 0 or \
math.isnan(r) or \
math.isinf(r):
return False
# Special case: 0 is an okay resistor
if r == 0:
return True
# Step 2: Move into the range [100, 1000)
while r < 100:
r = r * 10
while r >= 1000:
r = r / 10
# Step 3: Discount things which are not integers, and cast to int
if abs(r - round(r)) > 0.01:
return False
r = int(round(r))
# Step 4: Check if we're a valid EIA value
for type_list in valid_types:
if r in type_list:
return True
if int(r / 10.) in type_list and (r % 10) == 0:
return True
return False
if __name__ == '__main__':
# Test cases. All of these should return True
print(iseia(100)) # 100 ohm resistor is EIA
print(not iseia(101)) # 101 is not
print(not iseia(100.3)) # Floating point close to EIA is not EIA
print(iseia(100.001)) # But within floating point error is
print(iseia(1e5)) # We handle big numbers well
print(iseia(2200)) # We handle middle-of-the-list well
# We can handle 1% components correctly; 2.2k is EIA24, but not EIA48.
print(not iseia(2200, (E48, E96, E192)))
print(iseia(5490e2, (E48, E96, E192)))
print(iseia(2200))
print(not iseia(5490e2))
print(iseia(1e-5)) # We handle little numbers well
print(not iseia("Hello")) # Junk handled okay
print(not iseia(float('NaN')))
print(not iseia(-1))
print(not iseia(iseia))
print(not iseia(float('Inf')))
print(iseia(0)) # Corner case. 0 is a standard resistor value.

View File

@@ -1,3 +0,0 @@
#!/usr/bin/python # lint-amnesty, pylint: disable=missing-module-docstring
from .loncapa_check import * # lint-amnesty, pylint: disable=redefined-builtin

View File

@@ -1,41 +0,0 @@
#!/usr/bin/python # lint-amnesty, pylint: disable=missing-module-docstring
#
# File: mitx/lib/loncapa/loncapa_check.py
#
# Python functions which duplicate the standard comparison functions available to LON-CAPA problems.
# Used in translating LON-CAPA problems to i4x problem specification language.
import math
import random
from six.moves import range
def lc_random(lower, upper, stepsize):
'''
like random.randrange but lower and upper can be non-integer
'''
nstep = int((upper - lower) / (1.0 * stepsize))
choices = [lower + x * stepsize for x in range(nstep)]
return random.choice(choices)
def lc_choose(index, *args):
'''
return args[index]
'''
try:
return args[int(index) - 1]
except Exception as err: # lint-amnesty, pylint: disable=broad-except, unused-variable
pass
if len(args): # lint-amnesty, pylint: disable=len-as-condition
return args[0]
raise Exception(
"loncapa_check.lc_choose error, index={index}, args={args}".format(
index=index,
args=args,
)
)
deg2rad = math.pi / 180.0
rad2deg = 180.0 / math.pi

View File

@@ -1,17 +0,0 @@
# lint-amnesty, pylint: disable=missing-module-docstring
from setuptools import setup
setup(
name="sandbox-packages",
version="0.1.1",
packages=[
"loncapa",
"verifiers",
],
py_modules=[
"eia",
],
install_requires=[
],
)

View File

@@ -1,433 +0,0 @@
""" Grader of drag and drop input.
Client side behavior: user can drag and drop images from list on base image.
Then json returned from client is:
{
"draggable": [
{ "image1": "t1" },
{ "ant": "t2" },
{ "molecule": "t3" },
]
}
values are target names.
or:
{
"draggable": [
{ "image1": "[10, 20]" },
{ "ant": "[30, 40]" },
{ "molecule": "[100, 200]" },
]
}
values are (x, y) coordinates of centers of dragged images.
"""
import json
import six
from six.moves import zip
def flat_user_answer(user_answer):
"""
Convert nested `user_answer` to flat format.
{'up': {'first': {'p': 'p_l'}}}
to
{'up': 'p_l[p][first]'}
"""
def parse_user_answer(answer):
key = list(answer.keys())[0]
value = list(answer.values())[0]
if isinstance(value, dict):
# Make complex value:
# Example:
# Create like 'p_l[p][first]' from {'first': {'p': 'p_l'}
complex_value_list = []
v_value = value
while isinstance(v_value, dict):
v_key = list(v_value.keys())[0]
v_value = list(v_value.values())[0]
complex_value_list.append(v_key)
complex_value = '{0}'.format(v_value)
for i in reversed(complex_value_list):
complex_value = '{0}[{1}]'.format(complex_value, i)
res = {key: complex_value}
return res
else:
return answer
result = []
for answer in user_answer:
parse_answer = parse_user_answer(answer)
result.append(parse_answer)
return result
class PositionsCompare(list):
""" Class for comparing positions.
Args:
list or string::
"abc" - target
[10, 20] - list of integers
[[10, 20], 200] list of list and integer
"""
def __eq__(self, other):
""" Compares two arguments.
Default lists behavior is conversion of string "abc" to list
["a", "b", "c"]. We will use that.
If self or other is empty - returns False.
Args:
self, other: str, unicode, list, int, float
Returns: bool
"""
# checks if self or other is not empty list (empty lists = false)
if not self or not other:
return False
if (isinstance(self[0], (list, int, float)) and
isinstance(other[0], (list, int, float))):
return self.coordinate_positions_compare(other)
elif (isinstance(self[0], (six.text_type, str)) and
isinstance(other[0], (six.text_type, str))):
return ''.join(self) == ''.join(other)
else: # improper argument types: no (float / int or lists of list
#and float / int pair) or two string / unicode lists pair
return False
def __ne__(self, other):
return not self.__eq__(other)
def coordinate_positions_compare(self, other, r=10):
""" Checks if self is equal to other inside radius of forgiveness
(default 10 px).
Args:
self, other: [x, y] or [[x, y], r], where r is radius of
forgiveness;
x, y, r: int
Returns: bool.
"""
# get max radius of forgiveness
if isinstance(self[0], list): # [(x, y), r] case
r = max(self[1], r)
x1, y1 = self[0]
else:
x1, y1 = self
if isinstance(other[0], list): # [(x, y), r] case
r = max(other[1], r)
x2, y2 = other[0]
else:
x2, y2 = other
if (x2 - x1) ** 2 + (y2 - y1) ** 2 > r * r:
return False
return True
class DragAndDrop(object):
""" Grader class for drag and drop inputtype.
"""
def grade(self):
''' Grader user answer.
Checks if every draggable isplaced on proper target or on proper
coordinates within radius of forgiveness (default is 10).
Returns: bool.
'''
for draggable in self.excess_draggables:
if self.excess_draggables[draggable]:
return False # user answer has more draggables than correct answer
# Number of draggables in user_groups may be differ that in
# correct_groups, that is incorrect, except special case with 'number'
for index, draggable_ids in enumerate(self.correct_groups):
# 'number' rule special case
# for reusable draggables we may get in self.user_groups
# {'1': [u'2', u'2', u'2'], '0': [u'1', u'1'], '2': [u'3']}
# if '+number' is in rule - do not remove duplicates and strip
# '+number' from rule
current_rule = list(self.correct_positions[index].keys())[0]
if 'number' in current_rule:
rule_values = self.correct_positions[index][current_rule]
# clean rule, do not do clean duplicate items
self.correct_positions[index].pop(current_rule, None)
parsed_rule = current_rule.replace('+', '').replace('number', '')
self.correct_positions[index][parsed_rule] = rule_values
else: # remove dublicates
self.user_groups[index] = list(set(self.user_groups[index]))
if sorted(draggable_ids) != sorted(self.user_groups[index]):
return False
# Check that in every group, for rule of that group, user positions of
# every element are equal with correct positions
for index, _ in enumerate(self.correct_groups):
rules_executed = 0
for rule in ('exact', 'anyof', 'unordered_equal'):
# every group has only one rule
if self.correct_positions[index].get(rule, None):
rules_executed += 1
if not self.compare_positions(
self.correct_positions[index][rule],
self.user_positions[index]['user'], flag=rule):
return False
if not rules_executed: # no correct rules for current group
# probably xml content mistake - wrong rules names
return False
return True
def compare_positions(self, correct, user, flag):
""" Compares two lists of positions with flag rules. Order of
correct/user arguments is matter only in 'anyof' flag.
Rules description:
'exact' means 1-1 ordered relationship::
[el1, el2, el3] is 'exact' equal to [el5, el6, el7] when
el1 == el5, el2 == el6, el3 == el7.
Equality function is custom, see below.
'anyof' means subset relationship::
user = [el1, el2] is 'anyof' equal to correct = [el1, el2, el3]
when
set(user) <= set(correct).
'anyof' is ordered relationship. It always checks if user
is subset of correct
Equality function is custom, see below.
Examples:
- many draggables per position:
user ['1', '2', '2', '2'] is 'anyof' equal to ['1', '2', '3']
- draggables can be placed in any order:
user ['1', '2', '3', '4'] is 'anyof' equal to ['4', '2', '1', 3']
'unordered_equal' is same as 'exact' but disregards on order
Equality functions:
Equality functon depends on type of element. They declared in
PositionsCompare class. For position like targets
ids ("t1", "t2", etc..) it is string equality function. For coordinate
positions ([1, 2] or [[1, 2], 15]) it is coordinate_positions_compare
function (see docstrings in PositionsCompare class)
Args:
correst, user: lists of positions
Returns: True if within rule lists are equal, otherwise False.
"""
if flag == 'exact':
if len(correct) != len(user):
return False
for el1, el2 in zip(correct, user):
if PositionsCompare(el1) != PositionsCompare(el2):
return False
if flag == 'anyof':
for u_el in user:
for c_el in correct:
if PositionsCompare(u_el) == PositionsCompare(c_el):
break
else:
# General: the else is executed after the for,
# only if the for terminates normally (not by a break)
# In this case, 'for' is terminated normally if every element
# from 'correct' list isn't equal to concrete element from
# 'user' list. So as we found one element from 'user' list,
# that not in 'correct' list - we return False
return False
if flag == 'unordered_equal':
if len(correct) != len(user):
return False
temp = correct[:]
for u_el in user:
for c_el in temp:
if PositionsCompare(u_el) == PositionsCompare(c_el):
temp.remove(c_el)
break
else:
# same as upper - if we found element from 'user' list,
# that not in 'correct' list - we return False.
return False
return True
def __init__(self, correct_answer, user_answer):
""" Populates DragAndDrop variables from user_answer and correct_answer.
If correct_answer is dict, converts it to list.
Correct answer in dict form is simple structure for fast and simple
grading. Example of correct answer dict example::
correct_answer = {'name4': 't1',
'name_with_icon': 't1',
'5': 't2',
'7': 't2'}
It is draggable_name: dragable_position mapping.
Advanced form converted from simple form uses 'exact' rule
for matching.
Correct answer in list form is designed for advanced cases::
correct_answers = [
{
'draggables': ['1', '2', '3', '4', '5', '6'],
'targets': [
's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2'],
'rule': 'anyof'},
{
'draggables': ['7', '8', '9', '10'],
'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'],
'rule': 'anyof'
}
]
Advanced answer in list form is list of dicts, and every dict must have
3 keys: 'draggables', 'targets' and 'rule'. 'Draggables' value is
list of draggables ids, 'targes' values are list of targets ids, 'rule'
value one of 'exact', 'anyof', 'unordered_equal', 'anyof+number',
'unordered_equal+number'
Advanced form uses "all dicts must match with their rule" logic.
Same draggable cannot appears more that in one dict.
Behavior is more widely explained in sphinx documentation.
Args:
user_answer: json
correct_answer: dict or list
"""
self.correct_groups = [] # Correct groups from xml.
self.correct_positions = [] # Correct positions for comparing.
self.user_groups = [] # Will be populated from user answer.
self.user_positions = [] # Will be populated from user answer.
# Convert from dict answer format to list format.
if isinstance(correct_answer, dict):
tmp = []
for key in sorted(correct_answer.keys()):
value = correct_answer[key]
tmp.append({
'draggables': [key],
'targets': [value],
'rule': 'exact'})
correct_answer = tmp
# Convert string `user_answer` to object.
user_answer = json.loads(user_answer)
# This dictionary will hold a key for each draggable the user placed on
# the image. The value is True if that draggable is not mentioned in any
# correct_answer entries. If the draggable is mentioned in at least one
# correct_answer entry, the value is False.
# default to consider every user answer excess until proven otherwise.
self.excess_draggables = dict(
(list(users_draggable.keys())[0], True)
for users_draggable in user_answer
)
# Convert nested `user_answer` to flat format.
user_answer = flat_user_answer(user_answer)
# Create identical data structures from user answer and correct answer.
for answer in correct_answer:
user_groups_data = []
user_positions_data = []
for draggable_dict in user_answer:
# Draggable_dict is 1-to-1 {draggable_name: position}.
draggable_name = list(draggable_dict.keys())[0]
if draggable_name in answer['draggables']:
user_groups_data.append(draggable_name)
user_positions_data.append(
draggable_dict[draggable_name]
)
# proved that this is not excess
self.excess_draggables[draggable_name] = False
self.correct_groups.append(answer['draggables'])
self.correct_positions.append({answer['rule']: answer['targets']})
self.user_groups.append(user_groups_data)
self.user_positions.append({'user': user_positions_data})
def grade(user_input, correct_answer):
""" Creates DragAndDrop instance from user_input and correct_answer and
calls DragAndDrop.grade for grading.
Supports two interfaces for correct_answer: dict and list.
Args:
user_input: json. Format::
{ "draggables":
[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]}'
or
{"draggables": [{"1": "t1"}, \
{"name_with_icon": "t2"}]}
correct_answer: dict or list.
Dict form::
{'1': 't1', 'name_with_icon': 't2'}
or
{'1': '[10, 10]', 'name_with_icon': '[[10, 10], 20]'}
List form::
correct_answer = [
{
'draggables': ['l3_o', 'l10_o'],
'targets': ['t1_o', 't9_o'],
'rule': 'anyof'
},
{
'draggables': ['l1_c', 'l8_c'],
'targets': ['t5_c', 't6_c'],
'rule': 'anyof'
}
]
Returns: bool
"""
return DragAndDrop(correct_answer=correct_answer,
user_answer=user_input).grade()

View File

@@ -1,843 +0,0 @@
# lint-amnesty, pylint: disable=missing-module-docstring
import json
import unittest
from . import draganddrop
from .draganddrop import PositionsCompare
class Test_PositionsCompare(unittest.TestCase):
""" describe"""
def test_nested_list_and_list1(self):
assert PositionsCompare([[1, 2], 40]) == PositionsCompare([1, 3])
def test_nested_list_and_list2(self):
assert PositionsCompare([1, 12]) != PositionsCompare([1, 1])
def test_list_and_list1(self):
assert PositionsCompare([[1, 2], 12]) != PositionsCompare([1, 15])
def test_list_and_list2(self):
assert PositionsCompare([1, 11]) == PositionsCompare([1, 1])
def test_numerical_list_and_string_list(self):
assert PositionsCompare([1, 2]) != PositionsCompare(['1'])
def test_string_and_string_list1(self):
assert PositionsCompare('1') == PositionsCompare(['1'])
def test_string_and_string_list2(self):
assert PositionsCompare('abc') == PositionsCompare('abc')
def test_string_and_string_list3(self):
assert PositionsCompare('abd') != PositionsCompare('abe')
def test_float_and_string(self):
assert PositionsCompare([3.5, 5.7]) != PositionsCompare(['1'])
def test_floats_and_ints(self):
assert PositionsCompare([3.5, 4.5]) == PositionsCompare([5, 7])
class Test_DragAndDrop_Grade(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def test_targets_are_draggable_1(self):
user_input = json.dumps([
{'p': 'p_l'},
{'up': {'first': {'p': 'p_l'}}}
])
correct_answer = [
{
'draggables': ['p'],
'targets': ['p_l', 'p_r'],
'rule': 'anyof'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][first]'
],
'rule': 'anyof'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_targets_are_draggable_2(self):
user_input = json.dumps([
{'p': 'p_l'},
{'p': 'p_r'},
{'s': 's_l'},
{'s': 's_r'},
{'up': {'1': {'p': 'p_l'}}},
{'up': {'3': {'p': 'p_l'}}},
{'up': {'1': {'p': 'p_r'}}},
{'up': {'3': {'p': 'p_r'}}},
{'up_and_down': {'1': {'s': 's_l'}}},
{'up_and_down': {'1': {'s': 's_r'}}}
])
correct_answer = [
{
'draggables': ['p'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': ['s_l', 's_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': ['s_l[s][1]', 's_r[s][1]'],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][1]',
'p_l[p][3]',
'p_r[p][1]',
'p_r[p][3]',
],
'rule': 'unordered_equal'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_targets_are_draggable_2_manual_parsing(self):
user_input = json.dumps([
{'up': 'p_l[p][1]'},
{'p': 'p_l'},
{'up': 'p_l[p][3]'},
{'up': 'p_r[p][1]'},
{'p': 'p_r'},
{'up': 'p_r[p][3]'},
{'up_and_down': 's_l[s][1]'},
{'s': 's_l'},
{'up_and_down': 's_r[s][1]'},
{'s': 's_r'}
])
correct_answer = [
{
'draggables': ['p'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': ['s_l', 's_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': ['s_l[s][1]', 's_r[s][1]'],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'p_l[p][1]',
'p_l[p][3]',
'p_r[p][1]',
'p_r[p][3]',
],
'rule': 'unordered_equal'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_targets_are_draggable_3_nested(self):
user_input = json.dumps([
{'molecule': 'left_side_tagret'},
{'molecule': 'right_side_tagret'},
{'p': {'p_target': {'molecule': 'left_side_tagret'}}},
{'p': {'p_target': {'molecule': 'right_side_tagret'}}},
{'s': {'s_target': {'molecule': 'left_side_tagret'}}},
{'s': {'s_target': {'molecule': 'right_side_tagret'}}},
{'up': {'1': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}},
{'up': {'3': {'p': {'p_target': {'molecule': 'left_side_tagret'}}}}},
{'up': {'1': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}},
{'up': {'3': {'p': {'p_target': {'molecule': 'right_side_tagret'}}}}},
{'up_and_down': {'1': {'s': {'s_target': {'molecule': 'left_side_tagret'}}}}},
{'up_and_down': {'1': {'s': {'s_target': {'molecule': 'right_side_tagret'}}}}}
])
correct_answer = [
{
'draggables': ['molecule'],
'targets': ['left_side_tagret', 'right_side_tagret'],
'rule': 'unordered_equal'
},
{
'draggables': ['p'],
'targets': [
'left_side_tagret[molecule][p_target]',
'right_side_tagret[molecule][p_target]',
],
'rule': 'unordered_equal'
},
{
'draggables': ['s'],
'targets': [
'left_side_tagret[molecule][s_target]',
'right_side_tagret[molecule][s_target]',
],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': [
'left_side_tagret[molecule][s_target][s][1]',
'right_side_tagret[molecule][s_target][s][1]',
],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'left_side_tagret[molecule][p_target][p][1]',
'left_side_tagret[molecule][p_target][p][3]',
'right_side_tagret[molecule][p_target][p][1]',
'right_side_tagret[molecule][p_target][p][3]',
],
'rule': 'unordered_equal'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_targets_are_draggable_4_real_example(self):
user_input = json.dumps([
{'single_draggable': 's_l'},
{'single_draggable': 's_r'},
{'single_draggable': 'p_sigma'},
{'single_draggable': 'p_sigma*'},
{'single_draggable': 's_sigma'},
{'single_draggable': 's_sigma*'},
{'double_draggable': 'p_pi*'},
{'double_draggable': 'p_pi'},
{'triple_draggable': 'p_l'},
{'triple_draggable': 'p_r'},
{'up': {'1': {'triple_draggable': 'p_l'}}},
{'up': {'2': {'triple_draggable': 'p_l'}}},
{'up': {'2': {'triple_draggable': 'p_r'}}},
{'up': {'3': {'triple_draggable': 'p_r'}}},
{'up_and_down': {'1': {'single_draggable': 's_l'}}},
{'up_and_down': {'1': {'single_draggable': 's_r'}}},
{'up_and_down': {'1': {'single_draggable': 's_sigma'}}},
{'up_and_down': {'1': {'single_draggable': 's_sigma*'}}},
{'up_and_down': {'1': {'double_draggable': 'p_pi'}}},
{'up_and_down': {'2': {'double_draggable': 'p_pi'}}}
])
# 10 targets:
# s_l, s_r, p_l, p_r, s_sigma, s_sigma*, p_pi, p_sigma, p_pi*, p_sigma*
#
# 3 draggable objects, which have targets (internal target ids - 1, 2, 3):
# single_draggable, double_draggable, triple_draggable
#
# 2 draggable objects:
# up, up_and_down
correct_answer = [
{
'draggables': ['triple_draggable'],
'targets': ['p_l', 'p_r'],
'rule': 'unordered_equal'
},
{
'draggables': ['double_draggable'],
'targets': ['p_pi', 'p_pi*'],
'rule': 'unordered_equal'
},
{
'draggables': ['single_draggable'],
'targets': ['s_l', 's_r', 's_sigma', 's_sigma*', 'p_sigma', 'p_sigma*'],
'rule': 'unordered_equal'
},
{
'draggables': ['up'],
'targets': [
'p_l[triple_draggable][1]',
'p_l[triple_draggable][2]',
'p_r[triple_draggable][2]',
'p_r[triple_draggable][3]',
],
'rule': 'unordered_equal'
},
{
'draggables': ['up_and_down'],
'targets': [
's_l[single_draggable][1]',
's_r[single_draggable][1]',
's_sigma[single_draggable][1]',
's_sigma*[single_draggable][1]',
'p_pi[double_draggable][1]',
'p_pi[double_draggable][2]',
],
'rule': 'unordered_equal'
},
]
assert draganddrop.grade(user_input, correct_answer)
def test_targets_true(self):
user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2'}
assert draganddrop.grade(user_input, correct_answer)
def test_expect_no_actions_wrong(self):
user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]'
correct_answer = []
assert not draganddrop.grade(user_input, correct_answer)
def test_expect_no_actions_right(self):
user_input = '[]'
correct_answer = []
assert draganddrop.grade(user_input, correct_answer)
def test_targets_false(self):
user_input = '[{"1": "t1"}, \
{"name_with_icon": "t2"}]'
correct_answer = {'1': 't3', 'name_with_icon': 't2'}
assert not draganddrop.grade(user_input, correct_answer)
def test_multiple_images_per_target_true(self):
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2', '2': 't1'}
assert draganddrop.grade(user_input, correct_answer)
def test_multiple_images_per_target_false(self):
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}, \
{"2": "t1"}]'
correct_answer = {'1': 't2', 'name_with_icon': 't2', '2': 't1'}
assert not draganddrop.grade(user_input, correct_answer)
def test_targets_and_positions(self):
user_input = '[{"1": [10,10]}, \
{"name_with_icon": [[10,10],4]}]'
correct_answer = {'1': [10, 10], 'name_with_icon': [[10, 10], 4]}
assert draganddrop.grade(user_input, correct_answer)
def test_position_and_targets(self):
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]'
correct_answer = {'1': 't1', 'name_with_icon': 't2'}
assert draganddrop.grade(user_input, correct_answer)
def test_positions_exact(self):
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [10, 10], 'name_with_icon': [20, 20]}
assert draganddrop.grade(user_input, correct_answer)
def test_positions_false(self):
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [25, 25], 'name_with_icon': [20, 20]}
assert not draganddrop.grade(user_input, correct_answer)
def test_positions_true_in_radius(self):
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [14, 14], 'name_with_icon': [20, 20]}
assert draganddrop.grade(user_input, correct_answer)
def test_positions_true_in_manual_radius(self):
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [[40, 10], 30], 'name_with_icon': [20, 20]}
assert draganddrop.grade(user_input, correct_answer)
def test_positions_false_in_manual_radius(self):
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
assert not draganddrop.grade(user_input, correct_answer)
def test_correct_answer_not_has_key_from_user_answer(self):
user_input = '[{"1": "t1"}, {"name_with_icon": "t2"}]'
correct_answer = {'3': 't3', 'name_with_icon': 't2'}
assert not draganddrop.grade(user_input, correct_answer)
def test_anywhere(self):
"""Draggables can be places anywhere on base image.
Place grass in the middle of the image and ant in the
right upper corner."""
user_input = '[{"ant":[610.5,57.449951171875]},\
{"grass":[322.5,199.449951171875]}]'
correct_answer = {'grass': [[300, 200], 200], 'ant': [[500, 0], 200]}
assert draganddrop.grade(user_input, correct_answer)
def test_lcao_correct(self):
"""Describe carbon molecule in LCAO-MO"""
user_input = '[{"1":"s_left"}, \
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \
{"8":"p_left_2"},{"10":"p_right_1"},{"9":"p_right_2"}, \
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer = [{
'draggables': ['1', '2', '3', '4', '5', '6'],
'targets': [
's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2'
],
'rule': 'anyof'
}, {
'draggables': ['7', '8', '9', '10'],
'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'],
'rule': 'anyof'
}, {
'draggables': ['11', '12'],
'targets': ['s_sigma_name', 'p_sigma_name'],
'rule': 'anyof'
}, {
'draggables': ['13', '14'],
'targets': ['s_sigma_star_name', 'p_sigma_star_name'],
'rule': 'anyof'
}, {
'draggables': ['15'],
'targets': ['p_pi_name'],
'rule': 'anyof'
}, {
'draggables': ['16'],
'targets': ['p_pi_star_name'],
'rule': 'anyof'
}]
assert draganddrop.grade(user_input, correct_answer)
def test_lcao_extra_element_incorrect(self):
"""Describe carbon molecule in LCAO-MO"""
user_input = '[{"1":"s_left"}, \
{"5":"s_right"},{"4":"s_sigma"},{"6":"s_sigma_star"},{"7":"p_left_1"}, \
{"8":"p_left_2"},{"17":"p_left_3"},{"10":"p_right_1"},{"9":"p_right_2"}, \
{"2":"p_pi_1"},{"3":"p_pi_2"},{"11":"s_sigma_name"}, \
{"13":"s_sigma_star_name"},{"15":"p_pi_name"},{"16":"p_pi_star_name"}, \
{"12":"p_sigma_name"},{"14":"p_sigma_star_name"}]'
correct_answer = [{
'draggables': ['1', '2', '3', '4', '5', '6'],
'targets': [
's_left', 's_right', 's_sigma', 's_sigma_star', 'p_pi_1', 'p_pi_2'
],
'rule': 'anyof'
}, {
'draggables': ['7', '8', '9', '10'],
'targets': ['p_left_1', 'p_left_2', 'p_right_1', 'p_right_2'],
'rule': 'anyof'
}, {
'draggables': ['11', '12'],
'targets': ['s_sigma_name', 'p_sigma_name'],
'rule': 'anyof'
}, {
'draggables': ['13', '14'],
'targets': ['s_sigma_star_name', 'p_sigma_star_name'],
'rule': 'anyof'
}, {
'draggables': ['15'],
'targets': ['p_pi_name'],
'rule': 'anyof'
}, {
'draggables': ['16'],
'targets': ['p_pi_star_name'],
'rule': 'anyof'
}]
assert not draganddrop.grade(user_input, correct_answer)
def test_reuse_draggable_no_mupliples(self):
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target3"},{"2":"target4"},{"2":"target5"}, \
{"3":"target6"}]'
correct_answer = [
{
'draggables': ['1'],
'targets': ['target1', 'target3'],
'rule': 'anyof'
},
{
'draggables': ['2'],
'targets': ['target2', 'target4', 'target5'],
'rule': 'anyof'
},
{
'draggables': ['3'],
'targets': ['target6'],
'rule': 'anyof'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_reuse_draggable_with_mupliples(self):
"""Test reusable draggables with mupltiple draggables per target"""
user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \
{"3":"target6"}]'
correct_answer = [
{
'draggables': ['1'],
'targets': ['target1', 'target3'],
'rule': 'anyof'
},
{
'draggables': ['2'],
'targets': ['target2', 'target4'],
'rule': 'anyof'
},
{
'draggables': ['3'],
'targets': ['target6'],
'rule': 'anyof'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_reuse_many_draggable_with_mupliples(self):
"""Test reusable draggables with mupltiple draggables per target"""
user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"},{"2":"target4"},{"2":"target4"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]'
correct_answer = [
{
'draggables': ['1', '4'],
'targets': ['target1', 'target3'],
'rule': 'anyof'
},
{
'draggables': ['2', '6'],
'targets': ['target2', 'target4'],
'rule': 'anyof'
},
{
'draggables': ['5'],
'targets': ['target4', 'target5'],
'rule': 'anyof'
},
{
'draggables': ['3'],
'targets': ['target6'],
'rule': 'anyof'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_reuse_many_draggable_with_mupliples_wrong(self):
"""Test reusable draggables with mupltiple draggables per target"""
user_input = '[{"1":"target1"}, \
{"2":"target2"},{"1":"target1"}, \
{"2":"target3"}, \
{"2":"target4"}, \
{"3":"target6"}, {"4": "target3"}, {"5": "target4"}, \
{"5": "target5"}, {"6": "target2"}]'
correct_answer = [
{
'draggables': ['1', '4'],
'targets': ['target1', 'target3'],
'rule': 'anyof'
},
{
'draggables': ['2', '6'],
'targets': ['target2', 'target4'],
'rule': 'anyof'
},
{
'draggables': ['5'],
'targets': ['target4', 'target5'],
'rule': 'anyof'
},
{
'draggables': ['3'],
'targets': ['target6'],
'rule': 'anyof'
}]
assert not draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_false(self):
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a'],
'targets': ['target1', 'target4', 'target7', 'target10'],
'rule': 'unordered_equal'
},
{
'draggables': ['b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'unordered_equal'
},
{
'draggables': ['c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'unordered_equal'
}
]
assert not draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_(self):
"""Test reusable draggables (no mupltiple draggables per target)"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a'],
'targets': ['target1', 'target4', 'target7', 'target10'],
'rule': 'unordered_equal'
},
{
'draggables': ['b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'unordered_equal'
},
{
'draggables': ['c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'unordered_equal'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_multiple(self):
"""Test reusable draggables (mupltiple draggables per target)"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'a'],
'targets': ['target1', 'target4', 'target7', 'target10'],
'rule': 'anyof+number'
},
{
'draggables': ['b', 'b', 'b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'anyof+number'
},
{
'draggables': ['c', 'c', 'c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'anyof+number'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_multiple_false(self):
"""Test reusable draggables (mupltiple draggables per target)"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"a":"target4"},{"b":"target5"}, \
{"c":"target6"}, {"a":"target7"},{"b":"target8"},{"c":"target9"}, \
{"a":"target1"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'a'],
'targets': ['target1', 'target4', 'target7', 'target10'],
'rule': 'anyof+number'
},
{
'draggables': ['b', 'b', 'b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'anyof+number'
},
{
'draggables': ['c', 'c', 'c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'anyof+number'
}
]
assert not draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_reused(self):
"""Test a b c in 10 labels reused"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, \
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a'],
'targets': ['target1', 'target10'],
'rule': 'unordered_equal+number'
},
{
'draggables': ['b', 'b', 'b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'unordered_equal+number'
},
{
'draggables': ['c', 'c', 'c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'unordered_equal+number'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_label_10_targets_with_a_b_c_reused_false(self):
"""Test a b c in 10 labels reused false"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"},{"b":"target5"}, {"a":"target8"},\
{"c":"target6"}, {"b":"target8"},{"c":"target9"}, \
{"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a'],
'targets': ['target1', 'target10'],
'rule': 'unordered_equal+number'
},
{
'draggables': ['b', 'b', 'b'],
'targets': ['target2', 'target5', 'target8'],
'rule': 'unordered_equal+number'
},
{
'draggables': ['c', 'c', 'c'],
'targets': ['target3', 'target6', 'target9'],
'rule': 'unordered_equal+number'
}
]
assert not draganddrop.grade(user_input, correct_answer)
def test_mixed_reuse_and_not_reuse(self):
"""Test reusable draggables """
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"},\
{"a":"target5"}]'
correct_answer = [
{
'draggables': ['a', 'b'],
'targets': ['target1', 'target2', 'target4', 'target5'],
'rule': 'anyof'
},
{
'draggables': ['c'],
'targets': ['target3'],
'rule': 'exact'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_mixed_reuse_and_not_reuse_number(self):
"""Test reusable draggables with number """
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'b'],
'targets': ['target1', 'target2', 'target4'],
'rule': 'anyof+number'
},
{
'draggables': ['c'],
'targets': ['target3'],
'rule': 'exact'
}
]
assert draganddrop.grade(user_input, correct_answer)
def test_mixed_reuse_and_not_reuse_number_false(self):
"""Test reusable draggables with numbers, but wrong"""
user_input = '[{"a":"target1"}, \
{"b":"target2"},{"c":"target3"}, {"a":"target4"}, {"a":"target10"}]'
correct_answer = [
{
'draggables': ['a', 'a', 'b'],
'targets': ['target1', 'target2', 'target4', 'target10'],
'rule': 'anyof_number'
},
{
'draggables': ['c'],
'targets': ['target3'],
'rule': 'exact'
}
]
assert not draganddrop.grade(user_input, correct_answer)
def test_alternative_correct_answer(self):
user_input = '[{"name_with_icon":"t1"},\
{"name_with_icon":"t1"},{"name_with_icon":"t1"},{"name4":"t1"}, \
{"name4":"t1"}]'
correct_answer = [
{'draggables': ['name4'], 'targets': ['t1', 't1'], 'rule': 'exact'},
{'draggables': ['name_with_icon'], 'targets': ['t1', 't1', 't1'],
'rule': 'exact'}
]
assert draganddrop.grade(user_input, correct_answer)
class Test_DragAndDrop_Populate(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def test_1(self):
correct_answer = {'1': [[40, 10], 29], 'name_with_icon': [20, 20]}
user_input = '[{"1": [10, 10]}, {"name_with_icon": [20, 20]}]'
dnd = draganddrop.DragAndDrop(correct_answer, user_input)
correct_groups = [['1'], ['name_with_icon']]
correct_positions = [{'exact': [[[40, 10], 29]]}, {'exact': [[20, 20]]}]
user_groups = [['1'], ['name_with_icon']]
user_positions = [{'user': [[10, 10]]}, {'user': [[20, 20]]}]
assert correct_groups == dnd.correct_groups
assert correct_positions == dnd.correct_positions
assert user_groups == dnd.user_groups
assert user_positions == dnd.user_positions
class Test_DraAndDrop_Compare_Positions(unittest.TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def test_1(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='anyof')
def test_2a(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 3], [1, 1]], flag='exact')
def test_2b(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert not dnd.compare_positions(correct=[[1, 1], [2, 3]], user=[[2, 13], [1, 1]], flag='exact')
def test_3(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert not dnd.compare_positions(correct=['a', 'b'], user=['a', 'b', 'c'], flag='anyof')
def test_4(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'b'], flag='anyof')
def test_5(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert not dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'c', 'b'], flag='exact')
def test_6(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert dnd.compare_positions(correct=['a', 'b', 'c'], user=['a', 'c', 'b'], flag='anyof')
def test_7(self):
dnd = draganddrop.DragAndDrop({'1': 't1'}, '[{"1": "t1"}]')
assert not dnd.compare_positions(correct=['a', 'b', 'b'], user=['a', 'c', 'b'], flag='anyof')
def suite(): # lint-amnesty, pylint: disable=missing-function-docstring
testcases = [Test_PositionsCompare,
Test_DragAndDrop_Populate,
Test_DragAndDrop_Grade,
Test_DraAndDrop_Compare_Positions]
suites = []
for testcase in testcases:
suites.append(unittest.TestLoader().loadTestsFromTestCase(testcase))
return unittest.TestSuite(suites)
if __name__ == "__main__":
unittest.TextTestRunner(verbosity=2).run(suite())

View File

@@ -71,10 +71,7 @@ def top_python_dirs(dirname):
dirs = os.listdir(subdir)
top_dirs.extend(d for d in dirs if os.path.isdir(os.path.join(subdir, d)))
# sandbox-packages module causes F0001: module-not-found error when running pylint
# this will exclude sandbox-packages module from pylint execution
# TODO: upgrade the functionality to run pylint tests on sandbox-packages module too.
modules_to_remove = ['sandbox-packages', '__pycache__']
modules_to_remove = ['__pycache__']
for module in modules_to_remove:
if module in top_dirs:
top_dirs.remove(module)

View File

@@ -11,12 +11,11 @@ pyparsing # Python Parsing module
random2 # Implementation of random module that works identically under Python 2 and 3
scipy # Math, science, and engineering library
sympy # Symbolic math library
codejail-includes # CodeJail manages execution of untrusted code in secure sandboxes.
# numpy>=1.17.0 caused failures in importing numpy in code-jail environment.
# The issue will be investigated and fixed in https://openedx.atlassian.net/browse/BOM-2841.
numpy>=1.16.0,<1.17.0
# Install these packages from the edx-platform working tree
# NOTE: if you change code in these packages, you MUST change the version
# number in its setup.py or the code WILL NOT be installed during deploy.
-e common/lib/sandbox-packages

View File

@@ -4,8 +4,6 @@
#
# make upgrade
#
common/lib/sandbox-packages
# via -r requirements/edx-sandbox/py38.in
cffi==1.15.0
# via cryptography
chem==1.2.0
@@ -14,6 +12,8 @@ click==8.1.3
# via
# -c requirements/edx-sandbox/../constraints.txt
# nltk
codejail-includes==1.0.0
# via -r requirements/edx-sandbox/py38.in
cryptography==37.0.2
# via -r requirements/edx-sandbox/py38.in
cycler==0.11.0
@@ -82,6 +82,7 @@ scipy==1.7.3
six==1.16.0
# via
# chem
# codejail-includes
# python-dateutil
sympy==1.10.1
# via

View File

@@ -36,6 +36,7 @@ botocore==1.8.17 # via boto3, s3transfer
bridgekeeper # Used for determining permissions for courseware.
celery # Asynchronous task execution library
chem # A helper library for chemistry calculations
codejail-includes # CodeJail manages execution of untrusted code in secure sandboxes.
contextlib2 # We need contextlib2.ExitStack so we can stop using contextlib.nested which doesn't exist in python 3
crowdsourcehinter-xblock
cryptography # Implementations of assorted cryptography algorithms

View File

@@ -22,8 +22,6 @@
# via -r requirements/edx/local.in
-e git+https://github.com/edx/RateXBlock.git@2.0.1#egg=rate-xblock
# via -r requirements/edx/github.in
-e common/lib/sandbox-packages
# via -r requirements/edx/local.in
-e openedx/core/lib/xblock_builtin/xblock_discussion
# via -r requirements/edx/local.in
-e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive
@@ -143,6 +141,8 @@ code-annotations==1.3.0
# via
# edx-enterprise
# edx-toggles
codejail-includes==1.0.0
# via -r requirements/edx/base.in
contextlib2==21.6.0
# via -r requirements/edx/base.in
coreapi==2.3.3
@@ -962,6 +962,7 @@ six==1.16.0
# chem
# click-repl
# codejail
# codejail-includes
# crowdsourcehinter-xblock
# edx-ace
# edx-auth-backends

View File

@@ -22,8 +22,6 @@
# via -r requirements/edx/testing.txt
-e git+https://github.com/edx/RateXBlock.git@2.0.1#egg=rate-xblock
# via -r requirements/edx/testing.txt
-e common/lib/sandbox-packages
# via -r requirements/edx/testing.txt
-e openedx/core/lib/xblock_builtin/xblock_discussion
# via -r requirements/edx/testing.txt
-e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive
@@ -203,6 +201,8 @@ code-annotations==1.3.0
# edx-enterprise
# edx-lint
# edx-toggles
codejail-includes==1.0.0
# via -r requirements/edx/testing.txt
contextlib2==21.6.0
# via -r requirements/edx/testing.txt
coreapi==2.3.3
@@ -1334,6 +1334,7 @@ six==1.16.0
# chem
# click-repl
# codejail
# codejail-includes
# crowdsourcehinter-xblock
# edx-ace
# edx-auth-backends

View File

@@ -1,7 +1,6 @@
# Python libraries to install that are local to the edx-platform repo
-e .
-e common/lib/capa
-e common/lib/sandbox-packages
-e common/lib/xmodule
-e openedx/core/lib/xblock_builtin/xblock_discussion

View File

@@ -22,8 +22,6 @@
# via -r requirements/edx/base.txt
-e git+https://github.com/edx/RateXBlock.git@2.0.1#egg=rate-xblock
# via -r requirements/edx/base.txt
-e common/lib/sandbox-packages
# via -r requirements/edx/base.txt
-e openedx/core/lib/xblock_builtin/xblock_discussion
# via -r requirements/edx/base.txt
-e git+https://github.com/edx-solutions/xblock-google-drive.git@2d176468e33c0713c911b563f8f65f7cf232f5b6#egg=xblock-google-drive
@@ -195,6 +193,8 @@ code-annotations==1.3.0
# edx-enterprise
# edx-lint
# edx-toggles
codejail-includes==1.0.0
# via -r requirements/edx/base.txt
contextlib2==21.6.0
# via -r requirements/edx/base.txt
coreapi==2.3.3
@@ -1261,6 +1261,7 @@ six==1.16.0
# chem
# click-repl
# codejail
# codejail-includes
# crowdsourcehinter-xblock
# edx-ace
# edx-auth-backends

View File

@@ -38,7 +38,7 @@ exclude+='|^common/test/data/?.*$'
# * common/lib/xmodule -> EXCLUDE from check.
# * common/lib/xmodule/xmodule/modulestore -> INCLUDE in check.
exclude+='|^common/lib$'
exclude+='|^common/lib/(capa|sandbox-packages|xmodule)$'
exclude+='|^common/lib/(capa|xmodule)$'
# Docs, scripts.
exclude+='|^docs/.*$'