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 _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 __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)
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