예제 #1
0
    def test_analysis_summary(self):
        """Test we can aggregate the aggregate hazard to the analysis."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4', 'intermediate',
            'aggregate_classified_hazard_summary.geojson')

        aggregate_hazard.keywords['hazard_keywords'] = {
            'classification': 'generic_hazard_classes'
        }

        analysis = load_test_vector_layer('gisv4',
                                          'intermediate',
                                          'analysis.geojson',
                                          clone=True)

        number_of_fields = analysis.fields().count()

        layer = analysis_summary(aggregate_hazard, analysis)

        check_inasafe_fields(layer)

        fields = aggregate_hazard.keywords['inasafe_fields']
        hazard_class = fields[hazard_class_field['key']]
        hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class)
        unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index)

        # expected number of fields:
        # - one field for each hazard class
        # - 2 fields for analysis id and analysis name
        # - 4 fields for total affected, not_affected, not exposed and total
        self.assertEqual(layer.fields().count(),
                         len(unique_hazard) + number_of_fields + 4)
예제 #2
0
    def test_aggregation_summary(self):
        """Test we can aggregate the aggregate hazard to the aggregation."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'aggregate_classified_hazard_summary.geojson')

        aggregation = load_test_vector_layer(
            'gisv4',
            'aggregation',
            'aggregation_cleaned.geojson',
            clone=True)

        number_of_fields = aggregation.fields().count()

        layer = aggregation_summary(aggregate_hazard, aggregation)

        check_inasafe_fields(layer)

        # I need the number of unique exposure
        pattern = exposure_count_field['key']
        pattern = pattern.replace('%s', '')
        unique_exposure = []
        inasafe_fields = aggregate_hazard.keywords['inasafe_fields']
        for key, name_field in inasafe_fields.iteritems():
            if key.endswith(pattern):
                unique_exposure.append(key.replace(pattern, ''))

        self.assertEqual(
            layer.fields().count(),
            len(unique_exposure) + number_of_fields + 1
        )
예제 #3
0
    def test_analysis_earthquake_summary(self):
        """Test we can compute summary after an EQ on population."""
        hazard = load_test_raster_layer('gisv4', 'hazard', 'earthquake.asc')
        exposure = load_test_raster_layer(
            'gisv4', 'exposure', 'raster', 'population.asc')
        aggregation = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.hazard = hazard
        impact_function.exposure = exposure
        impact_function.aggregation = aggregation
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        layer = impact_function.analysis_impacted
        classification = hazard.keywords['classification']
        classes = definition(classification)['classes']
        for hazard_class in classes:
            field_name = hazard_count_field['field_name'] % hazard_class['key']
            message = '%s is not found in the EQ summary layer.' % field_name
            self.assertNotEqual(-1, layer.fieldNameIndex(field_name), message)

        check_inasafe_fields(impact_function.analysis_impacted)
        check_inasafe_fields(impact_function.aggregation_summary)
예제 #4
0
    def test_exposure_summary_table(self):
        """Test we can produce the breakdown for the exposure type."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4', 'intermediate',
            'aggregate_classified_hazard_summary.geojson')

        aggregate_hazard.keywords['hazard_keywords'] = {
            'classification': 'generic_hazard_classes'
        }

        # I need the number of unique exposure
        unique_exposure = read_dynamic_inasafe_field(
            aggregate_hazard.keywords['inasafe_fields'], exposure_count_field)

        # I need the number of unique hazard
        fields = aggregate_hazard.keywords['inasafe_fields']
        hazard_class = fields[hazard_class_field['key']]
        hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class)
        unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index)

        layer = exposure_summary_table(aggregate_hazard, None)

        check_inasafe_fields(layer)

        self.assertEqual(len(unique_exposure), layer.featureCount())

        # We should have
        # one column per hazard
        # one for the exposure
        # one for total affected
        # one for total not affected
        # one for total not exposed
        # one for total
        self.assertEqual(layer.fields().count(), len(unique_hazard) + 5)
예제 #5
0
    def test_aggregation_summary(self):
        """Test we can aggregate the aggregate hazard to the aggregation."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'aggregate_classified_hazard_summary.geojson')

        aggregation = load_test_vector_layer(
            'gisv4',
            'aggregation',
            'aggregation_cleaned.geojson',
            clone=True)

        number_of_fields = aggregation.fields().count()

        layer = aggregation_summary(aggregate_hazard, aggregation)

        check_inasafe_fields(layer)

        # I need the number of unique exposure
        pattern = exposure_count_field['key']
        pattern = pattern.replace('%s', '')
        unique_exposure = []
        inasafe_fields = aggregate_hazard.keywords['inasafe_fields']
        for key, name_field in inasafe_fields.iteritems():
            if key.endswith(pattern):
                unique_exposure.append(key.replace(pattern, ''))

        self.assertEqual(
            layer.fields().count(),
            len(unique_exposure) + number_of_fields + 1
        )
