def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        # To store parameters, we need the english version.
        translated_to_english = dict(
            (y, x) for x, y in list(self.resource_parameters.items()))
        resource = {}
        for parameter in parameters:
            resource[translated_to_english[parameter.name]] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds as e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError as e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMinimumError as e:
            warning = self.tr(
                'Problem - minimum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        # end of test for parameter validity

        self.add_resource(resource)
        self.switch_context(self.profile_edit_page)
    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        # To store parameters, we need the english version.
        translated_to_english = dict(
            (y, x) for x, y in list(self.resource_parameters.items()))
        resource = {}
        for parameter in parameters:
            resource[translated_to_english[parameter.name]] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds as e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError as e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMinimumError as e:
            warning = self.tr(
                'Problem - minimum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        # end of test for parameter validity

        self.add_resource(resource)
        self.switch_context(self.profile_edit_page)
Exemple #3
0
def _initializes_minimum_needs_fields():
    """Initialize minimum needs fields.

    Minimum needs definitions are taken from currently used profile.
    """
    needs_profile = NeedsProfile()
    needs_profile.load()
    fields = []

    needs_parameters = needs_profile.get_needs_parameters()

    for need_parameter in needs_parameters:
        if isinstance(need_parameter, ResourceParameter):
            format_args = {
                'namespace': minimum_needs_namespace,
                'key': _normalize_field_name(need_parameter.name),
                'name': need_parameter.name,
                'field_name': _normalize_field_name(need_parameter.name),
            }

            key = '{namespace}__{key}_count_field'.format(**format_args)
            name = '{name}'.format(**format_args)
            field_name = '{namespace}__{field_name}'.format(**format_args)
            field_type = QVariant.LongLong  # See issue #4039
            length = 11  # See issue #4039
            precision = 0
            absolute = True
            replace_null = False
            description = need_parameter.description

            field_definition = {
                'key': key,
                'name': name,
                'field_name': field_name,
                'type': field_type,
                'length': length,
                'precision': precision,
                'absolute': absolute,
                'description': description,
                'replace_null': replace_null,
                'unit_abbreviation': need_parameter.unit.abbreviation,
                # Link to need_parameter
                'need_parameter': need_parameter
            }
            fields.append(field_definition)
    return fields
Exemple #4
0
def _initializes_minimum_needs_fields():
    """Initialize minimum needs fields.

    Minimum needs definitions are taken from currently used profile.
    """
    needs_profile = NeedsProfile()
    needs_profile.load()
    fields = []

    needs_parameters = needs_profile.get_needs_parameters()

    for need_parameter in needs_parameters:
        if isinstance(need_parameter, ResourceParameter):
            format_args = {
                'namespace': minimum_needs_namespace,
                'key': _normalize_field_name(need_parameter.name),
                'name': need_parameter.name,
                'field_name': _normalize_field_name(need_parameter.name),
            }

            key = '{namespace}__{key}_count_field'.format(**format_args)
            name = '{name}'.format(**format_args)
            field_name = '{namespace}__{field_name}'.format(**format_args)
            field_type = QVariant.LongLong  # See issue #4039
            length = 11  # See issue #4039
            precision = 0
            absolute = True
            replace_null = False
            description = need_parameter.description

            field_definition = {
                'key': key,
                'name': name,
                'field_name': field_name,
                'type': field_type,
                'length': length,
                'precision': precision,
                'absolute': absolute,
                'description': description,
                'replace_null': replace_null,
                'unit_abbreviation': need_parameter.unit.abbreviation,
                # Link to need_parameter
                'need_parameter': need_parameter
            }
            fields.append(field_definition)
    return fields
    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtWidgets.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)
    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtGui.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)
    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.resource_widget.layout().itemAt(i) for i in
            range(self.resource_widget.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        resource = {}
        for parameter in parameters:
            resource[parameter.name] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds, e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.resource_widget.layout().itemAt(i) for i in
            range(self.resource_widget.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        resource = {}
        for parameter in parameters:
            resource[parameter.name] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds, e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
    def __init__(self, parent=None, dock=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param dock: Dock widget instance that we can notify of changes.
        :type dock: Dock
        """
        QtWidgets.QDialog.__init__(self, parent)
        # List of parameters with the translated name.
        self.resource_parameters = {
            'Resource name': tr('Resource name'),
            'Resource description': tr('Resource description'),
            'Unit': tr('Unit'),
            'Units': tr('Units'),
            'Unit abbreviation': tr('Unit abbreviation'),
            'Minimum allowed': tr('Minimum allowed'),
            'Maximum allowed': tr('Maximum allowed'),
            'Default': tr('Default'),
            'Frequency': tr('Frequency'),
            'Readable sentence': tr('Readable sentence')
        }

        self.setupUi(self)
        icon = resources_path('img', 'icons', 'show-minimum-needs.svg')
        self.setWindowIcon(QtGui.QIcon(icon))
        self.dock = dock
        # These are in the little button bar at the bottom
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Restore defaults profiles
        self.restore_defaults_button = QPushButton(self.tr('Restore defaults'))
        self.button_box.addButton(
            self.restore_defaults_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.restore_defaults_button.clicked.connect(self.restore_defaults)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up things for context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        # initial sync profile_combo and resource list
        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
        self.select_profile(self.profile_combo.currentIndex())
class NeedsManagerDialog(QDialog, FORM_CLASS):
    """Dialog class for the InaSAFE global minimum needs configuration.

    .. versionadded:: 2.2.
    """

    def __init__(self, parent=None, dock=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param dock: Dock widget instance that we can notify of changes.
        :type dock: Dock
        """
        QtWidgets.QDialog.__init__(self, parent)
        # List of parameters with the translated name.
        self.resource_parameters = {
            'Resource name': tr('Resource name'),
            'Resource description': tr('Resource description'),
            'Unit': tr('Unit'),
            'Units': tr('Units'),
            'Unit abbreviation': tr('Unit abbreviation'),
            'Minimum allowed': tr('Minimum allowed'),
            'Maximum allowed': tr('Maximum allowed'),
            'Default': tr('Default'),
            'Frequency': tr('Frequency'),
            'Readable sentence': tr('Readable sentence')
        }

        self.setupUi(self)
        icon = resources_path('img', 'icons', 'show-minimum-needs.svg')
        self.setWindowIcon(QtGui.QIcon(icon))
        self.dock = dock
        # These are in the little button bar at the bottom
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Restore defaults profiles
        self.restore_defaults_button = QPushButton(self.tr('Restore defaults'))
        self.button_box.addButton(
            self.restore_defaults_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.restore_defaults_button.clicked.connect(self.restore_defaults)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up things for context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        # initial sync profile_combo and resource list
        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
        self.select_profile(self.profile_combo.currentIndex())

    def reject(self):
        """Overload the base dialog reject event so we can handle state change.

        If the user is in resource editing mode, clicking close button,
        window [x] or pressing escape should switch context back to the
        profile view, not close the whole window.

        See https://github.com/AIFDR/inasafe/issues/1387
        """
        if self.stacked_widget.currentWidget() == self.resource_edit_page:
            self.edit_item = None
            self.switch_context(self.profile_edit_page)
        else:
            super(NeedsManagerDialog, self).reject()

    def populate_resource_list(self):
        """Populate the list resource list.
        """
        minimum_needs = self.minimum_needs.get_full_needs()
        for full_resource in minimum_needs["resources"]:
            self.add_resource(full_resource)
        self.provenance.setText(minimum_needs["provenance"])

    def clear_resource_list(self):
        """Clear the resource list.
        """
        self.resources_list.clear()

    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtWidgets.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)

    def restore_defaults(self):
        """Restore defaults profiles."""
        title = tr('Restore defaults')
        msg = tr(
            'Restoring defaults will overwrite your changes on profiles '
            'provided by InaSAFE. Do you want to continue ?')
        # noinspection PyCallByClass
        reply = QMessageBox.question(
            self,
            title,
            msg,
            QtWidgets.QMessageBox.Yes,
            QtWidgets.QMessageBox.No
        )

        if reply == QtWidgets.QMessageBox.Yes:
            self.profile_combo.clear()
            self.load_profiles(True)
            # Next 2 lines fixes issues #1388 #1389 #1390 #1391
            if self.profile_combo.count() > 0:
                self.select_profile(0)

    def load_profiles(self, overwrite=False):
        """Load the profiles into the dropdown list.

        :param overwrite: If we overwrite existing profiles from the plugin.
        :type overwrite: bool
        """
        for profile in self.minimum_needs.get_profiles(overwrite):
            self.profile_combo.addItem(profile)
        minimum_needs = self.minimum_needs.get_full_needs()
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(minimum_needs['profile']))

    def select_profile(self, index):
        """Select a given profile by index.

        Slot for when profile is selected.

        :param index: The selected item's index
        :type index: int
        """
        new_profile = self.profile_combo.itemText(index)
        self.resources_list.clear()
        self.minimum_needs.load_profile(new_profile)
        self.clear_resource_list()
        self.populate_resource_list()
        self.minimum_needs.save()

    def select_profile_by_name(self, profile_name):
        """Select a given profile by profile name

        :param profile_name: The profile name
        :type profile_name: str
        """
        self.select_profile(self.profile_combo.findText(profile_name))

    def mark_current_profile_as_pending(self):
        """Mark the current profile as pending by colouring the text red.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('red'))

    def mark_current_profile_as_saved(self):
        """Mark the current profile as saved by colouring the text black.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('black'))

    def add_new_resource(self):
        """Handle add new resource requests.
        """
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text('')
        parameter_widgets[1].set_text('')
        parameter_widgets[2].set_text('')
        parameter_widgets[3].set_text('')
        parameter_widgets[4].set_text('')
        parameter_widgets[5].set_value(10)
        parameter_widgets[6].set_value(0)
        parameter_widgets[7].set_value(100)
        parameter_widgets[8].set_text(tr('weekly'))
        parameter_widgets[9].set_text(tr(
            'A displaced person should be provided with '
            '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of '
            '%(resource name)s. Though no less than %(minimum allowed)s '
            'and no more than %(maximum allowed)s. This should be provided '
            '%(frequency)s.' % {
                'default value': '{{ Default }}',
                'unit': '{{ Unit }}',
                'units': '{{ Units }}',
                'unit abbreviation': '{{ Unit abbreviation }}',
                'resource name': '{{ Resource name }}',
                'minimum allowed': '{{ Minimum allowed }}',
                'maximum allowed': '{{ Maximum allowed }}',
                'frequency': '{{ Frequency }}'
            }))
        self.stacked_widget.setCurrentWidget(self.resource_edit_page)
        # hide the close button
        self.button_box.button(QDialogButtonBox.Close).setHidden(True)

    def edit_resource(self):
        """Handle edit resource requests.
        """
        self.mark_current_profile_as_pending()
        resource = None
        for item in self.resources_list.selectedItems()[:1]:
            resource = item.resource_full
            self.edit_item = item
        if not resource:
            return
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text(resource['Resource name'])
        parameter_widgets[1].set_text(resource['Resource description'])
        parameter_widgets[2].set_text(resource['Unit'])
        parameter_widgets[3].set_text(resource['Units'])
        parameter_widgets[4].set_text(resource['Unit abbreviation'])
        parameter_widgets[5].set_value(float(resource['Default']))
        parameter_widgets[6].set_value(float(resource['Minimum allowed']))
        parameter_widgets[7].set_value(float(resource['Maximum allowed']))
        parameter_widgets[8].set_text(resource['Frequency'])
        parameter_widgets[9].set_text(resource['Readable sentence'])
        self.switch_context(self.resource_edit_page)

    def set_up_resource_parameters(self):
        """Set up the resource parameter for the add/edit view.
        """
        name_parameter = StringParameter('UUID-1')
        name_parameter.name = self.resource_parameters['Resource name']
        name_parameter.help_text = tr(
            'Name of the resource that will be provided '
            'as part of minimum needs. '
            'e.g. Rice, Water etc.')
        name_parameter.description = tr(
            'A <b>resource</b> is something that you provide to displaced '
            'persons in the event of a disaster. The resource will be made '
            'available at IDP camps and may need to be stockpiled by '
            'contingency planners in their preparations for a disaster.')
        name_parameter.is_required = True
        name_parameter.value = ''

        description_parameter = StringParameter('UUID-2')
        description_parameter.name = self.resource_parameters[
            'Resource description']
        description_parameter.help_text = tr(
            'Description of the resource that will be provided as part of '
            'minimum needs.')
        description_parameter.description = tr(
            'This gives a detailed description of what the resource is and ')
        description_parameter.is_required = True
        description_parameter.value = ''

        unit_parameter = StringParameter('UUID-3')
        unit_parameter.name = self.resource_parameters['Unit']
        unit_parameter.help_text = tr(
            'Single unit for the resources spelled out. e.g. litre, '
            'kilogram etc.')
        unit_parameter.description = tr(
            'A <b>unit</b> is the basic measurement unit used for computing '
            'the allowance per individual. For example when planning water '
            'rations the unit would be single litre.')
        unit_parameter.is_required = True
        unit_parameter.value = ''

        units_parameter = StringParameter('UUID-4')
        units_parameter.name = self.resource_parameters['Units']
        units_parameter.help_text = tr(
            'Multiple units for the resources spelled out. e.g. litres, '
            'kilogram etc.')
        units_parameter.description = tr(
            '<b>Units</b> are the basic measurement used for computing the '
            'allowance per individual. For example when planning water '
            'rations the units would be litres.')
        units_parameter.is_required = True
        units_parameter.value = ''

        unit_abbreviation_parameter = StringParameter('UUID-5')
        unit_abbreviation_parameter.name = \
            self.resource_parameters['Unit abbreviation']
        unit_abbreviation_parameter.help_text = tr(
            'Abbreviations of unit for the resources. e.g. l, kg etc.')
        unit_abbreviation_parameter.description = tr(
            "A <b>unit abbreviation</b> is the basic measurement unit's "
            "shortened. For example when planning water rations "
            "the units would be l.")
        unit_abbreviation_parameter.is_required = True
        unit_abbreviation_parameter.value = ''

        minimum_parameter = FloatParameter('UUID-6')
        minimum_parameter.name = self.resource_parameters['Minimum allowed']
        minimum_parameter.is_required = True
        minimum_parameter.precision = 6
        minimum_parameter.minimum_allowed_value = 0.0
        minimum_parameter.maximum_allowed_value = 99999.0
        minimum_parameter.help_text = tr(
            'The minimum allowable quantity per person. ')
        minimum_parameter.description = tr(
            'The <b>minimum</b> is the minimum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be less '
            'than 0.5l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation')
        minimum_parameter.value = 0.00

        maximum_parameter = FloatParameter('UUID-7')
        maximum_parameter.name = self.resource_parameters['Maximum allowed']
        maximum_parameter.is_required = True
        maximum_parameter.precision = 6
        maximum_parameter.minimum_allowed_value = 0.0
        maximum_parameter.maximum_allowed_value = 99999.0
        maximum_parameter.help_text = tr(
            'The maximum allowable quantity per person. ')
        maximum_parameter.description = tr(
            'The <b>maximum</b> is the maximum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be more '
            'than 67l. This is enforced when tweaking a maximum needs set '
            'before an impact evaluation.')
        maximum_parameter.value = 100.0

        default_parameter = FloatParameter('UUID-8')
        default_parameter.name = self.resource_parameters['Default']
        default_parameter.is_required = True
        default_parameter.precision = 6
        default_parameter.minimum_allowed_value = 0.0
        default_parameter.maximum_allowed_value = 99999.0
        default_parameter.help_text = tr(
            'The default allowable quantity per person. ')
        default_parameter.description = tr(
            "The <b>default</b> is the default allowed quantity of the "
            "resource per person. For example you may indicate that the water "
            "ration per person weekly should be 67l.")
        default_parameter.value = 10.0

        frequency_parameter = StringParameter('UUID-9')
        frequency_parameter.name = self.resource_parameters['Frequency']
        frequency_parameter.help_text = tr(
            "The frequency that this resource needs to be provided to a "
            "displaced person. e.g. weekly, daily, once etc.")
        frequency_parameter.description = tr(
            "The <b>frequency</b> informs the aid worker how regularly this "
            "resource needs to be provided to the displaced person.")
        frequency_parameter.is_required = True
        frequency_parameter.value = tr('weekly')

        sentence_parameter = TextParameter('UUID-10')
        sentence_parameter.name = self.resource_parameters['Readable sentence']
        sentence_parameter.help_text = tr(
            'A readable presentation of the resource.')
        sentence_parameter.description = tr(
            "A <b>readable sentence</b> is a presentation of the resource "
            "that displays all pertinent information. If you are unsure then "
            "use the default. Properties should be included using double "
            "curly brackets '{{' '}}'. Including the resource name would be "
            "achieved by including e.g. {{ Resource name }}")
        sentence_parameter.is_required = True
        sentence_parameter.value = tr(
            'A displaced person should be provided with '
            '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of '
            '%(resource name)s. Though no less than %(minimum allowed)s '
            'and no more than %(maximum allowed)s. This should be provided '
            '%(frequency)s.' % {
                'default value': '{{ Default }}',
                'unit': '{{ Unit }}',
                'units': '{{ Units }}',
                'unit abbreviation': '{{ Unit abbreviation }}',
                'resource name': '{{ Resource name }}',
                'minimum allowed': '{{ Minimum allowed }}',
                'maximum allowed': '{{ Maximum allowed }}',
                'frequency': '{{ Frequency }}'
            })

        parameters = [
            name_parameter,
            description_parameter,
            unit_parameter,
            units_parameter,
            unit_abbreviation_parameter,
            default_parameter,
            minimum_parameter,
            maximum_parameter,
            frequency_parameter,
            sentence_parameter
        ]
        parameter_container = ParameterContainer(parameters)
        parameter_container.setup_ui()

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(parameter_container)
        self.parameters_scrollarea.setLayout(layout)

    def remove_resource(self):
        """Remove the currently selected resource.
        """
        self.mark_current_profile_as_pending()
        for item in self.resources_list.selectedItems():
            self.resources_list.takeItem(self.resources_list.row(item))

    def discard_changes(self):
        """Discard the changes to the resource add/edit.
        """
        self.edit_item = None
        self.switch_context(self.profile_edit_page)

    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        # To store parameters, we need the english version.
        translated_to_english = dict(
            (y, x) for x, y in list(self.resource_parameters.items()))
        resource = {}
        for parameter in parameters:
            resource[translated_to_english[parameter.name]] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds as e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError as e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMinimumError as e:
            warning = self.tr(
                'Problem - minimum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        # end of test for parameter validity

        self.add_resource(resource)
        self.switch_context(self.profile_edit_page)

    def import_profile(self):
        """ Import minimum needs from an existing json file.

        The minimum needs are loaded from a file into the table. This state
        is only saved if the form is accepted.
        """
        # noinspection PyCallByClass,PyTypeChecker
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptOpen)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        path_name = resources_path('minimum_needs')
        file_name_dialog.setDirectory(path_name)
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        else:
            return -1

        if self.minimum_needs.read_from_file(file_name) == -1:
            return -1

        self.clear_resource_list()
        self.populate_resource_list()
        self.switch_context(self.profile_edit_page)

    def export_profile(self):
        """ Export minimum needs to a json file.

        This method will save the current state of the minimum needs setup.
        Then open a dialog allowing the user to browse to the desired
        destination location and allow the user to save the needs as a json
        file.
        """
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptSave)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        file_name = None
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        if file_name != '' and file_name is not None:
            self.minimum_needs.write_to_file(file_name)

    def save_profile(self):
        """ Save the current state of the minimum needs widget.

        The minimum needs widget current state is saved to the QSettings via
        the appropriate QMinimumNeeds class' method.
        """
        minimum_needs = {'resources': []}
        for index in range(self.resources_list.count()):
            item = self.resources_list.item(index)
            minimum_needs['resources'].append(item.resource_full)
        minimum_needs['provenance'] = self.provenance.text()
        minimum_needs['profile'] = self.profile_combo.itemText(
            self.profile_combo.currentIndex()
        )
        self.minimum_needs.update_minimum_needs(minimum_needs)
        self.minimum_needs.save()
        self.minimum_needs.save_profile(minimum_needs['profile'])
        self.mark_current_profile_as_saved()

    def save_profile_as(self):
        """Save the minimum needs under a new profile name.
        """
        # noinspection PyCallByClass,PyTypeChecker
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptSave)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        dir = os.path.join(QgsApplication.qgisSettingsDirPath(),
                           'inasafe', 'minimum_needs')
        file_name_dialog.setDirectory(expanduser(dir))
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        else:
            return
        file_name = basename(file_name)
        file_name = file_name.replace('.json', '')
        minimum_needs = {'resources': []}
        self.mark_current_profile_as_saved()
        for index in range(self.resources_list.count()):
            item = self.resources_list.item(index)
            minimum_needs['resources'].append(item.resource_full)
        minimum_needs['provenance'] = self.provenance.text()
        minimum_needs['profile'] = file_name
        self.minimum_needs.update_minimum_needs(minimum_needs)
        self.minimum_needs.save()
        self.minimum_needs.save_profile(file_name)
        if self.profile_combo.findText(file_name) == -1:
            self.profile_combo.addItem(file_name)
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(file_name))

    def new_profile(self):
        """Create a new profile by name.
        """
        # noinspection PyCallByClass,PyTypeChecker
        dir = os.path.join(QgsApplication.qgisSettingsDirPath(),
                           'inasafe', 'minimum_needs')
        file_name, __ = QFileDialog.getSaveFileName(
            self,
            self.tr('Create a minimum needs profile'),
            expanduser(dir),
            self.tr('JSON files (*.json *.JSON)'),
            options=QFileDialog.DontUseNativeDialog)
        if not file_name:
            return
        file_name = basename(file_name)
        if self.profile_combo.findText(file_name) == -1:
            minimum_needs = {
                'resources': [], 'provenance': '', 'profile': file_name}
            self.minimum_needs.update_minimum_needs(minimum_needs)
            self.minimum_needs.save_profile(file_name)
            self.profile_combo.addItem(file_name)
            self.clear_resource_list()
            self.profile_combo.setCurrentIndex(
                self.profile_combo.findText(file_name))
        else:
            self.profile_combo.setCurrentIndex(
                self.profile_combo.findText(file_name))
            self.select_profile_by_name(file_name)

    def page_changed(self, index):
        """Slot for when tab changes in the stacked widget changes.

        :param index: Index of the now active tab.
        :type index: int
        """
        if index == 0:  # profile edit page
            for item in self.resource_editing_buttons:
                item.hide()
            for item in self.profile_editing_widgets:
                item.setEnabled(True)
            for item in self.profile_editing_buttons:
                item.show()
        else:  # resource_edit_page
            for item in self.resource_editing_buttons:
                item.show()
            for item in self.profile_editing_widgets:
                item.setEnabled(False)
            for item in self.profile_editing_buttons:
                item.hide()

    def switch_context(self, page):
        """Switch context tabs by tab widget name.

        :param page: The page should be focussed.
        :type page: QWidget
        """
        # noinspection PyUnresolvedReferences
        if page.objectName() == 'profile_edit_page':
            self.stacked_widget.setCurrentIndex(0)
            self.button_box.button(QDialogButtonBox.Close).setHidden(False)
        else:  # resource_edit_page
            self.stacked_widget.setCurrentIndex(1)
            # hide the close button
            self.button_box.button(QDialogButtonBox.Close).setHidden(True)

    def remove_profile(self):
        """Remove the current profile.

        Make sure the user is sure.
        """
        profile_name = self.profile_combo.currentText()
        # noinspection PyTypeChecker
        button_selected = QMessageBox.warning(
            None,
            'Remove Profile',
            self.tr('Remove %s.') % profile_name,
            QMessageBox.Ok,
            QMessageBox.Cancel
        )
        if button_selected == QMessageBox.Ok:
            self.profile_combo.removeItem(
                self.profile_combo.currentIndex()
            )
            self.minimum_needs.remove_profile(profile_name)
            self.select_profile(self.profile_combo.currentIndex())

    @pyqtSlot(bool)  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        .. versionadded: 3.2.1

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user.

        .. versionadded: 3.2.1
        """
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = needs_manager_helps()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)
Exemple #11
0
def population_infographic_extractor(impact_report, component_metadata):
    """Extracting aggregate result of demographic.

    :param impact_report: the impact report that acts as a proxy to fetch
        all the data that extractor needed
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component_metadata: the component metadata. Used to obtain
        information about the component we want to render
    :type component_metadata: safe.report.report_metadata.
        ReportComponentsMetadata

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {}
    extra_args = component_metadata.extra_args
    """Initializations"""
    hazard_layer = impact_report.hazard
    analysis_layer = impact_report.analysis
    analysis_layer_fields = analysis_layer.keywords['inasafe_fields']
    icons = component_metadata.extra_args.get('icons')

    # this report sections only applies if it is a population report.
    population_fields = [
        population_count_field['key'],
        exposure_count_field['key'] % (exposure_population['key'], ),
    ] + [f['key'] for f in minimum_needs_fields]

    for item in population_fields:
        if item in analysis_layer_fields:
            break
    else:
        return context

    # We try to get total affected field
    # if it didn't exists, check other fields to show
    total_affected_fields = [
        total_affected_field['key'],
        # We might want to check other fields, but turn it off until further
        # discussion
        population_count_field['key'],
        exposure_count_field['key'] % (exposure_population['key'], ),
    ]

    for item in total_affected_fields:
        if item in analysis_layer_fields:
            total_affected = value_from_field_name(analysis_layer_fields[item],
                                                   analysis_layer)
            total_affected_field_used = item
            break
    else:
        return context

    if displaced_field['key'] in analysis_layer_fields:
        total_displaced = value_from_field_name(
            analysis_layer_fields[displaced_field['key']], analysis_layer)
    else:
        return context

    sections = OrderedDict()
    """People Section"""

    # Take default value from definitions
    people_header = resolve_from_dictionary(extra_args,
                                            ['sections', 'people', 'header'])
    people_items = resolve_from_dictionary(extra_args,
                                           ['sections', 'people', 'items'])

    # create context for affected infographic
    sub_header = resolve_from_dictionary(people_items[0], 'sub_header')

    # retrieve relevant header based on the fields we showed.
    sub_header = sub_header[total_affected_field_used]

    affected_infographic = PeopleInfographicElement(
        header=sub_header,
        icon=icons.get(total_affected_field['key']),
        number=total_affected)

    # create context for displaced infographic
    sub_header = resolve_from_dictionary(people_items[1], 'sub_header')
    sub_header_note_format = resolve_from_dictionary(people_items[1],
                                                     'sub_header_note_format')
    rate_description_format = resolve_from_dictionary(
        people_items[1], 'rate_description_format')
    rate_description = []

    hazard_classification = layer_hazard_classification(hazard_layer)
    for hazard_class in hazard_classification['classes']:
        displacement_rate = hazard_class.get('displacement_rate', 0)
        if displacement_rate:
            rate_description.append(
                rate_description_format.format(**hazard_class))

    rate_description_string = ', '.join(rate_description)

    sub_header_note = sub_header_note_format.format(
        rate_description=rate_description_string)

    displaced_infographic = PeopleInfographicElement(
        header=sub_header,
        header_note=sub_header_note,
        icon=icons.get(displaced_field['key']),
        number=total_displaced)

    sections['people'] = {
        'header': people_header,
        'items': [affected_infographic, displaced_infographic]
    }
    """Vulnerability Section"""

    # Take default value from definitions
    vulnerability_items = resolve_from_dictionary(
        extra_args, ['sections', 'vulnerability', 'items'])

    vulnerability_section_header = resolve_from_dictionary(
        extra_args, ['sections', 'vulnerability', 'header'])

    vulnerability_section_sub_header_format = resolve_from_dictionary(
        extra_args, ['sections', 'vulnerability', 'sub_header_format'])

    infographic_elements = []
    for group in vulnerability_items:
        fields = group['fields']
        group_header = group['sub_group_header']
        bootstrap_column = group['bootstrap_column']
        element_column = group['element_column']
        headers = group['headers']
        elements = []
        for field, header in zip(fields, headers):
            field_key = field['key']
            try:
                field_name = analysis_layer_fields[field_key]
                value = value_from_field_name(field_name, analysis_layer)
            except KeyError:
                # It means the field is not there
                continue

            if value:
                value_percentage = value * 100.0 / total_displaced
            else:
                value_percentage = 0

            infographic_element = PeopleVulnerabilityInfographicElement(
                header=header,
                icon=icons.get(field_key),
                number=value,
                percentage=value_percentage)
            elements.append(infographic_element)
        if elements:
            infographic_elements.append({
                'group_header': group_header,
                'bootstrap_column': bootstrap_column,
                'element_column': element_column,
                'items': elements
            })

    total_displaced_rounded = format_number(total_displaced,
                                            enable_rounding=True,
                                            is_population=True)

    sections['vulnerability'] = {
        'header':
        vulnerability_section_header,
        'small_header':
        vulnerability_section_sub_header_format.format(
            number_displaced=total_displaced_rounded),
        'items':
        infographic_elements
    }
    """Minimum Needs Section"""

    minimum_needs_header = resolve_from_dictionary(
        extra_args, ['sections', 'minimum_needs', 'header'])
    empty_unit_string = resolve_from_dictionary(
        extra_args, ['sections', 'minimum_needs', 'empty_unit_string'])

    items = []

    for item in minimum_needs_fields:
        need = item['need_parameter']
        if isinstance(need, ResourceParameter):

            needs_count = value_from_field_name(item['field_name'],
                                                analysis_layer)

            if need.unit.abbreviation:
                unit_string = '{unit}/{frequency}'.format(
                    unit=need.unit.abbreviation, frequency=need.frequency)
            else:
                unit_string = empty_unit_string

            item = PeopleMinimumNeedsInfographicElement(header=item['name'],
                                                        icon=icons.get(
                                                            item['key']),
                                                        number=needs_count,
                                                        unit=unit_string)
            items.append(item)

    # TODO: get from impact function provenance later
    needs_profile = NeedsProfile()

    sections['minimum_needs'] = {
        'header': minimum_needs_header,
        'small_header': needs_profile.provenance,
        'items': items,
    }
    """Population Charts"""

    population_donut_path = impact_report.component_absolute_output_path(
        'population-chart-png')

    css_label_classes = []
    try:
        population_chart_context = impact_report.metadata.component_by_key(
            'population-chart').context['context']
        """
        :type: safe.report.extractors.infographic_elements.svg_charts.
            DonutChartContext
        """
        for pie_slice in population_chart_context.slices:
            label = pie_slice['label']
            if not label:
                continue
            css_class = label.replace(' ', '').lower()
            css_label_classes.append(css_class)
    except KeyError:
        population_chart_context = None

    sections['population_chart'] = {
        'img_path': resource_url(population_donut_path),
        'context': population_chart_context,
        'css_label_classes': css_label_classes
    }

    context['brand_logo'] = resource_url(
        resources_path('img', 'logos', 'inasafe-logo-white.png'))
    context['sections'] = sections
    context['title'] = analysis_layer.title() or value_from_field_name(
        analysis_name_field['field_name'], analysis_layer)

    return context
    def __init__(self, parent=None, dock=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param dock: Dock widget instance that we can notify of changes.
        :type dock: Dock
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.dock = dock
        # These are in the little button bar at the top
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up things for context help
        self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        # initial sync profile_combo and resource list
        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
        self.select_profile(self.profile_combo.currentIndex())
class NeedsManagerDialog(QDialog, FORM_CLASS):
    """Dialog class for the InaSAFE global minimum needs configuration.

    .. versionadded:: 2.2.
    """

    def __init__(self, parent=None, dock=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param dock: Dock widget instance that we can notify of changes.
        :type dock: Dock
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)
        self.dock = dock
        # These are in the little button bar at the top
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up things for context help
        self.help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        # initial sync profile_combo and resource list
        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
        self.select_profile(self.profile_combo.currentIndex())

    def reject(self):
        """Overload the base dialog reject event so we can handle state change.

        If the user is in resource editing mode, clicking close button,
        window [x] or pressing escape should switch context back to the
        profile view, not close the whole window.

        See https://github.com/AIFDR/inasafe/issues/1387
        """
        if self.stacked_widget.currentWidget() == self.resource_edit_page:
            self.edit_item = None
            self.switch_context(self.profile_edit_page)
        else:
            super(NeedsManagerDialog, self).reject()

    def populate_resource_list(self):
        """Populate the list resource list.
        """
        minimum_needs = self.minimum_needs.get_full_needs()
        for full_resource in minimum_needs["resources"]:
            self.add_resource(full_resource)
        self.provenance.setText(minimum_needs["provenance"])

    def clear_resource_list(self):
        """Clear the resource list.
        """
        self.resources_list.clear()

    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtGui.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)

    def load_profiles(self):
        """Load the profiles into the dropdown list.
        """
        for profile in self.minimum_needs.get_profiles():
            self.profile_combo.addItem(profile)
        minimum_needs = self.minimum_needs.get_full_needs()
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(minimum_needs['profile']))

    def select_profile(self, index):
        """Select a given profile by index.

        Slot for when profile is selected.

        :param index: The selected item's index
        :type index: int
        """
        new_profile = self.profile_combo.itemText(index)
        self.resources_list.clear()
        self.minimum_needs.load_profile(new_profile)
        self.clear_resource_list()
        self.populate_resource_list()
        self.minimum_needs.save()

    def select_profile_by_name(self, profile_name):
        """Select a given profile by profile name

        :param profile_name: The profile name
        :type profile_name: str
        """
        self.select_profile(self.profile_combo.findText(profile_name))

    def mark_current_profile_as_pending(self):
        """Mark the current profile as pending by colouring the text red.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('red'))

    def mark_current_profile_as_saved(self):
        """Mark the current profile as saved by colouring the text black.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('black'))

    def add_new_resource(self):
        """Handle add new resource requests.
        """
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text('')
        parameter_widgets[1].set_text('')
        parameter_widgets[2].set_text('')
        parameter_widgets[3].set_text('')
        parameter_widgets[4].set_text('')
        parameter_widgets[5].set_value(10)
        parameter_widgets[6].set_value(0)
        parameter_widgets[7].set_value(100)
        parameter_widgets[8].set_text(tr('weekly'))
        parameter_widgets[9].set_text(tr(
            "A displaced person should be provided with "
            "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of "
            "{{ Resource name }}. Though no less than {{ Minimum allowed }} "
            "and no more than {{ Maximum allowed }}. This should be provided "
            "{{ Frequency }}."))
        self.stacked_widget.setCurrentWidget(self.resource_edit_page)
        # hide the close button
        self.button_box.button(QDialogButtonBox.Close).setHidden(True)

    def edit_resource(self):
        """Handle edit resource requests.
        """
        self.mark_current_profile_as_pending()
        resource = None
        for item in self.resources_list.selectedItems()[:1]:
            resource = item.resource_full
            self.edit_item = item
        if not resource:
            return
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text(resource['Resource name'])
        parameter_widgets[1].set_text(resource['Resource description'])
        parameter_widgets[2].set_text(resource['Unit'])
        parameter_widgets[3].set_text(resource['Units'])
        parameter_widgets[4].set_text(resource['Unit abbreviation'])
        parameter_widgets[5].set_value(float(resource['Default']))
        parameter_widgets[6].set_value(float(resource['Minimum allowed']))
        parameter_widgets[7].set_value(float(resource['Maximum allowed']))
        parameter_widgets[8].set_text(resource['Frequency'])
        parameter_widgets[9].set_text(resource['Readable sentence'])
        self.switch_context(self.resource_edit_page)

    def set_up_resource_parameters(self):
        """Set up the resource parameter for the add/edit view.
        """
        name_parameter = StringParameter('UUID-1')
        name_parameter.name = tr('Resource name')
        name_parameter.help_text = tr(
            'Name of the resource that will be provided '
            'as part of minimum needs. '
            'e.g. Rice, Water etc.')
        name_parameter.description = tr(
            'A <b>resource</b> is something that you provide to displaced '
            'persons in the event of a disaster. The resource will be made '
            'available at IDP camps and may need to be stockpiled by '
            'contingency planners in their preparations for a disaster.')
        name_parameter.is_required = True
        name_parameter.value = ''

        description_parameter = StringParameter('UUID-2')
        description_parameter.name = tr('Resource description')
        description_parameter.help_text = tr(
            'Description of the resource that will be provided as part of '
            'minimum needs.')
        description_parameter.description = tr(
            'This gives a detailed description of what the resource is and ')
        description_parameter.is_required = True
        description_parameter.value = ''

        unit_parameter = StringParameter('UUID-3')
        unit_parameter.name = tr('Unit')
        unit_parameter.help_text = tr(
            'Single unit for the resources spelled out. e.g. litre, '
            'kilogram etc.')
        unit_parameter.description = tr(
            'A <b>unit</b> is the basic measurement unit used for computing '
            'the allowance per individual. For example when planning water '
            'rations the unit would be single litre.')
        unit_parameter.is_required = True
        unit_parameter.value = ''

        units_parameter = StringParameter('UUID-4')
        units_parameter.name = tr('Units')
        units_parameter.help_text = tr(
            'Multiple units for the resources spelled out. e.g. litres, '
            'kilogram etc.')
        units_parameter.description = tr(
            '<b>Units</b> are the basic measurement used for computing the '
            'allowance per individual. For example when planning water '
            'rations the units would be litres.')
        units_parameter.is_required = True
        units_parameter.value = ''

        unit_abbreviation_parameter = StringParameter('UUID-5')
        unit_abbreviation_parameter.name = tr('Unit abbreviation')
        unit_abbreviation_parameter.help_text = tr(
            'Abbreviations of unit for the resources. e.g. l, kg etc.')
        unit_abbreviation_parameter.description = tr(
            "A <b>unti abbreviation</b> is the basic measurement unit's "
            "shortened. For example when planning water rations "
            "the units would be l.")
        unit_abbreviation_parameter.is_required = True
        unit_abbreviation_parameter.value = ''

        minimum_parameter = FloatParameter('UUID-6')
        minimum_parameter.name = tr('Minimum allowed')
        minimum_parameter.is_required = True
        minimum_parameter.precision = 2
        minimum_parameter.minimum_allowed_value = -99999.0
        minimum_parameter.maximum_allowed_value = 99999.0
        minimum_parameter.help_text = tr(
            'The minimum allowable quantity per person. ')
        minimum_parameter.description = tr(
            'The <b>minimum</b> is the minimum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be less '
            'than 0.5l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation')
        minimum_parameter.value = 0.00

        maximum_parameter = FloatParameter('UUID-7')
        maximum_parameter.name = tr('Maximum allowed')
        maximum_parameter.is_required = True
        maximum_parameter.precision = 2
        maximum_parameter.minimum_allowed_value = -99999.0
        maximum_parameter.maximum_allowed_value = 99999.0
        maximum_parameter.help_text = tr(
            'The maximum allowable quantity per person. ')
        maximum_parameter.description = tr(
            'The <b>maximum</b> is the maximum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be more '
            'than 50l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation.')
        maximum_parameter.value = 100.0

        default_parameter = FloatParameter('UUID-8')
        default_parameter.name = tr('Default')
        default_parameter.is_required = True
        default_parameter.precision = 2
        default_parameter.minimum_allowed_value = -99999.0
        default_parameter.maximum_allowed_value = 99999.0
        default_parameter.help_text = tr(
            'The default allowable quantity per person. ')
        default_parameter.description = tr(
            "The <b>default</b> is the default allowed quantity of the "
            "resource per person. For example you may indicate that the water "
            "ration per person weekly should be 67l.")
        default_parameter.value = 10.0

        frequency_parameter = StringParameter('UUID-9')
        frequency_parameter.name = tr('Frequency')
        frequency_parameter.help_text = tr(
            "The frequency that this resource needs to be provided to a "
            "displaced person. e.g. weekly, daily, once etc.")
        frequency_parameter.description = tr(
            "The <b>frequency</b> informs the aid worker how regularly this "
            "resource needs to be provided to the displaced person.")
        frequency_parameter.is_required = True
        frequency_parameter.value = tr('weekly')

        sentence_parameter = TextParameter('UUID-10')
        sentence_parameter.name = tr('Readable sentence')
        sentence_parameter.help_text = tr(
            'A readable presentation of the resource.')
        sentence_parameter.description = tr(
            "A <b>readable sentence</b> is a presentation of the resource "
            "that displays all pertinent information. If you are unsure then "
            "use the default. Properties should be included using double "
            "curly brackets '{{' '}}'. Including the resource name would be "
            "achieved by including e.g. {{ Resource name }}")
        sentence_parameter.is_required = True
        sentence_parameter.value = tr(
            "A displaced person should be provided with "
            "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of "
            "{{ Resource name }}. Though no less than {{ Minimum allowed }} "
            "and no more than {{ Maximum allowed }}. This should be provided "
            "{{ Frequency }}.")

        parameters = [
            name_parameter,
            description_parameter,
            unit_parameter,
            units_parameter,
            unit_abbreviation_parameter,
            default_parameter,
            minimum_parameter,
            maximum_parameter,
            frequency_parameter,
            sentence_parameter
        ]
        parameter_container = ParameterContainer(parameters)
        parameter_container.setup_ui()

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(parameter_container)
        self.parameters_scrollarea.setLayout(layout)

    def remove_resource(self):
        """Remove the currently selected resource.
        """
        self.mark_current_profile_as_pending()
        for item in self.resources_list.selectedItems():
            self.resources_list.takeItem(self.resources_list.row(item))

    def discard_changes(self):
        """Discard the changes to the resource add/edit.
        """
        self.edit_item = None
        self.switch_context(self.profile_edit_page)

    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        resource = {}
        for parameter in parameters:
            resource[parameter.name] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds, e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError, e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
    def __init__(self, parent=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)

        # These are in the little button bar at the top
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up context help
        self.help_context = 'needs_manager'
        help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        help_button.clicked.connect(self.show_help)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
