def validate_row(self, row_data, row_index, ws): """ :param row_data: expect an array of tuples [(col1, val1), (col2, val2),...] :return: list of errors """ errors = [] fields = util_model.get_datasheet_fields_for_model(self.model) for field in fields: field_sheet_name = util_model.get_datasheet_field_name(field) field_value = None for name, value in row_data: if name == field_sheet_name: field_value = value break try: to_field_value_raise(field, field_value, commit=False, row_data=row_data) except Exception as e: error = Error.objects.get(label="field_error") params = { 'datasheet': ws.title, 'col': field_sheet_name, 'row': row_index + 1, # add 1 to match the worksheet number 'msg': str(e) } error.params = params errors.append(error) return errors
def datasheet_schema(): """ expose a json schema of all the models/fields used to create a SiteVisit from a datasheet. This method only export the models in the datasheet (main, vegetation, animals) anf their fields Current implementation only export fields that are in the datasheet. The order in the models and fields arrays respect the order in the datasheet """ def get_model_data(model_): return { 'name': model_._meta.model_name, 'module': model_.__module__, 'class_name': model_.__name__, 'verbose_name': model._meta.verbose_name, 'name_plural': model._meta.verbose_name_plural.capitalize() } def get_field_data(field_): return { 'name': field_.name, 'is_datasheet_field': utils_model.is_template_field(field_), 'datasheet_name': utils_model.get_datasheet_field_name(field_), 'is_lookup': utils_model.is_lookup_field(field_), 'is_mandatory': utils_model.is_mandatory(field_), 'is_species_name': utils_model.is_species_observation_field(field_), 'is_strict_lookup': utils_model.is_strict_lookup_field(field_), 'is_boolean': utils_model.is_boolean_field(field_), 'is_extra_species_attribute': utils_model.is_species_observation_extra_attribute(field), } def get_datasheet_mapping_data(mapping_): return { 'sheet_name': mapping_.sheet_name, 'top_left_column': mapping_.top_left_column, 'top_left_row': mapping_.top_left_row, 'transpose': mapping_.transpose, 'mandatory': mapping_.mandatory, 'unique': mapping_.unique } # The list of all the models are in the upload app datasheet_mappings = DATASHEET_MODELS_MAPPING result = {} models_data = [] for mapping in datasheet_mappings: model = mapping.model data = {} data.update(**get_model_data(model)) data.update({'datasheet_mapping': get_datasheet_mapping_data(mapping)}) models_data.append(data) # we only really want the fields that are in the datasheet fields_data = [] fields = utils_model.get_datasheet_fields_for_model(model) for field in fields: fields_data.append(get_field_data(field)) data.update({'fields': fields_data}) result.update({'models': models_data}) return result
def ground_cover_validate(row_data): fields = util_model.get_datasheet_fields_for_model(AbstractGroundCoverObservation) percentage_total = 0 verbose_field_names = [] for field in fields: field_sheet_name = util_model.get_datasheet_field_name(field) for name, value in row_data: if name == field_sheet_name: verbose_field_names.append(name) if value is not None: percentage_total += value break if percentage_total != 100.0: raise Exception("Ground cover fields ({}) of a row must add up to 100%".format(", ".join(verbose_field_names)))
def _write_model(self, mapping): model = mapping.model ws = util_xls.get_or_create_sheet(self.wb, mapping.sheet_name) fields = util_model.get_datasheet_fields_for_model(model) top_cell = ws.cell(row=mapping.top_left_row, column=mapping.top_left_column) column_cell = top_cell # create alignment for transposed header cells right_alignment = Alignment(horizontal='right') max_column_width = 0 for field in fields: # the column header col_header = util_model.get_datasheet_field_name(field) column_cell.font = self.column_header_font column_cell.value = col_header if mapping.transpose: column_cell.alignment = right_alignment # calculate column widths if util_model.is_species_observation_field(field): # special case for species column width ws.column_dimensions[column_cell.column].width = SPECIES_COLUMN_WIDTH else: column_width = len(column_cell.value) + COLUMN_WIDTH_BUFFER if mapping.transpose: if column_width > max_column_width: max_column_width = column_width else: column_width = max_column_width ws.column_dimensions[column_cell.column].width = column_width dv = self._create_data_validation(field) if dv is not None: # apply data validation to the row cell row_cell = util_xls.get_cell_neighbour(column_cell, mapping.next_row_direction) dv.add(row_cell) if not mapping.unique: # it is a multi row model. We need to extend the data validation to other rows # choose a max row max_row = self.max_row for i in range(0, max_row): row_cell = util_xls.get_cell_neighbour(row_cell, mapping.next_row_direction) dv.add(row_cell) ws.add_data_validation(dv) column_cell = util_xls.get_cell_neighbour(column_cell, mapping.next_col_direction) # insert formulas for field_name in mapping.formulas: field = mapping.model._meta.get_field_by_name(field_name)[0] field_header_cell = util_xls.find_cell_by_value(ws, util_model.get_datasheet_field_name(field)) field_cell = util_xls.get_cell_neighbour(field_header_cell, mapping.next_row_direction) parameter_cells = [] for param_field_name in mapping.formulas[field_name]['parameters']: param_field = mapping.model._meta.get_field_by_name(param_field_name)[0] param_header_cell = util_xls.find_cell_by_value(ws, util_model.get_datasheet_field_name(param_field)) parameter_cells.append(util_xls.get_cell_neighbour(param_header_cell, mapping.next_row_direction)) field_cell.value = self._create_formula(mapping.formulas[field_name]['formula'], *parameter_cells) if not mapping.unique: # it is a multi row model. We need to extend the formula to other rows choose a max row max_row = self.max_row for i in range(0, max_row): field_cell = util_xls.get_cell_neighbour(field_cell, mapping.next_row_direction) parameter_cells = [util_xls.get_cell_neighbour(cell, mapping.next_row_direction) for cell in parameter_cells] field_cell.value = self._create_formula(mapping.formulas[field_name]['formula'], *parameter_cells)
def setUp(self): self.datasheet_models = [m.model for m in DATASHEET_MODELS_MAPPING] ll = [util_model.get_datasheet_fields_for_model(m) for m in self.datasheet_models] self.datasheet_fields = [item for l in ll for item in l] self.mandatory_fields = [f for f in self.datasheet_fields if util_model.is_mandatory(f)] self.non_mandatory_fields = [f for f in self.datasheet_fields if not util_model.is_mandatory(f)]