예제 #6
0
    def test_analysis_earthquake_summary(self):
        """Test we can compute summary after an EQ on population."""
        hazard = load_test_raster_layer('gisv4', 'hazard', 'earthquake.asc')
        exposure = load_test_raster_layer('gisv4', 'exposure', 'raster',
                                          'population.asc')
        aggregation = load_test_vector_layer('gisv4', 'aggregation',
                                             'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.hazard = hazard
        impact_function.exposure = exposure
        impact_function.aggregation = aggregation
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        layer = impact_function.analysis_impacted
        classification = hazard.keywords['classification']
        classes = definition(classification)['classes']
        for hazard_class in classes:
            field_name = hazard_count_field['field_name'] % hazard_class['key']
            message = '%s is not found in the EQ summary layer.' % field_name
            self.assertNotEqual(-1, layer.fieldNameIndex(field_name), message)

        check_inasafe_fields(impact_function.analysis_impacted)
        check_inasafe_fields(impact_function.aggregation_summary)
예제 #7
0
    def test_exposure_summary_table_productivity(self):
        """Test we can produce the breakdown for the exposure type."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'summaries',
            'land_cover_aggregate_hazard_impacted.geojson')

        aggregate_hazard.keywords['hazard_keywords'] = {
            'hazard': 'generic',
            'classification': 'generic_hazard_classes'
        }

        aggregate_hazard.keywords['exposure_keywords'] = {
            'exposure': 'land_cover'
        }

        exposure_summary = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'summaries',
            'land_cover_exposure_summary.geojson'
        )

        # I need the number of unique exposure
        unique_exposure = read_dynamic_inasafe_field(
            aggregate_hazard.keywords['inasafe_fields'],
            exposure_count_field)

        # I need the number of unique hazard
        fields = aggregate_hazard.keywords['inasafe_fields']
        hazard_class = fields[hazard_class_field['key']]
        hazard_class_index = aggregate_hazard.fields().lookupField(hazard_class)
        unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index)

        layer = exposure_summary_table(aggregate_hazard, exposure_summary)

        check_inasafe_fields(layer)

        self.assertEqual(len(unique_exposure), layer.featureCount())

        # We should have
        # one column per hazard

        # 1. one for the exposure
        # 2. one for total affected
        # 3. one for total not affected
        # 4. one for total not exposed
        # 5. one for total
        # 6. one for affected productivity
        # 7. one for affected production cost
        # 8. one for affected production value
        self.assertEqual(layer.fields().count(), len(unique_hazard) + 8)
예제 #8
0
    def test_exposure_summary_table_productivity(self):
        """Test we can produce the breakdown for the exposure type."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'summaries',
            'land_cover_aggregate_hazard_impacted.geojson')

        aggregate_hazard.keywords['hazard_keywords'] = {
            'classification': 'generic_hazard_classes'
        }

        exposure_summary = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'summaries',
            'land_cover_exposure_summary.geojson'
        )

        # I need the number of unique exposure
        unique_exposure = read_dynamic_inasafe_field(
            aggregate_hazard.keywords['inasafe_fields'],
            exposure_count_field)

        # I need the number of unique hazard
        fields = aggregate_hazard.keywords['inasafe_fields']
        hazard_class = fields[hazard_class_field['key']]
        hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class)
        unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index)

        layer = exposure_summary_table(aggregate_hazard, exposure_summary)

        check_inasafe_fields(layer)

        self.assertEqual(len(unique_exposure), layer.featureCount())

        # We should have
        # one column per hazard
        # one for the exposure
        # one for total affected
        # one for total not affected
        # one for total not exposed
        # one for total
        # one for affected productivity
        # one for affected production cost
        # one for affected production value
        self.assertEqual(layer.fields().count(), len(unique_hazard) + 8)