class NeedsManagerDialog(QDialog, FORM_CLASS):
    """Dialog class for the InaSAFE global minimum needs configuration.

    .. versionadded:: 2.2.
    """

    def __init__(self, parent=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget
        """

        QtGui.QDialog.__init__(self, parent)
        self.setupUi(self)

        # These are in the little button bar at the top
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up context help
        self.help_context = 'needs_manager'
        help_button = self.button_box.button(QtGui.QDialogButtonBox.Help)
        help_button.clicked.connect(self.show_help)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)

    def reject(self):
        """Overload the base dialog reject event so we can handle state change.

        If the user is in resource editing mode, clicking close button,
        window [x] or pressing escape should switch context back to the
        profile view, not close the whole window.

        See https://github.com/AIFDR/inasafe/issues/1387
        """
        if self.stacked_widget.currentWidget() == self.resource_edit_page:
            self.switch_context(self.profile_edit_page)
        else:
            super(NeedsManagerDialog, self).reject()

    def show_help(self):
        """Load the help text for the dialog."""
        show_context_help(self.help_context)

    def populate_resource_list(self):
        """Populate the list resource list.
        """
        minimum_needs = self.minimum_needs.get_full_needs()
        for full_resource in minimum_needs["resources"]:
            self.add_resource(full_resource)
        self.provenance.setText(minimum_needs["provenance"])

    def clear_resource_list(self):
        """Clear the resource list.
        """
        self.resources_list.clear()

    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtGui.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)

    def load_profiles(self):
        """Load the profiles into the dropdown list.
        """
        for profile in self.minimum_needs.get_profiles():
            self.profile_combo.addItem(profile)
        minimum_needs = self.minimum_needs.get_full_needs()
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(minimum_needs['profile']))

    def select_profile(self, index):
        """Select a given profile by index.

        Slot for when profile is selected.

        :param index: The selected item's index
        :type index: int
        """
        new_profile = self.profile_combo.itemText(index)
        self.resources_list.clear()
        self.minimum_needs.load_profile(new_profile)
        self.clear_resource_list()
        self.populate_resource_list()
        self.minimum_needs.save()

    def select_profile_by_name(self, profile_name):
        """Select a given profile by profile name

        :param profile_name: The profile name
        :type profile_name: str
        """
        self.select_profile(self.profile_combo.findText(profile_name))

    def mark_current_profile_as_pending(self):
        """Mark the current profile as pending by colouring the text red.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('red'))

    def mark_current_profile_as_saved(self):
        """Mark the current profile as saved by colouring the text black.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('black'))

    def add_new_resource(self):
        """Handle add new resource requests.
        """
        parameters_widget = [
            self.resource_widget.layout().itemAt(i) for i in
            range(self.resource_widget.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text('')
        parameter_widgets[1].set_text('')
        parameter_widgets[2].set_text('')
        parameter_widgets[3].set_text('')
        parameter_widgets[4].set_text('')
        parameter_widgets[5].set_value(10)
        parameter_widgets[6].set_value(0)
        parameter_widgets[7].set_value(100)
        parameter_widgets[8].set_text('weekly')
        parameter_widgets[9].set_text(
            "A displaced person should be provided with "
            "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of "
            "{{ Resource name }}. Though no less than {{ Minimum allowed }} "
            "and no more than {{ Maximum allowed }}. This should be provided "
            "{{ Frequency }}.")
        self.stacked_widget.setCurrentWidget(self.resource_edit_page)

    def edit_resource(self):
        """Handle edit resource requests.
        """
        self.mark_current_profile_as_pending()
        resource = None
        for item in self.resources_list.selectedItems()[:1]:
            resource = item.resource_full
            self.edit_item = item
        if not resource:
            return
        parameters_widget = [
            self.resource_widget.layout().itemAt(i) for i in
            range(self.resource_widget.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text(resource['Resource name'])
        parameter_widgets[1].set_text(resource['Resource description'])
        parameter_widgets[2].set_text(resource['Unit'])
        parameter_widgets[3].set_text(resource['Units'])
        parameter_widgets[4].set_text(resource['Unit abbreviation'])
        parameter_widgets[5].set_value(float(resource['Default']))
        parameter_widgets[6].set_value(float(resource['Minimum allowed']))
        parameter_widgets[7].set_value(float(resource['Maximum allowed']))
        parameter_widgets[8].set_text(resource['Frequency'])
        parameter_widgets[9].set_text(resource['Readable sentence'])
        self.switch_context(self.resource_edit_page)

    def set_up_resource_parameters(self):
        """Set up the resource parameter for the add/edit view.
        """
        name_parameter = StringParameter('UUID-1')
        name_parameter.name = 'Resource name'
        name_parameter.help_text = (
            'Name of the resource that will be provided '
            'as part of minimum needs. '
            'e.g. Rice, Water etc.')
        name_parameter.description = (
            'A <b>resource</b> is something that you provide to displaced '
            'persons in the event of a disaster. The resource will be made '
            'available at IDP camps and may need to be stockpiled by '
            'contingency planners in their preparations for a disaster.')
        name_parameter.is_required = True
        name_parameter.value = ''

        description_parameter = StringParameter('UUID-2')
        description_parameter.name = 'Resource description'
        description_parameter.help_text = (
            'Description of the resource that will be provided as part of '
            'minimum needs.')
        description_parameter.description = (
            'This gives a detailed description of what the resource is and ')
        description_parameter.is_required = True
        description_parameter.value = ''

        unit_parameter = StringParameter('UUID-3')
        unit_parameter.name = 'Unit'
        unit_parameter.help_text = (
            'Single unit for the resources spelled out. e.g. litre, '
            'kilogram etc.')
        unit_parameter.description = (
            'A <b>unit</b> is the basic measurement unit used for computing '
            'the allowance per individual. For example when planning water '
            'rations the unit would be single litre.')
        unit_parameter.is_required = True
        unit_parameter.value = ''

        units_parameter = StringParameter('UUID-4')
        units_parameter.name = 'Units'
        units_parameter.help_text = (
            'Multiple units for the resources spelled out. e.g. litres, '
            'kilogram etc.')
        units_parameter.description = (
            '<b>Units</b> are the basic measurement used for computing the '
            'allowance per individual. For example when planning water '
            'rations the units would be litres.')
        units_parameter.is_required = True
        units_parameter.value = ''

        unit_abbreviation_parameter = StringParameter('UUID-5')
        unit_abbreviation_parameter.name = 'Unit abbreviation'
        unit_abbreviation_parameter.help_text = (
            'Abbreviations of unit for the resources. e.g. l, kg etc.')
        unit_abbreviation_parameter.description = (
            "A <b>unti abbreviation</b> is the basic measurement unit's "
            "shortened. For example when planning water rations "
            "the units would be l.")
        unit_abbreviation_parameter.is_required = True
        unit_abbreviation_parameter.value = ''

        minimum_parameter = FloatParameter('UUID-6')
        minimum_parameter.name = 'Minimum allowed'
        minimum_parameter.is_required = True
        minimum_parameter.precision = 2
        minimum_parameter.minimum_allowed_value = -99999.0
        minimum_parameter.maximum_allowed_value = 99999.0
        minimum_parameter.help_text = (
            'The minimum allowable quantity per person. ')
        minimum_parameter.description = (
            'The <b>minimum</b> is the minimum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be less '
            'than 0.5l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation')
        minimum_parameter.value = 0.00

        maximum_parameter = FloatParameter('UUID-7')
        maximum_parameter.name = 'Maximum allowed'
        maximum_parameter.is_required = True
        maximum_parameter.precision = 2
        maximum_parameter.minimum_allowed_value = -99999.0
        maximum_parameter.maximum_allowed_value = 99999.0
        maximum_parameter.help_text = (
            'The maximum allowable quantity per person. ')
        maximum_parameter.description = (
            'The <b>maximum</b> is the maximum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be more '
            'than 50l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation.')
        maximum_parameter.value = 100.0

        default_parameter = FloatParameter('UUID-8')
        default_parameter.name = 'Default'
        default_parameter.is_required = True
        default_parameter.precision = 2
        default_parameter.minimum_allowed_value = -99999.0
        default_parameter.maximum_allowed_value = 99999.0
        default_parameter.help_text = (
            'The default allowable quantity per person. ')
        default_parameter.description = (
            "The <b>default</b> is the default allowed quantity of the "
            "resource per person. For example you may indicate that the water "
            "ration per person weekly should be 67l.")
        default_parameter.value = 10.0

        frequency_parameter = StringParameter('UUID-9')
        frequency_parameter.name = 'Frequency'
        frequency_parameter.help_text = (
            "The frequency that this resource needs to be provided to a "
            "displaced person. e.g. weekly, daily, once etc.")
        frequency_parameter.description = (
            "The <b>frequency</b> informs the aid worker how regularly this "
            "resource needs to be provided to the displaced person.")
        frequency_parameter.is_required = True
        frequency_parameter.value = 'weekly'

        sentence_parameter = StringParameter('UUID-10')
        sentence_parameter.name = 'Readable sentence'
        sentence_parameter.help_text = (
            'A readable presentation of the resource.')
        sentence_parameter.description = (
            "A <b>readable sentence</b> is a presentation of the resource "
            "that displays all pertinent information. If you are unsure then "
            "use the default. Properties should be included using double "
            "curly brackets '{{' '}}'. Including the resource name would be "
            "achieved by including e.g. {{ Resource name }}")
        sentence_parameter.is_required = True
        sentence_parameter.value = (
            "A displaced person should be provided with "
            "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of "
            "{{ Resource name }}. Though no less than {{ Minimum allowed }} "
            "and no more than {{ Maximum allowed }}. This should be provided "
            "{{ Frequency }}.")

        parameters = [
            name_parameter,
            description_parameter,
            unit_parameter,
            units_parameter,
            unit_abbreviation_parameter,
            default_parameter,
            minimum_parameter,
            maximum_parameter,
            frequency_parameter,
            sentence_parameter
        ]
        parameter_container = ParameterContainer(parameters)

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(parameter_container)
        self.resource_widget.setLayout(layout)

    def remove_resource(self):
        """Remove the currently selected resource.
        """
        self.mark_current_profile_as_pending()
        for item in self.resources_list.selectedItems():
            self.resources_list.takeItem(self.resources_list.row(item))

    def discard_changes(self):
        """Discard the changes to the resource add/edit.
        """
        self.edit_item = None
        self.switch_context(self.profile_edit_page)

    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.resource_widget.layout().itemAt(i) for i in
            range(self.resource_widget.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        resource = {}
        for parameter in parameters:
            resource[parameter.name] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds, e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError, e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + e.message
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
class NeedsManagerDialog(QDialog, FORM_CLASS):
    """Dialog class for the InaSAFE global minimum needs configuration.

    .. versionadded:: 2.2.
    """

    def __init__(self, parent=None, dock=None):
        """Constructor for the minimum needs dialog.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param dock: Dock widget instance that we can notify of changes.
        :type dock: Dock
        """
        QtWidgets.QDialog.__init__(self, parent)
        # List of parameters with the translated name.
        self.resource_parameters = {
            'Resource name': tr('Resource name'),
            'Resource description': tr('Resource description'),
            'Unit': tr('Unit'),
            'Units': tr('Units'),
            'Unit abbreviation': tr('Unit abbreviation'),
            'Minimum allowed': tr('Minimum allowed'),
            'Maximum allowed': tr('Maximum allowed'),
            'Default': tr('Default'),
            'Frequency': tr('Frequency'),
            'Readable sentence': tr('Readable sentence')
        }

        self.setupUi(self)
        icon = resources_path('img', 'icons', 'show-minimum-needs.svg')
        self.setWindowIcon(QtGui.QIcon(icon))
        self.dock = dock
        # These are in the little button bar at the bottom
        # 'Remove resource' button
        # noinspection PyUnresolvedReferences
        self.remove_resource_button.clicked.connect(self.remove_resource)
        self.remove_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'remove.svg')))

        # Add resource
        # noinspection PyUnresolvedReferences
        self.add_resource_button.clicked.connect(self.add_new_resource)
        self.add_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'add.svg')))
        # Edit resource
        # noinspection PyUnresolvedReferences
        self.edit_resource_button.clicked.connect(self.edit_resource)
        self.edit_resource_button.setIcon(
            QIcon(os.path.join(
                resources_path(), 'img', 'icons', 'edit.svg')))

        # Discard changes to a resource
        self.discard_changes_button = QPushButton(self.tr('Discard changes'))
        self.button_box.addButton(
            self.discard_changes_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.discard_changes_button.clicked.connect(self.discard_changes)

        # Restore defaults profiles
        self.restore_defaults_button = QPushButton(self.tr('Restore defaults'))
        self.button_box.addButton(
            self.restore_defaults_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.restore_defaults_button.clicked.connect(self.restore_defaults)

        # Save changes to a resource
        self.save_resource_button = QPushButton(self.tr('Save resource'))
        self.button_box.addButton(
            self.save_resource_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_resource_button.clicked.connect(self.save_resource)

        # Export profile button
        self.export_profile_button = QPushButton(self.tr('Export ...'))
        self.button_box.addButton(
            self.export_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.export_profile_button.clicked.connect(self.export_profile)

        # Import profile button
        self.import_profile_button = QPushButton(self.tr('Import ...'))
        self.button_box.addButton(
            self.import_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.import_profile_button.clicked.connect(self.import_profile)

        # New profile button
        self.new_profile_button = QPushButton(self.tr('New'))
        self.button_box.addButton(
            self.new_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.new_profile_button.clicked.connect(self.new_profile)

        # Save profile button
        self.save_profile_button = QPushButton(self.tr('Save'))
        self.button_box.addButton(
            self.save_profile_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_button.clicked.connect(self.save_profile)

        # 'Save as' profile button
        self.save_profile_as_button = QPushButton(self.tr('Save as'))
        self.button_box.addButton(
            self.save_profile_as_button, QDialogButtonBox.ActionRole)
        # noinspection PyUnresolvedReferences
        self.save_profile_as_button.clicked.connect(
            self.save_profile_as)

        # Set up things for context help
        self.help_button = self.button_box.button(
            QtWidgets.QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)
        self.main_stacked_widget.setCurrentIndex(1)

        self.minimum_needs = NeedsProfile()
        self.edit_item = None

        # Remove profile button
        # noinspection PyUnresolvedReferences
        self.remove_profile_button.clicked.connect(self.remove_profile)

        # These are all buttons that will get hidden on context change
        # to the profile editing view
        self.profile_editing_buttons = list()
        self.profile_editing_buttons.append(self.remove_resource_button)
        self.profile_editing_buttons.append(self.add_resource_button)
        self.profile_editing_buttons.append(self.edit_resource_button)
        self.profile_editing_buttons.append(self.export_profile_button)
        self.profile_editing_buttons.append(self.import_profile_button)
        self.profile_editing_buttons.append(self.new_profile_button)
        self.profile_editing_buttons.append(self.save_profile_button)
        self.profile_editing_buttons.append(self.save_profile_as_button)
        # We also keep a list of all widgets to disable in context of resource
        # editing (not hidden, just disabled)
        self.profile_editing_widgets = self.profile_editing_buttons
        self.profile_editing_widgets.append(self.remove_profile_button)
        self.profile_editing_widgets.append(self.profile_combo)
        # These are all buttons that will get hidden on context change
        # to the resource editing view
        self.resource_editing_buttons = list()
        self.resource_editing_buttons.append(self.discard_changes_button)
        self.resource_editing_buttons.append(self.save_resource_button)
        for item in self.resource_editing_buttons:
            item.hide()

        self.load_profiles()
        # Next 2 lines fixes issues #1388 #1389 #1390 #1391
        if self.profile_combo.count() > 0:
            self.select_profile(0)

        # initial sync profile_combo and resource list
        self.clear_resource_list()
        self.populate_resource_list()
        self.set_up_resource_parameters()
        # Only do this afterward load_profiles to avoid the resource list
        # being updated
        # noinspection PyUnresolvedReferences
        self.profile_combo.activated.connect(self.select_profile)
        # noinspection PyUnresolvedReferences
        self.stacked_widget.currentChanged.connect(self.page_changed)
        self.select_profile(self.profile_combo.currentIndex())

    def reject(self):
        """Overload the base dialog reject event so we can handle state change.

        If the user is in resource editing mode, clicking close button,
        window [x] or pressing escape should switch context back to the
        profile view, not close the whole window.

        See https://github.com/AIFDR/inasafe/issues/1387
        """
        if self.stacked_widget.currentWidget() == self.resource_edit_page:
            self.edit_item = None
            self.switch_context(self.profile_edit_page)
        else:
            super(NeedsManagerDialog, self).reject()

    def populate_resource_list(self):
        """Populate the list resource list.
        """
        minimum_needs = self.minimum_needs.get_full_needs()
        for full_resource in minimum_needs["resources"]:
            self.add_resource(full_resource)
        self.provenance.setText(minimum_needs["provenance"])

    def clear_resource_list(self):
        """Clear the resource list.
        """
        self.resources_list.clear()

    def add_resource(self, resource):
        """Add a resource to the minimum needs table.

        :param resource: The resource to be added
        :type resource: dict
        """
        updated_sentence = NeedsProfile.format_sentence(
            resource['Readable sentence'], resource)
        if self.edit_item:
            item = self.edit_item
            item.setText(updated_sentence)
            self.edit_item = None
        else:
            item = QtWidgets.QListWidgetItem(updated_sentence)
        item.resource_full = resource
        self.resources_list.addItem(item)

    def restore_defaults(self):
        """Restore defaults profiles."""
        title = tr('Restore defaults')
        msg = tr(
            'Restoring defaults will overwrite your changes on profiles '
            'provided by InaSAFE. Do you want to continue ?')
        # noinspection PyCallByClass
        reply = QMessageBox.question(
            self,
            title,
            msg,
            QtWidgets.QMessageBox.Yes,
            QtWidgets.QMessageBox.No
        )

        if reply == QtWidgets.QMessageBox.Yes:
            self.profile_combo.clear()
            self.load_profiles(True)
            # Next 2 lines fixes issues #1388 #1389 #1390 #1391
            if self.profile_combo.count() > 0:
                self.select_profile(0)

    def load_profiles(self, overwrite=False):
        """Load the profiles into the dropdown list.

        :param overwrite: If we overwrite existing profiles from the plugin.
        :type overwrite: bool
        """
        for profile in self.minimum_needs.get_profiles(overwrite):
            self.profile_combo.addItem(profile)
        minimum_needs = self.minimum_needs.get_full_needs()
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(minimum_needs['profile']))

    def select_profile(self, index):
        """Select a given profile by index.

        Slot for when profile is selected.

        :param index: The selected item's index
        :type index: int
        """
        new_profile = self.profile_combo.itemText(index)
        self.resources_list.clear()
        self.minimum_needs.load_profile(new_profile)
        self.clear_resource_list()
        self.populate_resource_list()
        self.minimum_needs.save()

    def select_profile_by_name(self, profile_name):
        """Select a given profile by profile name

        :param profile_name: The profile name
        :type profile_name: str
        """
        self.select_profile(self.profile_combo.findText(profile_name))

    def mark_current_profile_as_pending(self):
        """Mark the current profile as pending by colouring the text red.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('red'))

    def mark_current_profile_as_saved(self):
        """Mark the current profile as saved by colouring the text black.
        """
        index = self.profile_combo.currentIndex()
        item = self.profile_combo.model().item(index)
        item.setForeground(QtGui.QColor('black'))

    def add_new_resource(self):
        """Handle add new resource requests.
        """
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text('')
        parameter_widgets[1].set_text('')
        parameter_widgets[2].set_text('')
        parameter_widgets[3].set_text('')
        parameter_widgets[4].set_text('')
        parameter_widgets[5].set_value(10)
        parameter_widgets[6].set_value(0)
        parameter_widgets[7].set_value(100)
        parameter_widgets[8].set_text(tr('weekly'))
        parameter_widgets[9].set_text(tr(
            'A displaced person should be provided with '
            '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of '
            '%(resource name)s. Though no less than %(minimum allowed)s '
            'and no more than %(maximum allowed)s. This should be provided '
            '%(frequency)s.' % {
                'default value': '{{ Default }}',
                'unit': '{{ Unit }}',
                'units': '{{ Units }}',
                'unit abbreviation': '{{ Unit abbreviation }}',
                'resource name': '{{ Resource name }}',
                'minimum allowed': '{{ Minimum allowed }}',
                'maximum allowed': '{{ Maximum allowed }}',
                'frequency': '{{ Frequency }}'
            }))
        self.stacked_widget.setCurrentWidget(self.resource_edit_page)
        # hide the close button
        self.button_box.button(QDialogButtonBox.Close).setHidden(True)

    def edit_resource(self):
        """Handle edit resource requests.
        """
        self.mark_current_profile_as_pending()
        resource = None
        for item in self.resources_list.selectedItems()[:1]:
            resource = item.resource_full
            self.edit_item = item
        if not resource:
            return
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0].widget()
        parameter_widgets = [
            parameters_widget.vertical_layout.itemAt(i).widget() for i in
            range(parameters_widget.vertical_layout.count())]
        parameter_widgets[0].set_text(resource['Resource name'])
        parameter_widgets[1].set_text(resource['Resource description'])
        parameter_widgets[2].set_text(resource['Unit'])
        parameter_widgets[3].set_text(resource['Units'])
        parameter_widgets[4].set_text(resource['Unit abbreviation'])
        parameter_widgets[5].set_value(float(resource['Default']))
        parameter_widgets[6].set_value(float(resource['Minimum allowed']))
        parameter_widgets[7].set_value(float(resource['Maximum allowed']))
        parameter_widgets[8].set_text(resource['Frequency'])
        parameter_widgets[9].set_text(resource['Readable sentence'])
        self.switch_context(self.resource_edit_page)

    def set_up_resource_parameters(self):
        """Set up the resource parameter for the add/edit view.
        """
        name_parameter = StringParameter('UUID-1')
        name_parameter.name = self.resource_parameters['Resource name']
        name_parameter.help_text = tr(
            'Name of the resource that will be provided '
            'as part of minimum needs. '
            'e.g. Rice, Water etc.')
        name_parameter.description = tr(
            'A <b>resource</b> is something that you provide to displaced '
            'persons in the event of a disaster. The resource will be made '
            'available at IDP camps and may need to be stockpiled by '
            'contingency planners in their preparations for a disaster.')
        name_parameter.is_required = True
        name_parameter.value = ''

        description_parameter = StringParameter('UUID-2')
        description_parameter.name = self.resource_parameters[
            'Resource description']
        description_parameter.help_text = tr(
            'Description of the resource that will be provided as part of '
            'minimum needs.')
        description_parameter.description = tr(
            'This gives a detailed description of what the resource is and ')
        description_parameter.is_required = True
        description_parameter.value = ''

        unit_parameter = StringParameter('UUID-3')
        unit_parameter.name = self.resource_parameters['Unit']
        unit_parameter.help_text = tr(
            'Single unit for the resources spelled out. e.g. litre, '
            'kilogram etc.')
        unit_parameter.description = tr(
            'A <b>unit</b> is the basic measurement unit used for computing '
            'the allowance per individual. For example when planning water '
            'rations the unit would be single litre.')
        unit_parameter.is_required = True
        unit_parameter.value = ''

        units_parameter = StringParameter('UUID-4')
        units_parameter.name = self.resource_parameters['Units']
        units_parameter.help_text = tr(
            'Multiple units for the resources spelled out. e.g. litres, '
            'kilogram etc.')
        units_parameter.description = tr(
            '<b>Units</b> are the basic measurement used for computing the '
            'allowance per individual. For example when planning water '
            'rations the units would be litres.')
        units_parameter.is_required = True
        units_parameter.value = ''

        unit_abbreviation_parameter = StringParameter('UUID-5')
        unit_abbreviation_parameter.name = \
            self.resource_parameters['Unit abbreviation']
        unit_abbreviation_parameter.help_text = tr(
            'Abbreviations of unit for the resources. e.g. l, kg etc.')
        unit_abbreviation_parameter.description = tr(
            "A <b>unit abbreviation</b> is the basic measurement unit's "
            "shortened. For example when planning water rations "
            "the units would be l.")
        unit_abbreviation_parameter.is_required = True
        unit_abbreviation_parameter.value = ''

        minimum_parameter = FloatParameter('UUID-6')
        minimum_parameter.name = self.resource_parameters['Minimum allowed']
        minimum_parameter.is_required = True
        minimum_parameter.precision = 6
        minimum_parameter.minimum_allowed_value = 0.0
        minimum_parameter.maximum_allowed_value = 99999.0
        minimum_parameter.help_text = tr(
            'The minimum allowable quantity per person. ')
        minimum_parameter.description = tr(
            'The <b>minimum</b> is the minimum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be less '
            'than 0.5l. This is enforced when tweaking a minimum needs set '
            'before an impact evaluation')
        minimum_parameter.value = 0.00

        maximum_parameter = FloatParameter('UUID-7')
        maximum_parameter.name = self.resource_parameters['Maximum allowed']
        maximum_parameter.is_required = True
        maximum_parameter.precision = 6
        maximum_parameter.minimum_allowed_value = 0.0
        maximum_parameter.maximum_allowed_value = 99999.0
        maximum_parameter.help_text = tr(
            'The maximum allowable quantity per person. ')
        maximum_parameter.description = tr(
            'The <b>maximum</b> is the maximum allowed quantity of the '
            'resource per person. For example you may dictate that the water '
            'ration per person per day should never be allowed to be more '
            'than 67l. This is enforced when tweaking a maximum needs set '
            'before an impact evaluation.')
        maximum_parameter.value = 100.0

        default_parameter = FloatParameter('UUID-8')
        default_parameter.name = self.resource_parameters['Default']
        default_parameter.is_required = True
        default_parameter.precision = 6
        default_parameter.minimum_allowed_value = 0.0
        default_parameter.maximum_allowed_value = 99999.0
        default_parameter.help_text = tr(
            'The default allowable quantity per person. ')
        default_parameter.description = tr(
            "The <b>default</b> is the default allowed quantity of the "
            "resource per person. For example you may indicate that the water "
            "ration per person weekly should be 67l.")
        default_parameter.value = 10.0

        frequency_parameter = StringParameter('UUID-9')
        frequency_parameter.name = self.resource_parameters['Frequency']
        frequency_parameter.help_text = tr(
            "The frequency that this resource needs to be provided to a "
            "displaced person. e.g. weekly, daily, once etc.")
        frequency_parameter.description = tr(
            "The <b>frequency</b> informs the aid worker how regularly this "
            "resource needs to be provided to the displaced person.")
        frequency_parameter.is_required = True
        frequency_parameter.value = tr('weekly')

        sentence_parameter = TextParameter('UUID-10')
        sentence_parameter.name = self.resource_parameters['Readable sentence']
        sentence_parameter.help_text = tr(
            'A readable presentation of the resource.')
        sentence_parameter.description = tr(
            "A <b>readable sentence</b> is a presentation of the resource "
            "that displays all pertinent information. If you are unsure then "
            "use the default. Properties should be included using double "
            "curly brackets '{{' '}}'. Including the resource name would be "
            "achieved by including e.g. {{ Resource name }}")
        sentence_parameter.is_required = True
        sentence_parameter.value = tr(
            'A displaced person should be provided with '
            '%(default value)s %(unit)s/%(units)s/%(unit abbreviation)s of '
            '%(resource name)s. Though no less than %(minimum allowed)s '
            'and no more than %(maximum allowed)s. This should be provided '
            '%(frequency)s.' % {
                'default value': '{{ Default }}',
                'unit': '{{ Unit }}',
                'units': '{{ Units }}',
                'unit abbreviation': '{{ Unit abbreviation }}',
                'resource name': '{{ Resource name }}',
                'minimum allowed': '{{ Minimum allowed }}',
                'maximum allowed': '{{ Maximum allowed }}',
                'frequency': '{{ Frequency }}'
            })

        parameters = [
            name_parameter,
            description_parameter,
            unit_parameter,
            units_parameter,
            unit_abbreviation_parameter,
            default_parameter,
            minimum_parameter,
            maximum_parameter,
            frequency_parameter,
            sentence_parameter
        ]
        parameter_container = ParameterContainer(parameters)
        parameter_container.setup_ui()

        layout = QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(parameter_container)
        self.parameters_scrollarea.setLayout(layout)

    def remove_resource(self):
        """Remove the currently selected resource.
        """
        self.mark_current_profile_as_pending()
        for item in self.resources_list.selectedItems():
            self.resources_list.takeItem(self.resources_list.row(item))

    def discard_changes(self):
        """Discard the changes to the resource add/edit.
        """
        self.edit_item = None
        self.switch_context(self.profile_edit_page)

    def save_resource(self):
        """Accept the add/edit of the current resource.
        """
        # --
        # Hackorama to get this working outside the method that the
        # parameters where defined in.
        parameters_widget = [
            self.parameters_scrollarea.layout().itemAt(i) for i in
            range(self.parameters_scrollarea.layout().count())][0]
        parameters = parameters_widget.widget().get_parameters()

        # To store parameters, we need the english version.
        translated_to_english = dict(
            (y, x) for x, y in list(self.resource_parameters.items()))
        resource = {}
        for parameter in parameters:
            resource[translated_to_english[parameter.name]] = parameter.value

        # verify the parameters are ok - create a throw-away resource param
        try:
            parameter = ResourceParameter()
            parameter.name = resource['Resource name']
            parameter.help_text = resource['Resource description']
            # Adding in the frequency property. This is not in the
            # FloatParameter by default, so maybe we should subclass.
            parameter.frequency = resource['Frequency']
            parameter.description = NeedsProfile.format_sentence(
                resource['Readable sentence'],
                resource)
            parameter.minimum_allowed_value = float(
                resource['Minimum allowed'])
            parameter.maximum_allowed_value = float(
                resource['Maximum allowed'])
            parameter.unit.name = resource['Unit']
            parameter.unit.plural = resource['Units']
            parameter.unit.abbreviation = resource['Unit abbreviation']
            parameter.value = float(resource['Default'])
        except ValueOutOfBounds as e:
            warning = self.tr(
                'Problem - default value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMaximumError as e:
            warning = self.tr(
                'Problem - maximum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        except InvalidMinimumError as e:
            warning = self.tr(
                'Problem - minimum value is invalid') + '\n' + str(e)
            # noinspection PyTypeChecker,PyArgumentList
            QMessageBox.warning(None, 'InaSAFE', warning)
            return
        # end of test for parameter validity

        self.add_resource(resource)
        self.switch_context(self.profile_edit_page)

    def import_profile(self):
        """ Import minimum needs from an existing json file.

        The minimum needs are loaded from a file into the table. This state
        is only saved if the form is accepted.
        """
        # noinspection PyCallByClass,PyTypeChecker
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptOpen)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        path_name = resources_path('minimum_needs')
        file_name_dialog.setDirectory(path_name)
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        else:
            return -1

        if self.minimum_needs.read_from_file(file_name) == -1:
            return -1

        self.clear_resource_list()
        self.populate_resource_list()
        self.switch_context(self.profile_edit_page)

    def export_profile(self):
        """ Export minimum needs to a json file.

        This method will save the current state of the minimum needs setup.
        Then open a dialog allowing the user to browse to the desired
        destination location and allow the user to save the needs as a json
        file.
        """
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptSave)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        file_name = None
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        if file_name != '' and file_name is not None:
            self.minimum_needs.write_to_file(file_name)

    def save_profile(self):
        """ Save the current state of the minimum needs widget.

        The minimum needs widget current state is saved to the QSettings via
        the appropriate QMinimumNeeds class' method.
        """
        minimum_needs = {'resources': []}
        for index in range(self.resources_list.count()):
            item = self.resources_list.item(index)
            minimum_needs['resources'].append(item.resource_full)
        minimum_needs['provenance'] = self.provenance.text()
        minimum_needs['profile'] = self.profile_combo.itemText(
            self.profile_combo.currentIndex()
        )
        self.minimum_needs.update_minimum_needs(minimum_needs)
        self.minimum_needs.save()
        self.minimum_needs.save_profile(minimum_needs['profile'])
        self.mark_current_profile_as_saved()

    def save_profile_as(self):
        """Save the minimum needs under a new profile name.
        """
        # noinspection PyCallByClass,PyTypeChecker
        file_name_dialog = QFileDialog(self)
        file_name_dialog.setAcceptMode(QFileDialog.AcceptSave)
        file_name_dialog.setNameFilter(self.tr('JSON files (*.json *.JSON)'))
        file_name_dialog.setDefaultSuffix('json')
        dir = os.path.join(QgsApplication.qgisSettingsDirPath(),
                           'inasafe', 'minimum_needs')
        file_name_dialog.setDirectory(expanduser(dir))
        if file_name_dialog.exec_():
            file_name = file_name_dialog.selectedFiles()[0]
        else:
            return
        file_name = basename(file_name)
        file_name = file_name.replace('.json', '')
        minimum_needs = {'resources': []}
        self.mark_current_profile_as_saved()
        for index in range(self.resources_list.count()):
            item = self.resources_list.item(index)
            minimum_needs['resources'].append(item.resource_full)
        minimum_needs['provenance'] = self.provenance.text()
        minimum_needs['profile'] = file_name
        self.minimum_needs.update_minimum_needs(minimum_needs)
        self.minimum_needs.save()
        self.minimum_needs.save_profile(file_name)
        if self.profile_combo.findText(file_name) == -1:
            self.profile_combo.addItem(file_name)
        self.profile_combo.setCurrentIndex(
            self.profile_combo.findText(file_name))

    def new_profile(self):
        """Create a new profile by name.
        """
        # noinspection PyCallByClass,PyTypeChecker
        dir = os.path.join(QgsApplication.qgisSettingsDirPath(),
                           'inasafe', 'minimum_needs')
        file_name, __ = QFileDialog.getSaveFileName(
            self,
            self.tr('Create a minimum needs profile'),
            expanduser(dir),
            self.tr('JSON files (*.json *.JSON)'),
            options=QFileDialog.DontUseNativeDialog)
        if not file_name:
            return
        file_name = basename(file_name)
        if self.profile_combo.findText(file_name) == -1:
            minimum_needs = {
                'resources': [], 'provenance': '', 'profile': file_name}
            self.minimum_needs.update_minimum_needs(minimum_needs)
            self.minimum_needs.save_profile(file_name)
            self.profile_combo.addItem(file_name)
            self.clear_resource_list()
            self.profile_combo.setCurrentIndex(
                self.profile_combo.findText(file_name))
        else:
            self.profile_combo.setCurrentIndex(
                self.profile_combo.findText(file_name))
            self.select_profile_by_name(file_name)

    def page_changed(self, index):
        """Slot for when tab changes in the stacked widget changes.

        :param index: Index of the now active tab.
        :type index: int
        """
        if index == 0:  # profile edit page
            for item in self.resource_editing_buttons:
                item.hide()
            for item in self.profile_editing_widgets:
                item.setEnabled(True)
            for item in self.profile_editing_buttons:
                item.show()
        else:  # resource_edit_page
            for item in self.resource_editing_buttons:
                item.show()
            for item in self.profile_editing_widgets:
                item.setEnabled(False)
            for item in self.profile_editing_buttons:
                item.hide()

    def switch_context(self, page):
        """Switch context tabs by tab widget name.

        :param page: The page should be focussed.
        :type page: QWidget
        """
        # noinspection PyUnresolvedReferences
        if page.objectName() == 'profile_edit_page':
            self.stacked_widget.setCurrentIndex(0)
            self.button_box.button(QDialogButtonBox.Close).setHidden(False)
        else:  # resource_edit_page
            self.stacked_widget.setCurrentIndex(1)
            # hide the close button
            self.button_box.button(QDialogButtonBox.Close).setHidden(True)

    def remove_profile(self):
        """Remove the current profile.

        Make sure the user is sure.
        """
        profile_name = self.profile_combo.currentText()
        # noinspection PyTypeChecker
        button_selected = QMessageBox.warning(
            None,
            'Remove Profile',
            self.tr('Remove %s.') % profile_name,
            QMessageBox.Ok,
            QMessageBox.Cancel
        )
        if button_selected == QMessageBox.Ok:
            self.profile_combo.removeItem(
                self.profile_combo.currentIndex()
            )
            self.minimum_needs.remove_profile(profile_name)
            self.select_profile(self.profile_combo.currentIndex())

    @pyqtSlot(bool)  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        .. versionadded: 3.2.1

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user.

        .. versionadded: 3.2.1
        """
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = needs_manager_helps()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)