def test_instance_is_reused(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES(), type='categorical') variable = Variable(resource, MagicMock()) cat_list = variable.categories self.assertTrue(isinstance(cat_list, CategoryList))
def test_edit_category_date(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES_WITH_DATE(), type='categorical') variable = Variable(resource, MagicMock()) variable.categories[1].edit(date='2021-01-01') resource.entity._edit.assert_called_with( categories=[{ 'numeric_value': None, 'selected': False, 'id': 1, 'missing': False, 'name': 'Female', 'date': '2021-01-01' }, { 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Male', 'date': '2020-02-02' }, { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' }]) resource.entity.refresh.assert_called_once() self.assertEqual(variable.categories[1].date, '2021-01-01')
def test_reorder(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES(), type='categorical') variable = Variable(resource, MagicMock()) variable.categories.order(2, -1, 1) # Reordered values resource.entity._edit.assert_called_with(categories=[{ 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Male' }, { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' }, { 'numeric_value': None, 'missing': False, 'id': 1, 'name': 'Female' }]) resource.entity.refresh.assert_called_once()
def test_read_category_date(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES_WITH_DATE(), type='categorical') variable = Variable(resource, MagicMock()) self.assertEqual(variable.categories[1].date, '2020-01-01') self.assertEqual(variable.categories[2].date, '2020-02-02') with self.assertRaises(KeyError): # The `No Data` category doesn't provide a `date field _ = variable.categories[3].date
def test_edit_derived(self): resource = EditableMock() resource.entity.body = dict( categories=TEST_CATEGORIES(), type='categorical', derivation={'function': 'derivation_function'}) variable = Variable(resource, MagicMock()) error_msg = "Cannot edit categories on derived variables. Re-derive with the appropriate expression" with pytest.raises(TypeError, message=error_msg): variable.categories[1].edit(name='Mujer') # Try again with an empty derivation resource = EditableMock() resource.entity.body = dict( categories=TEST_CATEGORIES(), type='categorical', derivation={} # Empty ) variable = Variable(resource, MagicMock()) variable.categories[1].edit(name='Mujer') resource.entity._edit.assert_called_with(categories=[{ 'numeric_value': None, 'selected': False, 'id': 1, 'missing': False, 'name': 'Mujer' }, { 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Male' }, { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' }])
def test_validate_range_expression(self): test_map = {1: range(1, 5)} test_cats = {1: "China"} ds_res_mock = mock.MagicMock() variable_mock = mock.MagicMock() subvar_mock = mock.MagicMock(entity_url=subvar1_url) # mock the call to entity, this will happen on Variable.resource variable_mock.entity.subvariables.by.return_value = { 'parent_1': subvar_mock, 'parent_2': subvar_mock, 'parent_3': subvar_mock, 'parent_4': subvar_mock, } parent_var = Variable(variable_mock, ds_res_mock) modified_map = responses_from_map(parent_var, test_map, test_cats, 'test', 'parent') # subvar_url * 4 because we used the same mock for all subvars assert modified_map[0]['combined_ids'] == [subvar1_url] * 4
def test_delete_category(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES(), type='categorical') variable = Variable(resource, MagicMock()) variable.categories[1].delete() # Calling edit without the one that we wanted to delete resource.entity._edit.assert_called_with(categories=[{ 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Male' }, { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' }])
def test_combine_categories_from_entity(self): resource = mock.MagicMock() resource.body = {'name': 'mocked_dataset'} entity_mock = mock.MagicMock() entity_mock.entity.self = var_url resource.variables.by.return_value = { 'test': entity_mock } resource.variables.index = {} # Var not present # mock a Tuple object tuple_mock = mock.MagicMock() tuple_mock.entity.self = var_url entity = Variable(tuple_mock, resource) ds = MutableDataset(resource) with pytest.raises(ValueError) as err: ds.combine_categorical(entity, CATEGORY_MAP, CATEGORY_NAMES, name='name', alias='alias') ds.resource.variables.create.assert_called_with(RECODES_PAYLOAD) assert 'Entity mocked_dataset has no (sub)variable' in str(err.value)
def test_category_attribute_writes(self): resource = EditableMock() resource.entity.body = dict( categories=TEST_CATEGORIES(), type='categorical', ) variable = Variable(resource, MagicMock()) error_msg = "use the edit() method for mutating attributes" with pytest.raises(AttributeError) as excinfo: variable.categories[1].id = 42 # nothing has changed assert variable.categories[1].id == 1 assert str(excinfo.value) == "Can't edit attibute 'id'" with pytest.raises(AttributeError) as excinfo: variable.categories[1].name = 'forbidden' # nothing has changed assert variable.categories[1].name == 'Female' assert str(excinfo.value) == error_msg with pytest.raises(AttributeError) as excinfo: variable.categories[1].numeric_value = 42 # nothing has changed assert variable.categories[1].numeric_value is None assert str(excinfo.value) == error_msg with pytest.raises(AttributeError) as excinfo: variable.categories[1].missing = True # nothing has changed assert variable.categories[1].missing is False assert str(excinfo.value) == error_msg with pytest.raises(AttributeError) as excinfo: variable.categories[1].selected = True # nothing has changed, default is False assert variable.categories[1].selected is False assert str(excinfo.value) == error_msg
def test_category_dict_attribytes(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES(), type='categorical') variable = Variable(resource, MagicMock()) # Does not have `date` unnecessarily assert variable.categories[1].as_dict() == { 'id': 1, 'missing': False, 'name': 'Female', 'numeric_value': None, 'selected': False } variable.categories[1].edit(date="1990-02-04") # Contains .date if needed assert variable.categories[1].as_dict() == { 'id': 1, 'missing': False, 'name': 'Female', 'numeric_value': None, 'selected': False, "date": "1990-02-04" }
def test_recode_multiple_responses(self, get_dataset_mock): dataset_id = '123' categories = [{ 'numeric_value': 1, 'selected': True, 'id': 1, 'name': 'selected', 'missing': False }, { 'numeric_value': 2, 'selected': False, 'id': 2, 'name': 'not selected', 'missing': False }, { 'numeric_value': 9, 'missing': True, 'id': 9, 'name': 'not asked' }, { 'numeric_value': 8, 'missing': True, 'id': 8, 'name': 'skipped' }] var_res = Entity( mock.MagicMock(), **{ 'element': 'shoji:entity', 'self': 'http://test.crunch.io/api/datasets/%s/variables/0001/' % dataset_id, # needed in order to simulate a Tuple, now Variable is inited with Tuple 'entity_url': 'http://test.crunch.io/api/datasets/%s/variables/0001/' % dataset_id, 'body': { 'name': 'Q1', 'subreferences': [{ 'alias': 'Q1_1', 'is_subvar': True, 'name': 'One' }, { 'alias': 'Q1_2', 'is_subvar': True, 'name': 'Two' }, { 'alias': 'Q1_3', 'is_subvar': True, 'name': 'Three' }], 'missing_reasons': { 'skipped': 8, 'not asked': 9 }, 'alias': 'Q1', 'subvariables': [ 'http://test.crunch.io/api/datasets/%s/variables/0001/subvariables/000a/' % dataset_id, 'http://test.crunch.io/api/datasets/%s/variables/0001/subvariables/000b/' % dataset_id, 'http://test.crunch.io/api/datasets/%s/variables/0001/subvariables/000c/' % dataset_id ], 'dataset_id': dataset_id, 'type': 'multiple_response', 'id': '0001', 'categories': categories, 'description': 'Multiple Response Example' } }) table_mock = mock.MagicMock( metadata={ '00001': { 'id': '00001', 'alias': 'sexuality', 'type': 'categorical', 'categories': categories } }) ds_res = mock.MagicMock() Variable(var_res, ds_res) ds_res.self = dataset_url ds_res.follow.return_value = table_mock dataset = Dataset(ds_res) subvar_mock = mock.MagicMock() subvar_mock.self = var_url subvar_mock.id = 'subvar' subvariables = { 'Q1_1': subvar_mock, 'Q1_2': subvar_mock, 'Q1_3': subvar_mock, } dataset.create_categorical( [{ 'id': 1, 'name': 'Q1_recoded_1', 'case': mr_in(var_url, 'Q1', [1, 2], subvariables) }, { 'id': 2, 'name': 'Q1_recoded_2', 'case': mr_in(var_url, 'Q1', [3], subvariables) }], alias='Q1_recoded', name='Q1_recoded', multiple=True) # Test how the recoded var was created. ds_res.variables.create.assert_called_with({ 'element': 'shoji:entity', 'body': { 'name': 'Q1_recoded', 'description': '', 'alias': 'Q1_recoded', 'derivation': { 'function': 'array', 'args': [{ 'function': 'select', 'args': [{ 'map': { '0001': { 'function': 'case', 'args': [{ 'column': [1, 2], 'type': { 'value': { 'class': 'categorical', 'categories': [{ 'selected': True, 'numeric_value': None, 'missing': False, 'id': 1, 'name': 'Selected' }, { 'selected': False, 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Not selected' }] } } }, { 'function': 'any', 'args': [{ 'variable': 'http://test.crunch.io/api/datasets/123/variables/0001/' }, { 'column': ['subvar', 'subvar'] }] }], 'references': { 'alias': 'Q1_recoded_1', 'name': 'Q1_recoded_1' } }, '0002': { 'function': 'case', 'args': [{ 'column': [1, 2], 'type': { 'value': { 'class': 'categorical', 'categories': [{ 'selected': True, 'numeric_value': None, 'missing': False, 'id': 1, 'name': 'Selected' }, { 'selected': False, 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Not selected' }] } } }, { 'function': 'any', 'args': [{ 'variable': 'http://test.crunch.io/api/datasets/123/variables/0001/' }, { 'column': ['subvar'] }] }], 'references': { 'alias': 'Q1_recoded_2', 'name': 'Q1_recoded_2' } } } }] }] } } })
def test_edit_category(self): resource = EditableMock() resource.entity.body = dict(categories=TEST_CATEGORIES(), type='categorical') variable = Variable(resource, MagicMock()) variable.categories[1].edit(name='Mujer') resource.entity._edit.assert_called_with(categories=[ { 'numeric_value': None, 'selected': False, 'id': 1, 'missing': False, 'name': 'Mujer' }, # These two don't have selected yet because it is reusing the # API categories still, only replacing the modified one { 'numeric_value': None, 'missing': False, 'id': 2, 'name': 'Male' }, { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' } ]) resource.entity.refresh.assert_called_once() self.assertEqual(variable.categories[1].name, 'Mujer') # Editing Male variable.categories[2].edit(name='Hombre') resource.entity._edit.assert_called_with(categories=[ { 'numeric_value': None, 'selected': False, 'id': 1, 'missing': False, 'name': 'Mujer' }, { 'numeric_value': None, 'selected': False, 'missing': False, 'id': 2, 'name': 'Hombre' }, # Same as above, reusing the existing value from API still { 'numeric_value': None, 'missing': True, 'id': -1, 'name': 'No Data' } ]) # Try to change the ID with self.assertRaises(AttributeError) as err: variable.categories[2].edit(id=100) self.assertEqual(str(err.exception), 'Cannot edit the following attributes: id') # Nothing changed self.assertEqual(set(variable.categories.keys()), {1, 2, -1})