예제 #9
0
    def test_impact_summary(self):
        """Test we can aggregate the impact to the aggregate hazard."""
        impact = load_test_vector_layer(
            'gisv4',
            'impacts',
            'building-points-classified-vector.geojson')

        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'aggregate_classified_hazard.geojson',
            clone=True)

        aggregate_hazard.keywords['hazard_keywords'] = {
            'hazard': 'generic',
            'classification': 'generic_hazard_classes'
        }
        impact.keywords['classification'] = {
            'classification': 'generic_structure_classes'
        }
        impact.keywords['exposure_keywords'] = {
            'exposure': 'structure'
        }

        number_of_fields = aggregate_hazard.fields().count()

        layer = aggregate_hazard_summary(impact, aggregate_hazard)

        self.assertIn(total_field['key'], layer.keywords['inasafe_fields'])

        check_inasafe_fields(layer)

        fields = impact.keywords['inasafe_fields']
        exposure_class = fields[exposure_class_field['key']]
        exposure_class_index = impact.fields().lookupField(exposure_class)
        unique_exposure = impact.uniqueValues(exposure_class_index)

        # One field per exposure type
        # Number of previous fields in the layer
        # 3 : 1 fields for absolute values, 2 fields for affected and total.
        self.assertEqual(
            layer.fields().count(),
            len(unique_exposure) + number_of_fields + 3
        )
예제 #10
0
    def test_impact_summary(self):
        """Test we can aggregate the impact to the aggregate hazard."""
        impact = load_test_vector_layer(
            'gisv4',
            'impacts',
            'building-points-classified-vector.geojson')

        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'aggregate_classified_hazard.geojson',
            clone=True)

        aggregate_hazard.keywords['hazard_keywords'] = {
            'classification': 'generic_hazard_classes'
        }
        impact.keywords['classification'] = {
            'classification': 'generic_structure_classes'
        }

        number_of_fields = aggregate_hazard.fields().count()

        layer = aggregate_hazard_summary(impact, aggregate_hazard)

        self.assertIn(total_field['key'], layer.keywords['inasafe_fields'])

        check_inasafe_fields(layer)

        fields = impact.keywords['inasafe_fields']
        exposure_class = fields[exposure_class_field['key']]
        exposure_class_index = impact.fieldNameIndex(exposure_class)
        unique_exposure = impact.uniqueValues(exposure_class_index)

        # One field per exposure type
        # Number of previous fields in the layer
        # 3 : 1 fields for absolute values, 2 fields for affected and total.
        self.assertEqual(
            layer.fields().count(),
            len(unique_exposure) + number_of_fields + 3
        )
예제 #11
0
    def test_analysis_summary(self):
        """Test we can aggregate the aggregate hazard to the analysis."""
        aggregate_hazard = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'aggregate_classified_hazard_summary.geojson')

        aggregate_hazard.keywords['hazard_keywords'] = {
            'classification': 'generic_hazard_classes'
        }

        analysis = load_test_vector_layer(
            'gisv4',
            'intermediate',
            'analysis.geojson',
            clone=True)

        number_of_fields = analysis.fields().count()

        layer = analysis_summary(aggregate_hazard, analysis)

        check_inasafe_fields(layer)

        fields = aggregate_hazard.keywords['inasafe_fields']
        hazard_class = fields[hazard_class_field['key']]
        hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class)
        unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index)

        # expected number of fields:
        # - one field for each hazard class
        # - 2 fields for analysis id and analysis name
        # - 4 fields for total affected, not_affected, not exposed and total
        self.assertEqual(
            layer.fields().count(),
            len(unique_hazard) + number_of_fields + 4
        )
