def test_set_from_cell_failure(self): """Test that wrong headers and fields raises Exceptions""" t = TestData() t.set_fk_labels_objects() # Test the value table format key = {'testitema': 'a1', 'testitemb': 'b1', 'testitemc': 'c1'} value = '4' column_field = 'value' # bad header field should raise a NoFieldError for header header = 'bad_header_name' try: t.set_from_cell(key, header, value, column_field) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoFieldError('bad_header_name', TestData))) # bad value_field should raise a NoItemError for value_field column_field = 'bad_column_field' header = 'value' try: t.set_from_cell(key, header, value, column_field) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoFieldError('bad_column_field', TestData))) # Bad header in multi-column should raise NoItemError for header key = {'testitema': 'a1', 'testitemb': 'b1'} value = '4' column_field = 'testitemc' header = 'bad_item_name' try: t.set_from_cell(key, header, value, column_field) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoItemError('bad_item_name', TestItemC)))
def test_table_save_POST_failure(self): """Test failure when saving data using POST edit method.""" t = AssessTable(TestData, "") t.load(False, []) # Test bad decimal POST = {"('a1', 'b1', 'c1', 'value')": 'bad_decimal'} t.save_POST(POST) self.assertEqual(str(t.errors.pop()), str(NotDecimalError('bad_decimal', TestData))) self.assertEqual(t.records[('a1', 'b1', 'c1', 'value')].get_value(), '1,000') # Test bad POST key - item label POST = {"('bad_item', 'b1', 'c1', 'value')": '1'} t.save_POST(POST) self.assertEqual( str(t.errors.pop()), str( KeyNotFound("bad_item in ('bad_item', 'b1', 'c1', 'value')", TestData))) self.assertEqual(t.records[('a1', 'b1', 'c1', 'value')].get_value(), '1,000') # Test bad value field POST = {"('a1', 'b1', 'c1', 'bad_value_field')": '1'} t.save_POST(POST) self.assertEqual(str(t.errors.pop()), str(NoFieldError('bad_value_field', TestData))) self.assertEqual(t.records[('a1', 'b1', 'c1', 'value')].get_value(), '1,000')
def test_set_from_record_failure(self): """Test that a record dict will update the model value.""" t = TestData() t.set_fk_labels_objects() # Bad index name should raise NoFieldError r = { 'bad_index_name': 'a1', 'testitemb': 'b1', 'testitemc': 'c1', 'value': '4' } try: t.set_from_record_dict(r) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoFieldError('bad_index_name', TestData))) # Missing index fields in record dict should raise NoFieldError r = {'testitema': 'a1', 'testitemb': 'b1', 'value': '4'} try: t.set_from_record_dict(r) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoFieldError('testitemc', TestData))) # Bad value name should raise NoITemError r = { 'testitema': 'bad_item', 'testitemb': 'b1', 'testitemc': 'c1', 'value': '4' } try: t.set_from_record_dict(r) except NoItemError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoItemError('bad_item', TestItemA))) # Missing value_field should raise NoFieldError r = {'testitema': 'a1', 'testitemb': 'b1', 'testitemc': 'c1'} try: t.set_from_record_dict(r) except NoFieldError as e: e_msg = str(e) self.assertEqual(e_msg, str(NoFieldError('value', TestData)))
def test_key_init_inconsistent_fields(self): """Testing internal check of fieldname consistency in __init__""" # Modify the class to add a bogus field name that is not in the DB TestMappings.index_fields.append('bad_field') tm = Keys(TestMappings) # Remove the field name again (or get horrible stuff in rest of tests) TestMappings.index_fields.remove('bad_field') # Careful if we end up going multi-language on error messages error_msg = str(NoFieldError('bad_field',TestMappings)) self.assertEqual(str(tm.errors),error_msg)
def split_table_headers(self, table_headers) -> (list, list): """Split and check list of table headers to index + value headers.""" table_index_headers = [] table_value_headers = [] # Split into index and value headers # Root out fields that are for field in table_headers: if field in self.index_headers: table_index_headers.append(field) elif field in self.value_headers: table_value_headers.append(field) else: raise NoFieldError(field, self.model) # Check that table index_fields are in the supplied index fields for field in self.index_headers: if field not in table_index_headers: raise NoFieldError(field, self.model) # No check that all table value_fields are in user value_fields # User is allowed to supply only a subset of value fields # However, at least one value field must be supplied if len(table_value_headers) == 0: raise NoFieldError(self.model.value_field, self.model) return table_index_headers, table_value_headers
def split_key_str(self, key_str: str) -> tuple: """Split a key string, check validity and return key tuple.""" key_list = [] key_labels = {} # Construct field names for model fields = self.model.index_fields.copy() fields.append(self.model.value_field) # Expected key string format is '(label1,label2,label3)' labels = key_str.strip("()").replace('\'', '').replace(" ", "").split(',') # First part of the key string validation: # * Does splitting produce the right number of labels: if len(labels) != len(fields): raise KeyInvalid(key_str, self.model) # Second part of the key string validation: # * Are the labels present in list of index labels: fields_labels = dict(zip(fields, labels)) for (field, label) in fields_labels.items(): # label is the value_field name for non-index_fields if label == self.model.value_field: # add the value_field name to the key list key_list.append(label) # field is the index_field name for index fields elif field in self.model.index_fields: # For index_fields, label is the item label if label in self.indices_labels[field]: # Add the index_field name to key list key_list.append(label) # Add the inde_field name / index_label to the key dict key_labels[field] = label else: # The label may not be an index item raise KeyNotFound(label + ' in ' + key_str, self.model) else: # The field name in the POST dict may be incorrect # if it is not an item label nor the value_field name raise NoFieldError(label, self.model) # A key is a tuple of index item labels return tuple(key_list), key_labels
def test_parse_post_data_invalid(self): # Test that an invalid key length in POST results in an error and that # the POST entry is not added to the records tIO = AssessTableIO(TestData,delimiters) records = tIO.parse_POST(POST_invalid_length) key = list(POST_invalid_length.keys()).pop() self.assertEqual(str(tIO.errors.pop()),str(KeyInvalid(key, TestData))) self.assertEqual(records,{}) # Test that an invalid key length in POST results in an error and that # the POST entry is not added to the records records = tIO.parse_POST(POST_invalid_label) key = 'bad_item in ' + list(POST_invalid_label.keys()).pop() self.assertEqual(str(tIO.errors.pop()),str(KeyNotFound(key, TestData))) self.assertEqual(records,{}) # Test that an invalid value field name in POST results in an error # and that the POST entry is not added to the records records = tIO.parse_POST(POST_invalid_value_field) error_msg = str(NoFieldError('bad_field', TestData)) self.assertNotEqual(len(tIO.errors),0) if len(tIO.errors) > 0: self.assertEqual(str(tIO.errors.pop()),error_msg) self.assertEqual(records,{})
def test_parse_csv_data_failure(self): """Test parsing of csv strings for mappings_model in TableIO - failures.""" e1 = AssessError() e2 = AssessError() # Test malformed header - bad index header item ch = ['testitemBAD', 'testitemb', 'c1', 'c2'] ct = ['testitema', 'testitemb', 'c1', 'c2'] csv_data_string_c_bad_header = """testitemBAD\ttestitemb\tc1\tc2 a1\tb1\t1\t2 a2\tb1\t5\t6""" POST = { 'column_field': 'testitemc', 'csv_string': csv_data_string_c_bad_header } tIO = AssessTableIO(TestData, delimiters) tIO.parse_csv(POST) msg = str(NoFieldError('testitemBAD', TestData)) e2 = CSVheaderMalformed(ch, ct, msg, TestData) e1 = tIO.errors.pop() self.assertEqual(str(e1), str(e2)) # Test malformed header - missing index header item ch = ['testitemb', 'c1', 'c2'] ct = ['testitema', 'testitemb', 'c1', 'c2'] csv_data_string_c_bad_header = """testitemb\tc1\tc2 a1\tb1\t1\t2 a2\tb1\t5\t6""" POST = { 'column_field': 'testitemc', 'csv_string': csv_data_string_c_bad_header } tIO = AssessTableIO(TestData, delimiters) tIO.parse_csv(POST) msg = str(NoFieldError('testitema', TestData)) e2 = CSVheaderMalformed(ch, ct, msg, TestData) e1 = tIO.errors.pop() self.assertEqual(str(e1), str(e2)) # Test malformed header - missing value header item(s) ch = ['testitema', 'testitemb'] ct = ['testitema', 'testitemb', 'c1', 'c2'] csv_data_string_c_bad_header = """testitema\ttestitemb a2\tb1\t5\t6""" POST = { 'column_field': 'testitemc', 'csv_string': csv_data_string_c_bad_header } tIO = AssessTableIO(TestData, delimiters) tIO.parse_csv(POST) msg = str(NoFieldError('value', TestData)) e2 = CSVheaderMalformed(ch, ct, msg, TestData) e1 = tIO.errors.pop() self.assertEqual(str(e1), str(e2)) # Test wrong line count: First dataline has 1 too many, last 1 too few csv_data_string_c_bad_count = """testitema\ttestitemb\tc1\tc2 a1\tb1\t1\t2\t99 a2\tb1\t5""" POST = { 'column_field': 'testitemc', 'csv_string': csv_data_string_c_bad_count } ch = ['testitema', 'testitemb', 'c1', 'c2'] tIO = AssessTableIO(TestData, delimiters) tIO.parse_csv(POST) # One too many row columns row = ['a2', 'b1', '5'] e2 = CSVwrongColumnCount(row, ch, '', TestData) e1 = tIO.errors.pop() self.maxDiff = None self.assertEqual(str(e1), str(e2)) # Test wrong key row = ['a1', 'b1', '1', '2', '99'] e2 = CSVwrongColumnCount(row, ch, '', TestData) e1 = tIO.errors.pop() self.maxDiff = None self.assertEqual(str(e1), str(e2)) # Test bad index item in data row csv_data_string_c_bad_count = """testitema\ttestitemb\tc1\tc2 a1\tb1\t1\t2 BADITEM\tb1\t5\t6""" POST = { 'column_field': 'testitemc', 'csv_string': csv_data_string_c_bad_count } ch = ['testitema', 'testitemb', 'c1', 'c2'] tIO = AssessTableIO(TestData, delimiters) tIO.parse_csv(POST) # One too many row columns row = ['BADITEM', 'b1', '5', '6'] msg = str(NoItemError('BADITEM', TestItemA)) e2 = CSVfieldNotFound(row, ch, msg, TestData) e1 = tIO.errors.pop() self.maxDiff = None self.assertEqual(str(e1), str(e2))
def __init__(self, model): """Set lookup dicts for labels/ids """ self.model = model foreignkey_columns = model.index_fields.copy() # Mappings model values are foreign keys, must be added to key lookup if self.model.model_type == 'mappings_model': foreignkey_columns.append(self.model.value_field) # Headers are for reading/displaying tables in other pivoting self.headers = [] # List of header strings # The union of item_headers and index_headers is equal to headers self.value_headers = [] # List of value header strings self.index_headers = [] # List of index header strings # Is table 1-column (value_field) or multi-column (column_field items) self.table_one_column = False # Lookup dicts for index field names and item ids and item labels self.indices_ids_labels = {} # Dict of dicts {field: {id: label}, } self.indices_labels_ids = {} # Dict of dicts {field: {label: id}, } self.indices_labels_objects = { } # Dict of dicts {field: {label: object}, } self.indices_labels = {} # Dict of lists {field: [label, ]} self.value_labels_ids = [] # Dict of value labels ids # OBS: Trying to return the indices items for specified archied # versions is going to be really messy. Current items are needed for # chekcing of user upload data integrity dimensions = [] self.size = 1 for column_name in foreignkey_columns: try: # Get the foreign key model items for each index in collection column_model = self.model._meta.get_field( column_name).remote_field.model except: # Bad stuff will happen when self.index_field supplied in # app_name / models.py does not reflect the model's columns # ### OBS: Provide better error message from messages.py self.errors = NoFieldError(column_name, self.model) ids_labels = {} labels_ids = {} labels_objects = {} labels = [] # Preferably the version filters should be imported from version.py # but this depend on keys so we cant. ### OBS!!! We restrict any table to use ONLY CURRENT VERSION ITEMS! ### Presently it's unclear how to load only items for given version fc = {'version_first__isnull': False, 'version_last__isnull': True} for item in column_model.objects.filter(**fc): ids_labels[item.id] = item.label labels_ids[item.label] = item.id labels_objects[item.label] = item labels.append(item.label) # Count dimension of the different indices s = len(labels) self.size = self.size * s dimensions.append(str(s)) # Store the list and dicts in the collection object self.indices_ids_labels[column_name] = ids_labels self.indices_labels_ids[column_name] = labels_ids self.indices_labels_objects[column_name] = labels_objects self.indices_labels[column_name] = labels if column_name == self.model.value_field: self.value_labels_ids = labels_ids # Text string for description of table dimension self.dimension = "{" + " x ".join(dimensions) + "}"