Example #1
0
    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
Example #2
0
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
Example #3
0
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)))
Example #4
0
    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)
Example #5
0
 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)]