예제 #12
0
def run_scenario(scenario, use_debug=False):
    """Run scenario.

    :param scenario: Dictionary of hazard, exposure, and aggregation.
    :type scenario: dict

    :param use_debug: If we should use debug_mode when we run the scenario.
    :type use_debug: bool

    :returns: Tuple(status, Flow dictionary, outputs).
    :rtype: list
    """
    if os.path.exists(scenario['exposure']):
        exposure_path = scenario['exposure']
    elif os.path.exists(standard_data_path('exposure', scenario['exposure'])):
        exposure_path = standard_data_path('exposure', scenario['exposure'])
    elif os.path.exists(
            standard_data_path(*(scenario['exposure'].split('/')))):
        exposure_path = standard_data_path(*(scenario['exposure'].split('/')))
    else:
        raise IOError('No exposure file')

    if os.path.exists(scenario['hazard']):
        hazard_path = scenario['hazard']
    elif os.path.exists(standard_data_path('hazard', scenario['hazard'])):
        hazard_path = standard_data_path('hazard', scenario['hazard'])
    elif os.path.exists(standard_data_path(*(scenario['hazard'].split('/')))):
        hazard_path = standard_data_path(*(scenario['hazard'].split('/')))
    else:
        raise IOError('No hazard file')

    if not scenario['aggregation']:
        aggregation_path = None
    else:
        if os.path.exists(scenario['aggregation']):
            aggregation_path = scenario['aggregation']
        elif os.path.exists(
                standard_data_path('aggregation', scenario['aggregation'])):
            aggregation_path = standard_data_path('aggregation',
                                                  scenario['aggregation'])
        elif os.path.exists(
                standard_data_path(*(scenario['aggregation'].split('/')))):
            aggregation_path = standard_data_path(
                *(scenario['aggregation'].split('/')))
        else:
            raise IOError('No aggregation file')

    impact_function = ImpactFunction()
    impact_function.debug_mode = use_debug

    layer = QgsVectorLayer(hazard_path, 'Hazard', 'ogr')
    if not layer.isValid():
        layer = QgsRasterLayer(hazard_path, 'Hazard')
    impact_function.hazard = layer

    layer = QgsVectorLayer(exposure_path, 'Exposure', 'ogr')
    if not layer.isValid():
        layer = QgsRasterLayer(exposure_path, 'Exposure')
    impact_function.exposure = layer

    if aggregation_path:
        impact_function.aggregation = QgsVectorLayer(aggregation_path,
                                                     'Aggregation', 'ogr')

    status, message = impact_function.prepare()
    if status != 0:
        return status, message, None

    status, message = impact_function.run()
    if status != 0:
        return status, message, None

    for layer in impact_function.outputs:
        if layer.type() == QgsMapLayer.VectorLayer:
            check_inasafe_fields(layer)

    return status, impact_function.state, impact_function.outputs
예제 #13
0
def run_scenario(scenario, use_debug=False):
    """Run scenario.

    :param scenario: Dictionary of hazard, exposure, and aggregation.
    :type scenario: dict

    :param use_debug: If we should use debug_mode when we run the scenario.
    :type use_debug: bool

    :returns: Tuple(status, Flow dictionary, outputs).
    :rtype: list
    """
    if os.path.exists(scenario['exposure']):
        exposure_path = scenario['exposure']
    elif os.path.exists(standard_data_path('exposure', scenario['exposure'])):
        exposure_path = standard_data_path('exposure', scenario['exposure'])
    elif os.path.exists(
            standard_data_path(*(scenario['exposure'].split('/')))):
        exposure_path = standard_data_path(*(scenario['exposure'].split('/')))
    else:
        raise IOError('No exposure file')

    if os.path.exists(scenario['hazard']):
        hazard_path = scenario['hazard']
    elif os.path.exists(standard_data_path('hazard', scenario['hazard'])):
        hazard_path = standard_data_path('hazard', scenario['hazard'])
    elif os.path.exists(standard_data_path(*(scenario['hazard'].split('/')))):
        hazard_path = standard_data_path(*(scenario['hazard'].split('/')))
    else:
        raise IOError('No hazard file')

    if not scenario['aggregation']:
        aggregation_path = None
    else:
        if os.path.exists(scenario['aggregation']):
            aggregation_path = scenario['aggregation']
        elif os.path.exists(standard_data_path(
                'aggregation', scenario['aggregation'])):
            aggregation_path = standard_data_path(
                'aggregation', scenario['aggregation'])
        elif os.path.exists(
                standard_data_path(*(scenario['aggregation'].split('/')))):
            aggregation_path = standard_data_path(
                *(scenario['aggregation'].split('/')))
        else:
            raise IOError('No aggregation file')

    impact_function = ImpactFunction()
    impact_function.debug_mode = use_debug

    layer = QgsVectorLayer(hazard_path, 'Hazard', 'ogr')
    if not layer.isValid():
        layer = QgsRasterLayer(hazard_path, 'Hazard')
    impact_function.hazard = layer

    layer = QgsVectorLayer(exposure_path, 'Exposure', 'ogr')
    if not layer.isValid():
        layer = QgsRasterLayer(exposure_path, 'Exposure')
    impact_function.exposure = layer

    if aggregation_path:
        impact_function.aggregation = QgsVectorLayer(
            aggregation_path, 'Aggregation', 'ogr')

    status, message = impact_function.prepare()
    if status != 0:
        return status, message, None

    status, message = impact_function.run()
    if status != 0:
        return status, message, None

    for layer in impact_function.outputs:
        if layer.type() == QgsMapLayer.VectorLayer:
            check_inasafe_fields(layer)

    return status, impact_function.state, impact_function.outputs
예제 #14
0
def check_input_layer(layer, purpose):
    """Function to check if the layer is valid.

    The function will also set the monkey patching if needed.

    :param layer: The layer to test.
    :type layer: QgsMapLayer

    :param purpose: The expected purpose of the layer.
    :type purpose: basestring

    :return: A tuple with the status of the layer and an error message if
        needed.
        The status is 0 if everything was fine.
        The status is 1 if the client should fix something.
    :rtype: (int, m.Message)
    """
    if not layer.isValid():
        title = tr('The {purpose} layer is invalid').format(purpose=purpose)
        content = tr('The impact function needs a {exposure} layer to run. '
                     'You must provide a valid {exposure} layer.').format(
                         purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    # We should read it using KeywordIO for the very beginning. To avoid
    # get the modified keywords in the patching.
    try:
        keywords = KeywordIO().read_keywords(layer)
    except NoKeywordsFoundError:

        title = tr('The {purpose} layer does not have keywords.').format(
            purpose=purpose)
        content = tr(
            'The {purpose} layer does not have keywords. Use the wizard '
            'to assign keywords to the layer.').format(purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    if keywords.get('layer_purpose') != purpose:
        title = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        content = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    version = keywords.get(inasafe_keyword_version_key)
    supported = is_keyword_version_supported(version)
    if not supported:
        parameters = {
            'version': inasafe_keyword_version,
            'source': layer.publicSource()
        }
        title = tr('The {purpose} layer is not up to date.').format(
            purpose=purpose)
        content = tr('The layer {source} must be updated to {version}.'
                     ).format(**parameters)
        message = generate_input_error_message(title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    layer.keywords = keywords

    if is_vector_layer(layer):
        try:
            check_inasafe_fields(layer, keywords_only=True)
        except InvalidLayerError:
            title = tr('The {purpose} layer is not up to date.').format(
                purpose=purpose)
            content = tr(
                'The layer {source} must be updated with the keyword '
                'wizard. Your fields which have been set in the keywords '
                'previously are not matching your layer.').format(
                    source=layer.publicSource())
            message = generate_input_error_message(title, m.Paragraph(content))
            del layer.keywords
            return PREPARE_FAILED_BAD_INPUT, message

    return PREPARE_SUCCESS, None
예제 #15
0
def check_input_layer(layer, purpose):
    """Function to check if the layer is valid.

    The function will also set the monkey patching if needed.

    :param layer: The layer to test.
    :type layer: QgsMapLayer

    :param purpose: The expected purpose of the layer.
    :type purpose: basestring

    :return: A tuple with the status of the layer and an error message if
        needed.
        The status is 0 if everything was fine.
        The status is 1 if the client should fix something.
    :rtype: (int, m.Message)
    """
    if not layer.isValid():
        title = tr(
            'The {purpose} layer is invalid').format(purpose=purpose)
        content = tr(
            'The impact function needs a {exposure} layer to run. '
            'You must provide a valid {exposure} layer.').format(
            purpose=purpose)
        message = generate_input_error_message(
            title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    # We should read it using KeywordIO for the very beginning. To avoid
    # get the modified keywords in the patching.
    try:
        keywords = KeywordIO().read_keywords(layer)
    except NoKeywordsFoundError:

        title = tr(
            'The {purpose} layer does not have keywords.').format(
            purpose=purpose)
        content = tr(
            'The {purpose} layer does not have keywords. Use the wizard '
            'to assign keywords to the layer.').format(purpose=purpose)
        message = generate_input_error_message(
            title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    if keywords.get('layer_purpose') != purpose:
        title = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        content = tr('The expected {purpose} layer is not an {purpose}.') \
            .format(purpose=purpose)
        message = generate_input_error_message(
            title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    version = keywords.get(inasafe_keyword_version_key)
    supported = is_keyword_version_supported(version)
    if not supported:
        parameters = {
            'version': inasafe_keyword_version,
            'source': layer.publicSource()
        }
        title = tr('The {purpose} layer is not up to date.').format(
            purpose=purpose)
        content = tr(
            'The layer {source} must be updated to {version}.').format(
            **parameters)
        message = generate_input_error_message(
            title, m.Paragraph(content))
        return PREPARE_FAILED_BAD_INPUT, message

    layer.keywords = keywords

    if is_vector_layer(layer):
        try:
            check_inasafe_fields(layer, keywords_only=True)
        except InvalidLayerError:
            title = tr('The {purpose} layer is not up to date.').format(
                purpose=purpose)
            content = tr(
                'The layer {source} must be updated with the keyword '
                'wizard. Your fields which have been set in the keywords '
                'previously are not matching your layer.').format(
                source=layer.publicSource())
            message = generate_input_error_message(
                title, m.Paragraph(content))
            del layer.keywords
            return PREPARE_FAILED_BAD_INPUT, message

    return PREPARE_SUCCESS, None