Пример #1
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    paragraph = m.Paragraph(m.Image('file:///%s/img/screenshots/'
                                    'batch-calculator-screenshot.png' %
                                    resources_path()),
                            style_class='text-center')
    message.add(paragraph)
    message.add(
        m.Paragraph(
            tr('Depending on which Impact Function you have chosen you have '
               'different options available for adjusting the parameters of the '
               'question you are asking. Some Impact Functions have more '
               'configurable Options than others. To open the Impact Function '
               'Configuration Dialog you need to click on the "Options ..." '
               'button next to the selected impact function paragraph in the '
               'InaSAFE dock. You can have up to 3 tabs visible:')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Options')),
            tr('- Depending in the Impact function you selected, you can '
               'influence the result of your question here (the Impact Function) '
               'by setting different values to the defaults that will be loaded. '
               'The options available will depend on the Impact Function you '
               'choose (some Impact Functions do not allow users to change the '
               'default parameters).')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Post-processors')),
            tr('- Takes the results from the Impact Function and calculates '
               'derivative indicators, for example if you have an affected '
               'population total, the Gender postprocessor will calculate gender '
               'specific indicators such as additional nutritional requirements '
               'for pregnant women.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Minimum Needs')),
            tr('- If the analysis uses population exposure, InaSAFE calculates '
               'the minimum needs of the people affected by the impact scenario. '
               'You should refer to the minimum needs tool for configuring the '
               'global defaults used in these calculations. '),
            m.Image(
                'file:///%s/img/icons/'
                'show-minimum-needs.svg' % resources_path(),
                **SMALL_ICON_STYLE),
            tr(' This panel will let you override global defaults for a specific '
               'analysis run.')))
    message.add(bullets)
    return message
Пример #2
0
    def emit_pre_run_message(self):
        """Inform the user about parameters before starting the processing."""
        title = tr('Processing started')
        details = tr(
            'Please wait - processing may take a while depending on your '
            'hardware configuration and the analysis extents and data.')
        # trap for issue 706
        try:
            exposure_name = self.exposure.name
            hazard_name = self.hazard.name
            # aggregation layer could be set to AOI so no check for that
        except AttributeError:
            title = tr('No valid layers')
            details = tr(
                'Please ensure your hazard and exposure layers are set '
                'in the question area and then press run again.')
            message = m.Message(LOGO_ELEMENT,
                                m.Heading(title, **WARNING_STYLE),
                                m.Paragraph(details))
            raise NoValidLayerError(message)
        text = m.Text(
            tr('This analysis will calculate the impact of'),
            m.EmphasizedText(hazard_name),
            tr('on'),
            m.EmphasizedText(exposure_name),
        )
        if self.aggregation is not None:
            try:
                aggregation_name = self.aggregation.name
                # noinspection PyTypeChecker
                text.add(
                    m.Text(tr('and bullet list the results'),
                           m.ImportantText(tr('aggregated by')),
                           m.EmphasizedText(aggregation_name)))
            except AttributeError:
                pass
        text.add('.')
        message = m.Message(LOGO_ELEMENT,
                            m.Heading(title, **PROGRESS_UPDATE_STYLE),
                            m.Paragraph(details), m.Paragraph(text))
        try:
            # add which postprocessors will run when appropriated
            # noinspection PyTypeChecker
            post_processors_names = self.parameters['postprocessors']
            post_processors = get_postprocessors(post_processors_names)
            message.add(
                m.Paragraph(tr('The following postprocessors will be used:')))

            bullet_list = m.BulletedList()

            for name, post_processor in post_processors.iteritems():
                bullet_list.add('%s: %s' % (get_postprocessor_human_name(name),
                                            post_processor.description()))
            message.add(bullet_list)

        except (TypeError, KeyError):
            # TypeError is for when function_parameters is none
            # KeyError is for when ['postprocessors'] is unavailable
            pass
        send_static_message(self, message)
Пример #3
0
def generate_expression_help(description, examples):
    """Generate the help message for QGIS Expressions.

    It will format nicely the help string with some examples.

    :param description: A description of the expression.
    :type description: basestring

    :param examples: A dictionnary of examples
    :type examples: dict

    :return: A message object.
    :rtype: message
    """
    help = m.Message()
    help.add(m.Paragraph(description))
    help.add(m.Paragraph(tr('Examples:')))
    bullets = m.BulletedList()
    for expression, result in examples.iteritems():
        if result:
            bullets.add(
                m.Text(m.ImportantText(expression), m.Text('→'),
                       m.Text(result)))
        else:
            bullets.add(m.Text(m.ImportantText(expression)))
    help.add(bullets)
    return help
Пример #4
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()

    message.add(m.Paragraph(tr(
        'To start report generation you need to click on the Print... '
        'button in the buttons area. This will open the Impact report '
        'dialog which has three main areas.')))

    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('Area to print')),
        tr(
            ' - There are two options available. Choose Current extent if '
            'current canvas extent represents necessary area. Analysis '
            'extent will set extent of the report map to impact layer '
            'extent.'
           )))
    bullets.add(m.Text(
        m.ImportantText(tr('Template to use')),
        tr(
            ' - Here you can select desired template for your report. All '
            'templates bundled with InaSAFE are available here, plus '
            'templates from user-defined template directory (see Options '
            'for information how to set templates directory). It is also '
            'possible to select custom template from any location: just '
            'activate radiobutton under combobox and provide path to template '
            'using the "..." button.'
           )))
    bullets.add(m.Text(
        m.ImportantText(tr('Buttons area')),
        tr(
            ' - In this area you will find buttons to open the report as '
            'a PDF or in the QGIS print composer. You can also get help by '
            'clicking on the help button or using the close button to close '
            'the print dialog.'
        )))
    message.add(bullets)

    return message
Пример #5
0
def get_error_message(exception, context=None, suggestion=None):
    """Convert exception into an ErrorMessage containing a stack trace.

    :param exception: Exception object.
    :type exception: Exception

    :param context: Optional context message.
    :type context: str

    :param suggestion: Optional suggestion.
    :type suggestion: str

    .. see also:: https://github.com/inasafe/inasafe/issues/577

    :returns: An error message with stack trace info suitable for display.
    :rtype: ErrorMessage
    """

    name, trace = humanise_exception(exception)

    problem = m.Message(name)

    if exception is None or exception == '':
        problem.append = m.Text(tr('No details provided'))
    else:
        if hasattr(exception, 'message') and \
                isinstance(exception.message, Message):
            problem.append = m.Text(str(exception.message.message))
        else:
            problem.append = m.Text(str(exception))

    suggestion = suggestion
    if suggestion is None and hasattr(exception, 'suggestion'):
        suggestion = exception.suggestion

    error_message = ErrorMessage(
        problem,
        detail=context,
        suggestion=suggestion,
        traceback=trace
    )

    args = exception.args
    for arg in args:
        error_message.details.append(arg)

    return error_message
Пример #6
0
def get_error_message(exception, context=None, suggestion=None):
    """Convert exception into an ErrorMessage containing a stack trace.


    :param exception: Exception object.
    :type exception: Exception

    :param context: Optional context message.
    :type context: str

    :param suggestion: Optional suggestion.
    :type suggestion: str

    .. see also:: https://github.com/AIFDR/inasafe/issues/577

    :returns: An error message with stack trace info suitable for display.
    :rtype: ErrorMessage
    """

    trace = ''.join(traceback.format_tb(sys.exc_info()[2]))

    problem = m.Message(m.Text(exception.__class__.__name__))

    if exception is None or exception == '':
        problem.append = m.Text(tr('No details provided'))
    else:
        if isinstance(exception.message, Message):
            problem.append = m.Text(str(exception.message.message))
        else:
            problem.append = m.Text(exception.message)

    suggestion = suggestion
    if suggestion is None and hasattr(exception, 'suggestion'):
        suggestion = exception.message

    error_message = ErrorMessage(
        problem,
        detail=context,
        suggestion=suggestion,
        traceback=trace
    )

    args = exception.args
    for arg in args:
        error_message.details.append(arg)

    return error_message
Пример #7
0
    def print_map_to_pdf(self, impact_report):
        """Print map to PDF given MapReport instance.

        :param impact_report: Impact Report instance that is ready to print
        :type impact_report: ImpactReport
        """
        impact_report.setup_composition()

        # Get Filename
        map_title = impact_report.map_title
        if map_title is not None:
            default_file_name = map_title + '.pdf'
            default_file_name = default_file_name.replace(' ', '_')
        else:
            self.show_error_message(self.tr('Keyword "map_title" not found.'))
            return

        # Get output path
        # noinspection PyCallByClass,PyTypeChecker
        output_path = QtGui.QFileDialog.getSaveFileName(
            self.parent, self.tr('Write to PDF'),
            os.path.join(temp_dir(), default_file_name),
            self.tr('Pdf File (*.pdf)'))
        output_path = str(output_path)

        if output_path is None or output_path == '':
            self.show_dynamic_message(
                self,
                m.Message(m.Heading(self.tr('Map Creator'), **WARNING_STYLE),
                          m.Text(self.tr('Printing cancelled!'))))
            return

        try:
            map_pdf_path, table_pdf_path = impact_report.print_to_pdf(
                output_path)

            # Make sure the file paths can wrap nicely:
            wrapped_map_path = map_pdf_path.replace(os.sep, '<wbr>' + os.sep)
            wrapped_table_path = table_pdf_path.replace(
                os.sep, '<wbr>' + os.sep)
            status = m.Message(
                m.Heading(self.tr('Map Creator'), **INFO_STYLE),
                m.Paragraph(
                    self.
                    tr('Your PDF was created....opening using the default PDF '
                       'viewer on your system. The generated pdfs were saved '
                       'as:')), m.Paragraph(wrapped_map_path),
                m.Paragraph(self.tr('and')), m.Paragraph(wrapped_table_path))

            # noinspection PyCallByClass,PyTypeChecker,PyTypeChecker
            QtGui.QDesktopServices.openUrl(
                QtCore.QUrl.fromLocalFile(table_pdf_path))
            # noinspection PyCallByClass,PyTypeChecker,PyTypeChecker
            QtGui.QDesktopServices.openUrl(
                QtCore.QUrl.fromLocalFile(map_pdf_path))

            self.show_dynamic_message(self, status)
        except TemplateLoadingError, e:
            self.show_error_message(get_error_message(e))
Пример #8
0
    def help_content(self):
        """Return the content of help for this step wizard.

            We only needs to re-implement this method in each wizard step.

        :returns: A message object contains help.
        :rtype: m.Message
        """
        help_message = m.Message()
        help_message.add(m.Text(tr('No help text for this wizard step, yet.')))
        return help_message
Пример #9
0
    def populate_bullet_list(message, information):
        """Populate a message object with bullet list.

        :param message: The message object.
        :type message: Message

        :param information: A dictionary of information.
        :type information: dict

        :return: A message object that has been updated.
        :rtype: Message
        """
        bullets = m.BulletedList()
        for key, item in information.iteritems():
            if item:
                bullets.add(
                    m.Text(m.ImportantText(key), m.Text('→'), m.Text(item)))
            else:
                bullets.add(m.Text(m.ImportantText(key)))
        message.add(bullets)
        return message
Пример #10
0
def _add_dict_to_bullets(target, value):
    """Add notes and actions in dictionary to the bullets

    :param target: Target bullets
    :type target: Bullets

    :param value: Dictionary that contains actions or notes
    :type value: dict

    :return: Updated bullets
    :rtype: Bullets
    """
    actions = value.get('action_list')
    notes = value.get('item_list')
    items_tobe_added = []
    if actions:
        items_tobe_added = actions
    elif notes:
        items_tobe_added = notes

    if items_tobe_added:
        for item in items_tobe_added:
            target.add(m.Text(item))
    return target
Пример #11
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    message.add(
        tr('The InaSAFE options dialog is used to control various aspects of '
           'the InaSAFE analysis and reporting environment. Here are brief '
           'descriptions of all the options available, grouped by the tab '
           'page on which they occur.'))

    header = m.Heading(tr('Basic options tab'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('The basic options tab provides several general settings:')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Show organisation logo in InaSAFE dock')),
            tr(' - When this option is enabled, a logo will be displayed at the '
               'bottom of the InaSAFE dock widget. By default the logo used '
               'is the InaSAFE supporters logo, but you can alter this by '
               'setting the \'Use custom organisation logo\' option in '
               'the template options tab (see below).')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Show only visible layers in the InaSAFE dock')),
            tr(' - When this option is enabled layers that are not visible '
               'in the QGIS layers panel will not be shown in the hazard, '
               'exposure and aggregation combo boxes in the dock area.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Set QGIS layer name from title in keywords')),
            tr(' - If this option is enabled, the InaSAFE keywords title '
               'attribute will be used for the layer name in the QGIS Layers list '
               'when adding a layer.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Zoom to impact layer on scenario estimate completion')),
            tr(' - When this option is enabled, the map view extents will '
               'be updated to match the extents of the generated impact layer '
               'once the analysis completes.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Hide exposure on scenario estimate completion')),
            tr(' - Use this option if you prefer to not show the exposure '
               'layer as an underlay behind the generated impact layer.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('When clipping to analysis extents, also clip features')),
            tr(' - This option only applies in cases where hazard or exposure '
               'layers are vector layers. In these cases, any feature (line or '
               'polygon) that extends beyond the analysis extents will first '
               'be clipped so that it is coincident with the analysis extent. '
               'Note that enabling this option may have some unwanted side '
               'effects. For example, if you have an area attribute, that '
               'attribute may no longer match the actual clipped polygon area. '
               )))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Show intermediate layers generated by post processing')),
            tr(' - When enabled, the working layer used for doing by-area '
               'breakdowns of impact results will be added to the current QGIS '
               'project. You can generally leave this option disabled.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Female ratio default value')),
            tr(' - When doing an analysis that uses population as the '
               'exposure layer, various post-processors are used to produce a '
               'demographic breakdown. In the case of the gender breakdown, '
               'InaSAFE will report on how many males versus females are present '
               'in each aggregation area. If there is no female ratio attribute '
               'defined in the aggregation layer, the value in this setting will '
               'be used to determine what the ratio between males to females '
               'is.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Location for results')),
            tr(' - By default, InaSAFE will write impact layer and intermediate '
               'outputs to the system temporary directory. On some operating '
               'systems, these temporary files will be deleted on each reboot. '
               'If you wish to, you can specify an alternative directory '
               'to use for storing these temporary files.')))
    message.add(bullets)

    header = m.Heading(tr('Template options tab'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('This tab has options relating to the printing of reports and the '
               'generation of map composer templates.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Prompt me when template has missing elements')),
            tr(' - You can define your own templates in InaSAFE. In some cases '
               'critical elements on the template may be ommitted during this '
               'template definition process. Should this happen, InaSAFE can '
               'warn you when you try to use the template that it is missing '
               'vital map components.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom organisation logo')),
            tr(' - By default, InaSAFE will add the supporters logo to each '
               'map template. The supporters logo is also used at tbe bottom '
               'of the dock panel if the \'show organisation logo in dock\' '
               'option is enabled. You can use this option to replace the '
               'organisation logo with that of your own organisation. The logo '
               'will be rescaled automatically to fill the space provided.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom north arrow image')),
            tr(' - InaSAFE provides a basic north arrow which is placed on '
               'generated map compositions and rendered PDF reports. You can '
               'replace this north arrow with one of your own choosing using '
               'this option.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Additional template directory')),
            tr(' - When generating a print report, InaSAFE will offer a number '
               'of pre-defined templates for you to use. For example there '
               'is an A4 variant, an A3 variant and so on. You can use this '
               'option to specify additional search directories to be used '
               'when presenting a list of available templates. This is useful '
               'in cases where you have created your own custom map templates '
               'and you wish to use them for report production.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom disclaimer text')),
            tr(' - By default, InaSAFE will display a disclaimer on reports '
               'advising readers of the report to exercise caution when '
               'interpreting the outputs presented. You can override this '
               'text using this option, though we do advise that you include '
               'a similar statement of caution in your overridden text.')))
    message.add(bullets)

    header = m.Heading(tr('ISO 19115 metadata tab'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('This tab is used to define various options related to the '
               'automated generation of ISO 19115 metadata which is associated with '
               'hazard, exposure, aggregation and impact layers.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Organisation')),
            tr(' - Use this option to specify the name of your organisation.'))
    )
    bullets.add(
        m.Text(
            m.ImportantText(tr('Contact email')),
            tr(' - Use this option to specify the contact person\s email '
               'address to use in the generated metadata document.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Website')),
            tr(' - Use this option to set the website address to be used in '
               'the generated metadata document.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Analysis title')),
            tr(' - Use this to set the title value for the generated metadata '
               'document.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Analysis license')),
            tr(' - Use this to set the usage and redistribution license for the '
               'generated impact layer.')))
    message.add(bullets)

    header = m.Heading(tr('Advanced tab'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('This tab contains options intended for advanced users only.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Keyword cache for remote databases')),
            tr(' - When InaSAFE is used with remote layers (for example a '
               'database layer or a WFS layer), it is not possible to store the '
               'keywords for the layer with the layer itself. To accommodate for '
               'these types of layers, InaSAFE writes the keywords to a small '
               'file based database (using sqlite) and then retrieves them based '
               'on unique connection details used for that layer. You can '
               'specify a custom path to be used for storing the keywords '
               'database using this option.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Help to improve InaSAFE by submitting errors to a '
                   'remote server')),
            tr(' - With this option enabled, InaSAFE will post any errors that '
               'occur to an online server for analysis by our development team. '
               'This option is disabled by default as some may consider some of '
               'the data submitted (IP Address, logged in user name) to be '
               'sensitive.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Enable developer mode')),
            tr(' - When this option is enabled, right clicking on the webview '
               'widget in the dock will allow you to debug the generated HTML. '
               'In addition, if the metadata.txt for the running InaSAFE is '
               'set to \'alpha\', an additional icon will be added to the '
               'toolbar to add test layers to the QGIS project.'),
            m.Image(
                'file:///%s/img/icons/'
                'add-test-layers.svg' % resources_path(), **SMALL_ICON_STYLE)))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use QGIS zonal statistics')),
            tr(' - Some versions of QGIS shipped with a buggy zonal statistics '
               'algorithm. With this option you can elect whether to use '
               'built in zonal statistics functions from QGIS or to use '
               'an implementation of zonal statistics included with InaSAFE. '
               'At some point in the future we expect to deprecate this feature '
               'but for now we suggest to use the InaSAFE implementation.')))
    message.add(bullets)

    return message
Пример #12
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    paragraph = m.Paragraph(tr(
        'This document describes the usage of the InaSAFE \'dock panel\''
        '- which is an interface for running risk scenarios within the QGIS '
        'environment. If you are a new user, you may also consider using the '
        '\'Impact Function Centric Wizard\' to run the analysis. You can '
        'launch the wizard by clicking on this icon in the toolbar:'),
        m.Image(
            'file:///%s/img/icons/'
            'show-wizard.svg' % resources_path(),
            **SMALL_ICON_STYLE),

    )
    message.add(paragraph)
    paragraph = m.Paragraph(tr(
        'You can drag and drop the dock panel to reposition it in the user '
        'interface. For example, dragging the panel towards the right margin '
        'of the QGIS application will dock it to the right side of the screen.'
    ))
    message.add(paragraph)

    message.add(m.Paragraph(tr(
        'There are three main areas to the dock panel:')))
    bullets = m.BulletedList()
    bullets.add(tr('the Questions area'))
    bullets.add(tr('the Results area'))
    bullets.add(tr('the Buttons area'))
    message.add(bullets)
    message.add(m.Paragraph(tr(
        'At any time you can obtain help in InaSAFE by clicking on the '
        'help buttons provided on each dock and dialog.')))

    header = m.Heading(tr('The questions area'), **INFO_STYLE)
    message.add(header)
    message.add(m.Paragraph(tr(
        'The intention of InaSAFE is to make it really simple and easy to '
        'perform your impact analysis. The question area provides a simple '
        'way for you to formulate what it is you want to find out? All '
        'questions are formulated in the form:'),
        m.EmphasizedText(tr(
            'In the event of a [hazard], how many [exposure] might be '
            'affected?'))))
    message.add(m.Paragraph(tr(
        'For example: "If there is a flood, how many buildings might be '
        'affected?"')))
    message.add(m.Paragraph(tr(
        'In order to answer such questions, the InaSAFE developers have '
        'built a number of Impact Functions that cover scenarios such as '
        'flood, tsunami, volcanic fall, earthquake and so on.')))
    message.add(m.Paragraph(tr(
        'The formulation of these questions if carried out by loading layers '
        'into QGIS that represent either hazard scenarios or exposure data.'
        'A hazard, for example, may be represented as, a raster layer in '
        'QGIS where each pixel in the raster represents the current flood '
        'depth following an inundation event. An exposure layer could be '
        'represented, for example, as vector polygon data representing '
        'building outlines, or a raster outline where each pixel represents '
        'the number of people thought to be resident in that cell.')))
    message.add(m.Paragraph(tr(
        'The impact function will combine these two input layers in a '
        'mathematical model in order to derive what the impacts of the '
        'hazard will be on the exposure infrastructure or people. By '
        'selecting a combination from the hazard and exposure combo boxes, '
        'an appropriate set of impact functions will be listed in the '
        'combo box. You may be wondering how the InaSAFE plugin determines '
        'whether a layer should be listed in the hazard or exposure combo '
        'boxes? The plugin relies on simple keyword metadata to be associated '
        'with each layer. You can define these keywords by selecting a layer '
        'and then clicking the InaSAFE Keywords Wizard icon on the toolbar: '),
        m.Image(
            'file:///%s/img/icons/'
            'show-keyword-wizard.svg' % resources_path(),
            **SMALL_ICON_STYLE),
        tr(
            'The wizard will guide you through the process of defining the '
            'keywords for that layer.')))
    message.add(m.Paragraph(tr(
        'Based on the combination of hazard and exposure layers that are '
        'selected, the Impact Function list (shown in the combo box under '
        '"Might" in the InaSAFE dock panel)  will be updated. Each impact '
        'function can only work with specific combinations of hazard and '
        'exposure types, so the options shown here will be limited '
        'accordingly. The chosen impact function can be configured (if '
        'applicable) by pressing the small ellipses (...) button next to '
        'the chosen impact function. This is explained in more detail below '
        'under the heading "Setting Analysis Parameters".')))
    message.add(m.Paragraph(tr(
        'Aggregation is the process whereby we group the results of the '
        'analysis by district so that you can see how many people, roads or '
        'buildings were affected in each area. This will help you to '
        'understand where the most critical needs are. Aggregation is '
        'optional in InaSAFE - if you do not use aggregation, the entire '
        'analysis area will be used for the data summaries. Typically '
        'aggregation layers in InaSAFE have as attributes the name of the '
        'district or reporting area. It is also possible to use extended '
        'attributes to indicate the ratio of men and women; youth, adults '
        'and elderly living in each area. Where these are provided and the '
        'exposure layer is population, InaSAFE will provide a demographic '
        'breakdown per aggregation area indicating how many men, women etc. '
        'were probably affected in that area.'
    )))

    header = m.Heading(tr('The results area'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'After running an analysis, the question area is hidden to maximise '
        'the amount of space allocated to the results area. You can '
        're-open the question area at any time by pressing the "show question '
        'form" button.')))

    message.add(m.Paragraph(tr(
        'The Results area is used to display various useful feedback items to '
        'the user. Once an impact scenario has been run, a summary table will '
        'be shown.')))

    message.add(m.Paragraph(tr(
        'If you select an impact layer (i.e. a layer that was produced using '
        'an InaSAFE impact function), in the QGIS layers list, this summary '
        'will also be displayed in the results area. When you select a hazard '
        'or exposure layer in the QGIS layers list, the keywords for that '
        'layer will be shown in the Results area, making it easy to '
        'understand what metadata exists for that layer.')))

    message.add(m.Paragraph(tr(
        'The Results area is also used to display status information. For '
        'example, when a suitable combination of hazard, exposure and impact '
        'function are selected, the results area will be updated to indicate '
        'that you can proceed to run the impact scenario calculation. The Run '
        'Button will be activated.'
    )))

    message.add(m.Paragraph(tr(
        'Finally, the Results area is also used to display any error messages '
        'so that the user is informed as to what went wrong and why. You '
        'might want to scroll down a bit in the messaging window to view the '
        'message completely.'
    )))

    message.add(m.Paragraph(tr(
        'To have more space for the results available your Question is '
        'automatically hidden to make the results area as large as possible '
        'to display the results. If you want to have a look again what the '
        'question was that you formulated click on the Show question form '
        'button on top of the result area.'
    )))

    message.add(m.Paragraph(tr(
        'If you want to hide the question area again to have more space to '
        'display the results again, just make the Layer you just calculated '
        'with InaSAFE active again in the Layers list of QGIS.'
    )))

    header = m.Heading(tr('The Buttons Area'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'The buttons area contains four buttons:')))
    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('Help')),
        tr(
            '- click on this if you need context help, such as the document '
            'you are reading right now!')))
    bullets.add(m.Text(
        m.ImportantText(tr('About')),
        tr(
            '- click on this to see short credits for the InaSAFE project.')))
    bullets.add(m.Text(
        m.ImportantText(tr('Help')),
        tr(
            'Print... - click on this if you wish to create a pdf of your '
            'impact scenarion project or just generate report and open it in '
            'composer for further tuning. An impact layer must be active '
            'before the Print button will be enabled.')))
    bullets.add(m.Text(
        m.ImportantText(tr('Run')),
        tr(
            '- Run - if the combination of options in the Questions area\'s '
            'combo boxes will allow you to run a scenario, this button is '
            'enabled.')))
    message.add(bullets)

    header = m.Heading(tr('Data conversions'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'When running a scenario, the data being used needs to be processed '
        'into a state where it is acceptable for use by the impact function. '
        'In particular it should be noted that:')))

    bullets = m.BulletedList()
    bullets.add(tr(
        'Remote datasets will be copied locally before processing.'))
    bullets.add(m.Text(
        tr(
            'All datasets will be clipped to the intersection of the hazard '
            'layer, exposure layer and the current view extents unless '
            'you have specified a different clipping behaviour in the '
            'extents selector dialog.'),
        m.Image(
            'file:///%s/img/icons/'
            'set-extents-tool.svg' % resources_path(),
            **SMALL_ICON_STYLE)
    ))
    bullets.add(tr(
        'All clipped datasets will be converted (reprojected) to Geographic '
        '(EPSG:4326) coordinate reference system before analysis.'))
    message.add(bullets)

    header = m.Heading(tr('Analysis parameters'), **INFO_STYLE)
    message.add(header)
    # this adds the help content from the IF options dialog
    message.add(options())

    header = m.Heading(tr('Generating impact reports'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'When the scenario analysis completed you may want to generate a '
        'report. Usually the "Print..."  button will be enabled immediately '
        'after analysis. Selecting an InaSAFE impact layer in QGIS Layers '
        'panel will also enable it.'
    )))

    # This adds the help content of the print dialog
    message.add(report())
    return message
Пример #13
0
    def print_map(self, mode="pdf"):
        """Open impact report dialog that used to tune report when print map
            button pressed."""
        # Check if selected layer is valid
        impact_layer = self.iface.activeLayer()
        if impact_layer is None:
            # noinspection PyCallByClass,PyTypeChecker
            QtGui.QMessageBox.warning(
                self.parent, self.tr('InaSAFE'),
                self.tr('Please select a valid impact layer before '
                        'trying to print.'))
            return

        # Open Impact Report Dialog
        print_dialog = ImpactReportDialog(self.iface)
        print_dialog.button_ok = QtGui.QPushButton(self.tr('OK'))
        print_dialog.buttonBox.addButton(print_dialog.button_ok,
                                         QtGui.QDialogButtonBox.ActionRole)

        print_dialog.button_ok.clicked.connect(print_dialog.accept)

        print_dialog.button_save_pdf.hide()
        print_dialog.button_open_composer.hide()

        if not print_dialog.exec_() == QtGui.QDialog.Accepted:
            self.show_dynamic_message(
                self,
                m.Message(m.Heading(self.tr('Map Creator'), **WARNING_STYLE),
                          m.Text(self.tr('Report generation cancelled!'))))
            return

        # Get the extent of the map for report
        use_full_extent = print_dialog.analysis_extent_radio.isChecked()
        if use_full_extent:
            map_crs = self.iface.mapCanvas().mapRenderer().destinationCrs()
            layer_crs = self.iface.activeLayer().crs()
            layer_extent = self.iface.activeLayer().extent()
            if map_crs != layer_crs:
                # noinspection PyCallingNonCallable
                transform = QgsCoordinateTransform(layer_crs, map_crs)
                layer_extent = transform.transformBoundingBox(layer_extent)
            area_extent = layer_extent
        else:
            area_extent = self.iface.mapCanvas().extent()

        # Get selected template path to use
        if print_dialog.default_template_radio.isChecked():
            template_path = print_dialog.template_combo.itemData(
                print_dialog.template_combo.currentIndex())
        else:
            template_path = print_dialog.template_path.text()
            if not os.path.exists(template_path):
                # noinspection PyCallByClass,PyTypeChecker
                QtGui.QMessageBox.warning(
                    self.parent, self.tr('InaSAFE'),
                    self.tr('Please select a valid template before printing. '
                            'The template you choose does not exist.'))
                return

        # Open in PDF or Open in Composer Flag (not used, the button is hidden
        #  by this class. The wizard has two its own buttons for Open In PDF
        # and Open In Composer buttons. Use variable 'mode' from param instead)
        create_pdf_flag = print_dialog.create_pdf

        # Instantiate and prepare Report
        self.show_dynamic_message(
            self,
            m.Message(
                m.Heading(self.tr('Map Creator'), **PROGRESS_UPDATE_STYLE),
                m.Text(self.tr('Preparing map and report'))))

        impact_report = ImpactReport(self.iface, template_path, impact_layer)
        impact_report.extent = area_extent

        # Get other setting
        settings = QSettings()
        logo_path = settings.value('inasafe/organisation_logo_path',
                                   '',
                                   type=str)
        impact_report.organisation_logo = logo_path

        disclaimer_text = settings.value('inasafe/reportDisclaimer',
                                         '',
                                         type=str)
        impact_report.disclaimer = disclaimer_text

        north_arrow_path = settings.value('inasafe/north_arrow_path',
                                          '',
                                          type=str)
        impact_report.north_arrow = north_arrow_path

        template_warning_verbose = bool(
            settings.value('inasafe/template_warning_verbose', True,
                           type=bool))

        # Check if there's missing elements needed in the template
        component_ids = [
            'safe-logo', 'north-arrow', 'organisation-logo', 'impact-map',
            'impact-legend'
        ]
        impact_report.component_ids = component_ids
        if template_warning_verbose and \
                        len(impact_report.missing_elements) != 0:
            title = self.tr('Template is missing some elements')
            question = self.tr(
                'The composer template you are printing to is missing '
                'these elements: %s. Do you still want to continue') % (
                    ', '.join(impact_report.missing_elements))
            # noinspection PyCallByClass,PyTypeChecker
            answer = QtGui.QMessageBox.question(
                self.parent, title, question,
                QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
            if answer == QtGui.QMessageBox.No:
                return

        create_pdf_flag = bool(mode == 'pdf')
        self.show_busy()
        if create_pdf_flag:
            self.print_map_to_pdf(impact_report)
        else:
            self.open_map_in_composer(impact_report)

        self.hide_busy()
Пример #14
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()

    message.add(
        m.Paragraph(
            tr('To start report generation you need to click on the Print '
               'button in the buttons area. This will open the Impact report '
               'dialog which has three main areas.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('InaSAFE reports')),
            tr(' - There are four checkboxes available which are representing '
               'the type of report component that will be generated.')))
    text = tr(
        ' - Here you can select desired template for your report. All '
        'templates bundled with InaSAFE are available here, plus '
        'templates from user-defined template directory (see Options '
        'for information how to set templates directory) and from qgis '
        'setting directory ({qgis_directory}). It is also '
        'possible to select custom template from any location: just '
        'activate radiobutton under combobox and provide path to template '
        'using the "..." button.')
    qgis_directory = join(QgsApplication.qgisSettingsDirPath(), 'inasafe')
    bullets.add(
        m.Text(m.ImportantText(tr('Map reports')),
               text.format(qgis_directory=qgis_directory)))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Buttons area')),
            tr(' - In this area you will find buttons to open the report as '
               'a PDF or in the QGIS print composer. You can also get help by '
               'clicking on the help button or using the close button to close '
               'the print dialog.')))
    message.add(bullets)

    message.add(
        m.Paragraph(
            tr('There are four options on which template would you use to generate '
               'a map report.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('InaSAFE default templates')),
            tr(' - The map report will be generated using InaSAFE default '
               'landscape and portrait map templates. Override template will '
               'not be used.')))

    bullets.add(
        m.Text(
            m.ImportantText(tr('Override template')),
            tr(' - The map report will be generated using override template '
               'found from qgis setting directory. InaSAFE default map templates '
               'will not be printed.')))

    bullets.add(
        m.Text(
            m.ImportantText(tr('Template from search directory')),
            tr(' - The map report will be generated using selected template on '
               'template dropdown selector. InaSAFE default map templates will '
               'not be printed and override template will not be used.')))

    bullets.add(
        m.Text(
            m.ImportantText(tr('Template from file system')),
            tr(' - The map report will be generated using selected template on '
               'file system. InaSAFE default map templates will not be printed '
               'and override template will not be used.')))

    message.add(bullets)

    return message
Пример #15
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    message.add(
        m.Paragraph(
            tr('The InaSAFE options dialog is used to control various aspects of '
               'the InaSAFE analysis and reporting environment. Here are brief '
               'descriptions of all the options available, grouped by the tab '
               'page on which they occur.')))

    header = m.Heading(tr('Organisation Profile tab'), **INFO_STYLE)
    message.add(header)

    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'inasafe-options-organisation-screenshot.png' % resources_path()),
                            style_class='text-center')

    message.add(paragraph)
    message.add(
        m.Paragraph(
            tr('The Organisation Profile tab provides several general settings:'
               )))
    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Organisation')),
            tr(' - Use this option to specify the name of your organisation.'))
    )
    bullets.add(
        m.Text(
            m.ImportantText(tr('Contact email')),
            tr(' - Use this option to specify the contact person\'s email '
               'address to use in the generated metadata document.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Website')),
            tr(' - Use this option to set the website address to be used in '
               'the generated metadata document.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom organisation logo')),
            tr(' - By default, InaSAFE will add the supporters logo to each '
               'map template. The supporters logo is also used at tbe bottom '
               'of the dock panel if the \'show organisation logo in dock\' '
               'option is enabled. You can use this option to replace the '
               'organisation logo with that of your own organisation. The logo '
               'will be rescaled automatically to fill the space provided.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Currency')),
            tr(' - InaSAFE will use the selected currency for the analysis.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Analysis license')),
            tr(' - Use this to set the usage and redistribution license for the '
               'generated impact layer.')))
    message.add(bullets)

    header = m.Heading(tr('Population Parameters tab'), **INFO_STYLE)
    message.add(header)

    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'inasafe-options-population-screenshot.png' % resources_path()),
                            style_class='text-center')
    message.add(paragraph)

    message.add(
        m.Paragraph(
            tr('In this tab you can define some parameters that will be used by '
               'InaSAFE in the analysis of exposed populations. You have the option '
               'to change the parameters for whether the exposed population is '
               'considered to be affected by each hazard type and class, and the '
               'displacement rate that will be used for affected people.')))

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Affected')),
            tr(' - When this option is checked, people exposed to the hazard '
               'class will be included in the count of affected people.')))

    bullets.add(
        m.Text(
            m.ImportantText(tr('Displacement Rate')),
            tr(' - The displacement rate is used to estimate the number of '
               'people displaced for each hazard class. People must be affected '
               'before they can be displaced. ')))
    message.add(bullets)
    message.add(
        m.Paragraph(
            tr('Please refer to the InaSAFE manual for concept definitions and '
               'more information on the source of the hazard classifications and '
               'default settings. We really encourage you to consider these '
               'parameters carefully and to choose appropriate values for your '
               'local situation based on past events and expert knowledge.')))

    header = m.Heading(tr('GIS Environment tab'), **INFO_STYLE)
    message.add(header)
    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'inasafe-options-environment-screenshot.png' % resources_path()),
                            style_class='text-center')
    message.add(paragraph)

    message.add(
        m.Paragraph(
            tr('The GIS Environment tab provides several general settings:')))

    bullets = m.BulletedList()

    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Always show welcome message when opening QGIS with InaSAFE'
                   )),
            tr(' - When this option is enabled, the welcome message will be '
               'enabled when opening QGIS with InaSAFE. By default the Welcome '
               'message will be displayed.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Show organisation logo in InaSAFE dock')),
            tr(' - When this option is enabled, a logo will be displayed at the '
               'bottom of the InaSAFE dock widget. By default the logo used '
               'is the InaSAFE supporters logo, but you can alter this by '
               'setting the \'Use custom organisation logo\' option in '
               'the template options tab (see below).')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Show only visible layers in the InaSAFE dock')),
            tr(' - When this option is enabled layers that are not visible '
               'in the QGIS layers panel will not be shown in the hazard, '
               'exposure and aggregation combo boxes in the dock area.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Set QGIS layer name from title in keywords')),
            tr(' - If this option is enabled, the InaSAFE keywords title '
               'attribute will be used for the layer name in the QGIS Layers list '
               'when adding a layer.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Zoom to impact layer on scenario estimate completion')),
            tr(' - When this option is enabled, the map view extents will '
               'be updated to match the extents of the generated impact layer '
               'once the analysis completes.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Hide exposure on scenario estimate completion')),
            tr(' - Use this option if you prefer to not show the exposure '
               'layer as an underlay behind the generated impact layer.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Show only impact layer on report map')),
            tr('When this option is enabled, the map report created after an '
               'analysis completes will not show any other layers in your '
               'current project except for the impact layer. ')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Print atlas report on atlas driven template with the '
                   'aggregation layer')),
            tr('When this option is enabled, InaSAFE will generate an atlas '
               'report based on aggregation area if the template has atlas '
               'generation flag enabled.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Use selected features only with the aggregation layer')),
            tr('If enabled, running an analysis with some features of the '
               'aggregation layer selected will constrain the analysis to only '
               'those selected aggregation areas, all others will be ignored.')
        ))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Location for results')),
            tr(' - By default, InaSAFE will write impact layer and intermediate '
               'outputs to the system temporary directory. On some operating '
               'systems, these temporary files will be deleted on each reboot. '
               'If you wish to, you can specify an alternative directory '
               'to use for storing these temporary files.')))
    message.add(bullets)

    header = m.Heading(tr('Earthquake tab'), **INFO_STYLE)
    message.add(header)
    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'inasafe-options-earthquake-screenshot.png' % resources_path()),
                            style_class='text-center')
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('In this tab you can select which earthquake fatality model to use '
           'when estimating earthquake impact on population. This option is '
           'global - it will affect all subsequent earthquake analyses carried '
           'out in InaSAFE.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('When selecting an earthquake analysis model, its details will be '
           'shown below in the text box area.'))
    message.add(paragraph)

    header = m.Heading(tr('Template Options tab'), **INFO_STYLE)
    message.add(header)
    paragraph = m.Paragraph(m.Image('file:///%s/img/screenshots/'
                                    'inasafe-options-template-screenshot.png' %
                                    resources_path()),
                            style_class='text-center')
    message.add(paragraph)

    message.add(
        m.Paragraph(
            tr('This tab has options relating to the generation of map composer '
               'templates and how reports will be printed:'
               '.')))

    bullets = m.BulletedList()

    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom north arrow image')),
            tr(' - InaSAFE provides a basic north arrow which is placed on '
               'generated map compositions and rendered PDF reports. You can '
               'replace this north arrow with one of your own choosing using '
               'this option.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Use custom disclaimer text')),
            tr(' - By default, InaSAFE will display a disclaimer on reports '
               'advising readers of the report to exercise caution when '
               'interpreting the outputs presented. You can override this '
               'text using this option, though we do advise that you include '
               'a similar statement of caution in your overridden text.')))
    message.add(bullets)

    header = m.Heading(tr('Demographic Defaults tab'), **INFO_STYLE)
    message.add(header)
    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'inasafe-options-demographic-screenshot.png' % resources_path()),
                            style_class='text-center')
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('In this tab you will find options for setting the default ratios '
           'for demographic groups. There is more detailed help on demographic '
           'groups within the main help page for InaSAFE in the Field Mapping '
           'Tool section. Essentially default ratios for demographic groups '
           'determine what proportion of the population are within each '
           'population group (e.g. infants versus children etc.). The options '
           'defined in this tab are used in cases where you choose to use the '
           'global default ratios while configuring the keywords for an '
           'aggregation layer as shown below.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Note that the contents of this tab may changed depending on what '
           'groups have been defined for demographic breakdowns.'))
    message.add(paragraph)
    paragraph = m.Paragraph(m.Image(
        'file:///%s/img/screenshots/'
        'field-mapping-tool-default-ratios-screenshot.png' % resources_path()),
                            style_class='text-center')
    message.add(paragraph)

    header = m.Heading(tr('Advanced tab'), **INFO_STYLE)
    message.add(header)
    paragraph = m.Paragraph(m.Image('file:///%s/img/screenshots/'
                                    'inasafe-options-advanced-screenshot.png' %
                                    resources_path()),
                            style_class='text-center')
    message.add(paragraph)

    message.add(
        m.Paragraph(
            tr('This tab contains options intended for advanced users only: '))
    )

    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Keyword cache for remote databases')),
            tr(' - When InaSAFE is used with remote layers (for example a '
               'database layer or a WFS layer), it is not possible to store the '
               'keywords for the layer with the layer itself. To accommodate for '
               'these types of layers, InaSAFE writes the keywords to a small '
               'file based database (using sqlite) and then retrieves them based '
               'on unique connection details used for that layer. You can '
               'specify a custom path to be used for storing the keywords '
               'database using this option.')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('Help to improve InaSAFE by submitting errors to a '
                   'remote server')),
            tr(' - With this option enabled, InaSAFE will post any errors that '
               'occur to an online server for analysis by our development team. '
               'This option is disabled by default as some may consider some of '
               'the data submitted (IP Address, logged in user name) to be '
               'sensitive.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Enable developer mode')),
            tr(' - When this option is enabled, right clicking on the webview '
               'widget in the dock will allow you to debug the generated HTML. '
               'In addition, if the metadata.txt for the running InaSAFE is '
               'set to \'alpha\', an additional icon will be added to the '
               'toolbar to add test layers to the QGIS project.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Generate reports')),
            tr(' - When this option is enabled, InaSAFE will generate reports. '
               )))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Show memory profile')),
            tr(' - When this option is enabled, InaSAFE will display the memory '
               'profile when it runs. ')))

    message.add(bullets)

    return message
Пример #16
0
def definition_to_message(definition, heading_style=None):
    """Helper function to render a definition to a message.

    :param definition: A definition dictionary (see definitions package).
    :type definition: dict

    :param heading_style: Optional style to apply to the definition
        heading. See safe.messaging.styles
    :type heading_style: dict


    :returns: Message
    :rtype: str
    """

    if heading_style:
        header = m.Heading(definition['name'], **heading_style)
    else:
        header = m.Paragraph(m.ImportantText(definition['name']))
    message = m.Message()
    message.add(header)
    # If the definition has an icon, we put the icon and description side by
    # side in a table otherwise just show the description as a paragraph
    url = _definition_icon_url(definition)
    if url is None:
        LOGGER.info('No URL for definition icon')
        message.add(m.Paragraph(definition['description']))
        for citation in definition['citations']:
            if citation['text'] in [None, '']:
                continue
            if citation['link'] in [None, '']:
                message.add(m.Paragraph(citation['text']))
            else:
                message.add(
                    m.Paragraph(m.Link(citation['link'], citation['text'])))
    else:
        LOGGER.info('Creating mini table for definition description: ' + url)
        table = m.Table(style_class='table table-condensed')
        row = m.Row()
        row.add(m.Cell(m.Image(url, **MEDIUM_ICON_STYLE)))
        row.add(m.Cell(definition['description']))
        table.add(row)
        for citation in definition['citations']:
            if citation['text'] in [None, '']:
                continue
            row = m.Row()
            row.add(m.Cell(''))
            if citation['link'] in [None, '']:
                row.add(m.Cell(citation['text']))
            else:
                row.add(m.Cell(m.Link(citation['link'], citation['text'])))
            table.add(row)
        message.add(table)

    url = _definition_screenshot_url(definition)
    if url:
        message.add(m.Image(url))

    # types contains e.g. hazard_all
    if 'types' in definition:
        for sub_definition in definition['types']:
            message.add(
                definition_to_message(sub_definition, RED_CHAPTER_STYLE))

    #
    # Notes section if available
    #

    if 'notes' in definition:
        # Start a notes details group too since we have an exposure
        message.add(m.Heading(tr('Notes:'), **DETAILS_STYLE))
        message.add(m.Heading(tr('General notes:'), **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['notes']:
            bullets.add(m.Text(note))
        message.add(bullets)

    # This only for EQ
    if 'earthquake_fatality_models' in definition:
        for model in definition['earthquake_fatality_models']:
            message.add(m.Heading(model['name'], **DETAILS_SUBGROUP_STYLE))
            bullets = m.BulletedList()
            for note in model['notes']:
                bullets.add(m.Text(note))
                message.add(bullets)

    for exposure in exposure_all:
        extra_exposure_notes = specific_notes(definition, exposure)
        if extra_exposure_notes:
            title = tr(u'Notes for exposure : {exposure_name}').format(
                exposure_name=exposure['name'])
            message.add(m.Heading(title, **DETAILS_SUBGROUP_STYLE))
            bullets = m.BulletedList()
            for note in extra_exposure_notes:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'continuous_notes' in definition:
        message.add(
            m.Heading(tr('Notes for continuous datasets:'),
                      **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['continuous_notes']:
            bullets.add(m.Text(note))
        message.add(bullets)

    if 'classified_notes' in definition:
        message.add(
            m.Heading(tr('Notes for classified datasets:'),
                      **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['classified_notes']:
            bullets.add(m.Text(note))
        message.add(bullets)

    if 'single_event_notes' in definition:
        message.add(m.Heading(tr('Notes for single events'), **DETAILS_STYLE))
        if len(definition['single_event_notes']) < 1:
            message.add(m.Paragraph(tr('No single event notes defined.')))
        else:
            bullets = m.BulletedList()
            for note in definition['single_event_notes']:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'multi_event_notes' in definition:
        message.add(
            m.Heading(tr('Notes for multi events / scenarios:'),
                      **DETAILS_STYLE))
        if len(definition['multi_event_notes']) < 1:
            message.add(m.Paragraph(tr('No multi-event notes defined.')))
        else:
            bullets = m.BulletedList()
            for note in definition['multi_event_notes']:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'actions' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Actions:'))))
        bullets = m.BulletedList()
        for note in definition['actions']:
            bullets.add(m.Text(note))
        message.add(bullets)

    for exposure in exposure_all:
        extra_exposure_actions = specific_actions(definition, exposure)
        if extra_exposure_actions:
            title = tr(u'Actions for exposure : {exposure_name}').format(
                exposure_name=exposure['name'])
            message.add(m.Heading(title, **DETAILS_SUBGROUP_STYLE))
            bullets = m.BulletedList()
            for note in extra_exposure_actions:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'continuous_hazard_units' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Units:'))))
        table = m.Table(style_class='table table-condensed table-striped')
        row = m.Row()
        row.add(m.Cell(tr('Name')), header_flag=True)
        row.add(m.Cell(tr('Plural')), header_flag=True)
        row.add(m.Cell(tr('Abbreviation')), header_flag=True)
        row.add(m.Cell(tr('Details')), header_flag=True)
        table.add(row)
        for unit in definition['continuous_hazard_units']:
            row = m.Row()
            row.add(m.Cell(unit['name']))
            row.add(m.Cell(unit['plural_name']))
            row.add(m.Cell(unit['abbreviation']))
            row.add(m.Cell(unit['description']))
            table.add(row)
        message.add(table)

    if 'extra_fields' and 'fields' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Fields:'))))
        table = _create_fields_table()
        all_fields = definition['fields'] + definition['extra_fields']
        for field in all_fields:
            _add_field_to_table(field, table)
        message.add(table)

    if 'classifications' in definition:
        message.add(m.Heading(tr('Hazard classifications'), **DETAILS_STYLE))
        message.add(
            m.Paragraph(definitions.hazard_classification['description']))
        for inasafe_class in definition['classifications']:
            message.add(
                definition_to_message(inasafe_class, DETAILS_SUBGROUP_STYLE))

    if 'classes' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Classes:'))))
        table = _make_defaults_table()
        for inasafe_class in definition['classes']:
            row = m.Row()
            # name() on QColor returns its hex code
            if 'color' in inasafe_class:
                colour = inasafe_class['color'].name()
                row.add(
                    m.Cell(u'', attributes='style="background: %s;"' % colour))
            else:
                row.add(m.Cell(u' '))

            row.add(m.Cell(inasafe_class['name']))
            if 'affected' in inasafe_class:
                row.add(m.Cell(tr(inasafe_class['affected'])))
            else:
                row.add(m.Cell(tr('unspecified')))

            if 'displacement_rate' in inasafe_class:
                rate = inasafe_class['displacement_rate'] * 100
                rate = u'%s%%' % rate
                row.add(m.Cell(rate))
            else:
                row.add(m.Cell(tr('unspecified')))

            if 'string_defaults' in inasafe_class:
                defaults = None
                for default in inasafe_class['string_defaults']:
                    if defaults:
                        defaults += ',%s' % default
                    else:
                        defaults = default
                row.add(m.Cell(defaults))
            else:
                row.add(m.Cell(tr('unspecified')))
            # Min may be a single value or a dict of values so we need
            # to check type and deal with it accordingly
            if 'numeric_default_min' in inasafe_class:
                if isinstance(inasafe_class['numeric_default_min'], dict):
                    bullets = m.BulletedList()
                    minima = inasafe_class['numeric_default_min']
                    for key, value in minima.iteritems():
                        bullets.add(u'%s : %s' % (key, value))
                    row.add(m.Cell(bullets))
                else:
                    row.add(m.Cell(inasafe_class['numeric_default_min']))
            else:
                row.add(m.Cell(tr('unspecified')))

            # Max may be a single value or a dict of values so we need
            # to check type and deal with it accordingly
            if 'numeric_default_max' in inasafe_class:
                if isinstance(inasafe_class['numeric_default_max'], dict):
                    bullets = m.BulletedList()
                    maxima = inasafe_class['numeric_default_max']
                    for key, value in maxima.iteritems():
                        bullets.add(u'%s : %s' % (key, value))
                    row.add(m.Cell(bullets))
                else:
                    row.add(m.Cell(inasafe_class['numeric_default_max']))
            else:
                row.add(m.Cell(tr('unspecified')))

            table.add(row)
            # Description goes in its own row with spanning
            row = m.Row()
            row.add(m.Cell(''))
            row.add(m.Cell(inasafe_class['description'], span=6))
            table.add(row)
        # For hazard classes we also add the 'not affected' class manually:
        if definition['type'] == definitions.hazard_classification_type:
            row = m.Row()
            colour = definitions.not_exposed_class['color'].name()
            row.add(m.Cell(u'', attributes='style="background: %s;"' % colour))
            description = definitions.not_exposed_class['description']
            row.add(m.Cell(description, span=5))
            table.add(row)

        message.add(table)

    if 'affected' in definition:
        if definition['affected']:
            message.add(
                m.Paragraph(
                    tr('Exposure entities in this class ARE considered affected'
                       )))
        else:
            message.add(
                m.Paragraph(
                    tr('Exposure entities in this class are NOT considered '
                       'affected')))

    if 'optional' in definition:
        if definition['optional']:
            message.add(
                m.Paragraph(
                    tr('This class is NOT required in the hazard keywords.')))
        else:
            message.add(
                m.Paragraph(
                    tr('This class IS required in the hazard keywords.')))

    return message
Пример #17
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 4.0.0

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    # We will store a contents section at the top for easy navigation
    table_of_contents = m.Message()
    # and this will be the main message that we create
    message = m.Message()

    _create_section_header(
        message,
        table_of_contents,
        'overview',
        tr('Overview'),
        heading_level=1)
    ##
    # Credits and disclaimers ...
    ##
    _create_section_header(
        message,
        table_of_contents,
        'disclaimer',
        tr('Disclaimer'),
        heading_level=2)
    message.add(m.Paragraph(definitions.messages.disclaimer()))

    _create_section_header(
        message,
        table_of_contents,
        'limitations',
        tr('Limitations and License'),
        heading_level=2)
    bullets = m.BulletedList()
    for item in definitions.limitations():
        bullets.add(item)
    message.add(bullets)

    ##
    # Basic concepts ...
    ##
    ##
    # Help dialog contents ...
    ##
    _create_section_header(
        message,
        table_of_contents,
        'glossary',
        tr('Glossary of terms'),
        heading_level=1)

    last_group = None
    table = None
    for key, value in list(definitions.concepts.items()):
        current_group = value['group']
        if current_group != last_group:
            if last_group is not None:
                message.add(table)
            _create_section_header(
                message,
                table_of_contents,
                current_group.replace(' ', '-'),
                current_group,
                heading_level=2)
            table = _start_glossary_table(current_group)
            last_group = current_group
        row = m.Row()
        term = value['key'].replace('_', ' ').title()
        description = m.Message(value['description'])
        for citation in value['citations']:
            if citation['text'] in [None, '']:
                continue
            if citation['link'] in [None, '']:
                description.add(m.Paragraph(citation['text']))
            else:
                description.add(m.Paragraph(
                    m.Link(citation['link'], citation['text'])))
        row.add(m.Cell(term))
        row.add(m.Cell(description))
        url = _definition_icon_url(value)
        if url:
            row.add(m.Cell(m.Image(url, **MEDIUM_ICON_STYLE)))
        else:
            row.add(m.Cell(''))
        table.add(row)
    # ensure the last group's table is added
    message.add(table)

    ##
    # Help dialog contents ...
    ##
    _create_section_header(
        message,
        table_of_contents,
        'core-functionality',
        tr('Core functionality and tools'),
        heading_level=1)

    _create_section_header(
        message,
        table_of_contents,
        'dock',
        tr('The InaSAFE Dock'),
        heading_level=2)
    message.add(dock_help())

    _create_section_header(
        message,
        table_of_contents,
        'reports',
        tr('InaSAFE Reports'),
        heading_level=2)
    message.add(report_help())

    _create_section_header(
        message,
        table_of_contents,
        'extents',
        tr('Managing analysis extents with the extents selector'),
        heading_level=2)
    message.add(extent_help())

    _create_section_header(
        message,
        table_of_contents,
        'options',
        tr('InaSAFE Options'),
        heading_level=2)
    message.add(options_help())

    _create_section_header(
        message,
        table_of_contents,
        'batch-runner',
        tr('The Batch Runner'),
        heading_level=2)
    message.add(batch_help())

    _create_section_header(
        message,
        table_of_contents,
        'osm-downloader',
        tr('The OpenStreetmap Downloader'),
        heading_level=2)
    message.add(osm_help())

    _create_section_header(
        message,
        table_of_contents,
        'petabencana-downloader',
        tr('The PetaBencana Downloader'),
        heading_level=2)
    message.add(petabencana_help())

    _create_section_header(
        message,
        table_of_contents,
        'shakemap-converter',
        tr('The Shakemap Converter'),
        heading_level=2)
    message.add(shakemap_help())

    _create_section_header(
        message,
        table_of_contents,
        'multi-buffer-tool',
        tr('The Multi Buffer Tool'),
        heading_level=2)
    message.add(multi_buffer_help())

    # Field mapping tool has a few added bits to enumerate the groups
    _create_section_header(
        message,
        table_of_contents,
        'field-mapping-tool',
        tr('The Field Mapping Tool'),
        heading_level=2)
    message.add(field_mapping_tool_help())

    _create_section_header(
        message,
        table_of_contents,
        'exposure-groups',
        tr('Exposure Groups'),
        heading_level=3)
    message.add(m.Paragraph(
        'The following demographic groups apply only to vector population '
        'exposure layers:'
    ))
    for group in population_field_groups:
        definition_to_message(
            group, message, table_of_contents, heading_level=4)

    _create_section_header(
        message,
        table_of_contents,
        'aggregation-groups',
        tr('Aggregation Groups'),
        heading_level=3)
    message.add(m.Paragraph(
        'The following demographic groups apply only to aggregation layers:'
    ))
    for group in aggregation_field_groups:
        definition_to_message(
            group, message, table_of_contents, heading_level=4)

    # End of field mapping tool help

    # Keep this last in the tool section please as it has subsections
    # and so uses the top level section style
    _create_section_header(
        message,
        table_of_contents,
        'minimum-needs',
        tr('Minimum Needs'),
        heading_level=2)
    _create_section_header(
        message,
        table_of_contents,
        'minimum-needs-tool',
        tr('The minimum needs tool'),
        heading_level=3)
    message.add(needs_help())
    _create_section_header(
        message,
        table_of_contents,
        'minimum-manager',
        tr('The minimum needs manager'),
        heading_level=3)
    message.add(needs_manager_help())

    ##
    #  Analysis workflow
    ##

    _create_section_header(
        message,
        table_of_contents,
        'analysis-steps',
        tr('Analysis steps'),
        heading_level=1)
    _create_section_header(
        message,
        table_of_contents,
        'analysis-internal-process',
        tr('Analysis internal process'),
        heading_level=2)
    analysis = definitions.concepts['analysis']
    message.add(analysis['description'])
    url = _definition_screenshot_url(analysis)
    if url:
        message.add(m.Paragraph(m.Image(url), style_class='text-center'))

    _create_section_header(
        message,
        table_of_contents,
        'analysis-progress-reporting',
        tr('Progress reporting steps'),
        heading_level=2)
    steps = list(definitions.analysis_steps.values())
    for step in steps:
        definition_to_message(
            step, message, table_of_contents, heading_level=3)

    ##
    #  Hazard definitions
    ##

    _create_section_header(
        message,
        table_of_contents,
        'hazards',
        tr('Hazard Concepts'),
        heading_level=1)

    hazard_category = definitions.hazard_category
    definition_to_message(
        hazard_category,
        message,
        table_of_contents,
        heading_level=2)

    hazards = definitions.hazards
    definition_to_message(
        hazards,
        message,
        table_of_contents,
        heading_level=2)

    ##
    #  Exposure definitions
    ##

    _create_section_header(
        message,
        table_of_contents,
        'exposures',
        tr('Exposure Concepts'),
        heading_level=1)
    exposures = definitions.exposures

    definition_to_message(
        exposures,
        message,
        table_of_contents,
        heading_level=2)

    ##
    #  Defaults
    ##

    _create_section_header(
        message,
        table_of_contents,
        'defaults',
        tr('InaSAFE Defaults'),
        heading_level=1)
    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('Name'), header=True))
    row.add(m.Cell(tr('Default value'), header=True))
    row.add(m.Cell(tr('Default min'), header=True))
    row.add(m.Cell(tr('Default max'), header=True))
    row.add(m.Cell(tr('Description'), header=True))
    table.add(row)
    defaults = [
        definitions.youth_ratio_default_value,
        definitions.adult_ratio_default_value,
        definitions.elderly_ratio_default_value,
        definitions.female_ratio_default_value,
        definitions.feature_rate_default_value
    ]
    for default in defaults:
        row = m.Row()
        row.add(m.Cell(default['name']))
        row.add(m.Cell(default['default_value']))
        row.add(m.Cell(default['min_value']))
        row.add(m.Cell(default['max_value']))
        row.add(m.Cell(default['description']))
        table.add(row)
    message.add(table)

    ##
    #  All Fields
    ##

    _create_section_header(
        message,
        table_of_contents,
        'all-fields',
        tr('Fields'),
        heading_level=1)
    _create_section_header(
        message,
        table_of_contents,
        'input-fields',
        tr('Input dataset fields'),
        heading_level=2)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Exposure fields'),
        definitions.exposure_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Hazard fields'),
        definitions.hazard_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Aggregation fields'),
        definitions.aggregation_fields)
    _create_section_header(
        message,
        table_of_contents,
        'output-fields',
        tr('Output dataset fields'),
        heading_level=2)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Impact fields'),
        definitions.impact_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Aggregate hazard fields'),
        definitions.aggregate_hazard_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Aggregation summary fields'),
        definitions.aggregation_summary_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Exposure summary table fields'),
        definitions.exposure_summary_table_fields)
    _create_fields_section(
        message,
        table_of_contents,
        tr('Analysis fields'),
        definitions.analysis_fields)

    ##
    #  Geometries
    ##

    _create_section_header(
        message,
        table_of_contents,
        'geometries',
        tr('Layer Geometry Types'),
        heading_level=1)
    _create_section_header(
        message,
        table_of_contents,
        'vector-geometries',
        tr('Vector'),
        heading_level=2)
    definition_to_message(
        definitions.layer_geometry_point,
        message,
        table_of_contents,
        heading_level=3)
    definition_to_message(
        definitions.layer_geometry_line,
        message,
        table_of_contents,
        heading_level=3)
    definition_to_message(
        definitions.layer_geometry_polygon,
        message,
        table_of_contents,
        heading_level=3)
    _create_section_header(
        message,
        table_of_contents,
        'raster-geometries',
        tr('Raster'),
        heading_level=2)
    definition_to_message(
        definitions.layer_geometry_raster,
        message,
        table_of_contents,
        heading_level=3)

    ##
    #  Layer Modes
    ##

    _create_section_header(
        message,
        table_of_contents,
        'layer-modes',
        tr('Layer Modes'),
        heading_level=1)
    definition_to_message(
        definitions.layer_mode,
        message,
        table_of_contents,
        heading_level=2)

    ##
    #  Layer Purposes
    ##

    _create_section_header(
        message,
        table_of_contents,
        'layer-purposes',
        tr('Layer Purposes'),
        heading_level=1)
    definition_to_message(
        definitions.layer_purpose_hazard,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_exposure,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_aggregation,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_exposure_summary,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_aggregate_hazard_impacted,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_aggregation_summary,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_exposure_summary_table,
        message,
        table_of_contents,
        heading_level=2)
    definition_to_message(
        definitions.layer_purpose_profiling,
        message,
        table_of_contents,
        heading_level=2)

    ##
    # All units
    ##

    _create_section_header(
        message,
        table_of_contents,
        'all-units',
        tr('All Units'),
        heading_level=1)
    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('Name'), header=True))
    row.add(m.Cell(tr('Plural'), header=True))
    row.add(m.Cell(tr('Abbreviation'), header=True))
    row.add(m.Cell(tr('Details'), header=True))
    table.add(row)
    for unit in definitions.units_all:
        row = m.Row()
        row.add(m.Cell(unit['name']))
        row.add(m.Cell(unit['plural_name']))
        row.add(m.Cell(unit['abbreviation']))
        row.add(m.Cell(unit['description']))
        table.add(row)
    message.add(table)

    ##
    #  Post processors
    ##

    _create_section_header(
        message,
        table_of_contents,
        'post-processors',
        tr('Post Processors'),
        heading_level=1)
    _create_section_header(
        message,
        table_of_contents,
        'post-processor-input-types',
        tr('Post Processor Input Types'),
        heading_level=2)
    table = _create_post_processor_subtable(
        post_processor_input_types
    )
    message.add(table)

    _create_section_header(
        message,
        table_of_contents,
        'post-processor-input-values',
        tr('Post Processor Input Values'),
        heading_level=2)
    table = _create_post_processor_subtable(
        post_processor_input_values
    )
    message.add(table)

    _create_section_header(
        message,
        table_of_contents,
        'post-processor-process-values',
        tr('Post Processor Process Types'),
        heading_level=2)
    table = _create_post_processor_subtable(
        safe.processors.post_processor_process_types
    )
    message.add(table)

    _create_section_header(
        message,
        table_of_contents,
        'post-processors',
        tr('Post Processors'),
        heading_level=2)
    post_processors = safe.processors.post_processors
    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('Name'), header=True))
    row.add(m.Cell(tr('Input Fields'), header=True))
    row.add(m.Cell(tr('Output Fields'), header=True))
    table.add(row)
    for post_processor in post_processors:
        row = m.Row()
        row.add(m.Cell(post_processor['name']))
        # Input fields
        bullets = m.BulletedList()
        for key, value in sorted(post_processor['input'].items()):
            bullets.add(key)
        row.add(m.Cell(bullets))
        # Output fields
        bullets = m.BulletedList()
        for key, value in sorted(post_processor['output'].items()):
            name = value['value']['name']
            formula_type = value['type']['key']
            if formula_type == 'formula':
                formula = value['formula']
            else:
                # We use python introspection because the processor
                # uses a python function for calculations
                formula = value['function'].__name__
                formula += ' ('
                formula += value['function'].__doc__
                formula += ')'
            bullets.add('%s  %s. : %s' % (
                name, formula_type, formula))

        row.add(m.Cell(bullets))
        table.add(row)
        # Add the descriptions
        row = m.Row()
        row.add(m.Cell(''))
        row.add(m.Cell(post_processor['description'], span=2))
        table.add(row)
    message.add(table)

    ##
    # Reporting
    ##
    _create_section_header(
        message,
        table_of_contents,
        'reporting',
        tr('Reporting'),
        heading_level=1)

    paragraph = m.Paragraph(
        m.ImportantText(tr('Note: ')),
        m.Text(tr(
            'This section of the help documentation is intended for advanced '
            'users who want to modify reports which are produced by InaSAFE.'
        )))
    message.add(paragraph)
    _create_section_header(
        message,
        table_of_contents,
        'reporting-overview',
        tr('Overview'),
        heading_level=2)
    message.add(m.Paragraph(tr(
        'Whenever InaSAFE completes an analysis, it will automatically '
        'generate a number of reports. Some of these reports are based on '
        'templates that are shipped with InaSAFE, and can be customised or '
        'over-ridden by creating your own templates. The following '
        'reports are produced in InaSAFE:'
    )))
    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('Name'), header=True))
    row.add(m.Cell(tr('Customisable?'), header=True))
    row.add(m.Cell(tr('Example'), header=True))
    row.add(m.Cell(tr('Description'), header=True))
    table.add(row)

    for report in all_reports:
        row = m.Row()
        row.add(m.Cell(report['name']))
        if report['customisable']:
            row.add(m.Cell(tr('Yes')))
        else:
            row.add(m.Cell(tr('No')))
        png_image_path = resources_path(
            'img', 'screenshots', report['thumbnail'])
        row.add(m.Image(png_image_path, style_class='text-center'))
        row.add(m.Cell(report['description']))
        table.add(row)

    message.add(table)

    message.add(m.Paragraph(tr(
        'In the sections that follow, we provide more technical information '
        'about the custom QGIS Expressions and special template elements '
        'that can be used to customise your templates.'
    )))

    _create_section_header(
        message,
        table_of_contents,
        'reporting-expressions',
        tr('QGIS Expressions'),
        heading_level=2)
    message.add(m.Paragraph(tr(
        'InaSAFE adds a number of expressions that can be used to '
        'conveniently obtain provenance data to the active analysis results. '
        'The expressions can also be used elsewhere in QGIS as needed.'
        '.'
    )))

    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('Name'), header=True))
    row.add(m.Cell(tr('Description'), header=True))
    table.add(row)
    for expression_name, expression in sorted(qgis_expressions().items()):
        row = m.Row()
        row.add(m.Cell(expression_name))
        help = expression.helptext()
        # This pattern comes from python/qgis/core/__init__.py ≈ L79
        pattern = r'<h3>(.*) function</h3><br>'
        help = re.sub(pattern, '', help)
        help = re.sub(r'\n', '<br>', help)
        row.add(m.Cell(help))
        table.add(row)
    message.add(table)

    _create_section_header(
        message,
        table_of_contents,
        'reporting-composer-elements',
        tr('Composer Elements'),
        heading_level=2)
    message.add(m.Paragraph(tr(
        'InaSAFE looks for elements with specific id\'s on the composer '
        'page and replaces them with InaSAFE specific content.'
    )))
    table = m.Table(style_class='table table-condensed table-striped')
    row = m.Row()
    row.add(m.Cell(tr('ID'), header=True))
    row.add(m.Cell(tr('Description'), header=True))
    table.add(row)
    for item in html_frame_elements:
        row = m.Row()
        row.add(m.Cell(item['id']))
        row.add(m.Cell(item['description']))
        table.add(row)
    message.add(table)

    ##
    # Developer documentation
    ##
    _create_section_header(
        message,
        table_of_contents,
        'developer-guide',
        tr('Developer Guide'),
        heading_level=1)
    message.add(developer_help())

    # Finally we add the table of contents at the top
    full_message = m.Message()
    # Contents is not a link so reset style
    style = SECTION_STYLE
    style['element_id'] = ''
    header = m.Heading(tr('Contents'), **style)
    full_message.add(header)
    full_message.add(table_of_contents)
    full_message.add(message)
    return full_message
Пример #18
0
    def show_current_state(self):
        """Setup the UI for QTextEdit to show the current state."""
        right_panel_heading = QLabel(tr('Status'))
        right_panel_heading.setFont(big_font)
        right_panel_heading.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.right_layout.addWidget(right_panel_heading)

        message = m.Message()
        if self.layer_mode == layer_mode_continuous:
            title = tr('Thresholds')
        else:
            title = tr('Value maps')

        message.add(m.Heading(title, **INFO_STYLE))

        for i in range(len(self.exposures)):
            message.add(m.Text(self.exposure_labels[i]))

            classification = self.get_classification(
                self.exposure_combo_boxes[i])
            if self.layer_mode == layer_mode_continuous:
                thresholds = self.thresholds.get(self.exposures[i]['key'])
                if not thresholds or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Minimum')))
                header.add(m.Cell(tr('Maximum')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    threshold = thresholds[classification['key']]['classes'][
                        the_class['key']]
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(threshold[0]))
                    row.add(m.Cell(threshold[1]))
                    table.add(row)
            else:
                value_maps = self.value_maps.get(self.exposures[i]['key'])
                if not value_maps or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Value')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    value_map = value_maps[classification['key']][
                        'classes'].get(the_class['key'], [])
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(', '.join([str(v) for v in value_map])))
                    table.add(row)
            message.add(table)

        # status_text_edit = QTextBrowser(None)
        status_text_edit = QWebView(None)
        status_text_edit.setSizePolicy(
            QSizePolicy.Ignored,
            QSizePolicy.Ignored)

        status_text_edit.page().mainFrame().setScrollBarPolicy(
            Qt.Horizontal,
            Qt.ScrollBarAlwaysOff)
        html_string = html_header() + message.to_html() + html_footer()
        status_text_edit.setHtml(html_string)
        self.right_layout.addWidget(status_text_edit)
Пример #19
0
def definition_to_message(definition,
                          message=None,
                          table_of_contents=None,
                          heading_level=None):
    """Helper function to render a definition to a message.

    :param definition: A definition dictionary (see definitions package).
    :type definition: dict

    :param message: The message that the definition should be appended to.
    :type message: safe_extras.parameters.message.Message

    :param table_of_contents: Table of contents that the headings should be
        included in.
    :type message: safe_extras.parameters.message.Message

    :param heading_level: Optional style to apply to the definition
        heading. See HEADING_LOOKUPS
    :type heading_level: int

    :returns: Message
    :rtype: str
    """

    if message is None:
        message = m.Message()

    if table_of_contents is None:
        table_of_contents = m.Message()

    if heading_level:
        _create_section_header(message,
                               table_of_contents,
                               definition['name'].replace(' ', '-'),
                               definition['name'],
                               heading_level=heading_level)
    else:
        header = m.Paragraph(m.ImportantText(definition['name']))
        message.add(header)

    # If the definition has an icon, we put the icon and description side by
    # side in a table otherwise just show the description as a paragraph
    url = _definition_icon_url(definition)
    if url is None:
        message.add(m.Paragraph(definition['description']))
        if 'citations' in definition:
            _citations_to_message(message, definition)
    else:
        LOGGER.info('Creating mini table for definition description: ' + url)
        table = m.Table(style_class='table table-condensed')
        row = m.Row()
        row.add(m.Cell(m.Image(url, **MEDIUM_ICON_STYLE)))
        row.add(m.Cell(definition['description']))
        table.add(row)
        for citation in definition['citations']:
            if citation['text'] in [None, '']:
                continue
            row = m.Row()
            row.add(m.Cell(''))
            if citation['link'] in [None, '']:
                row.add(m.Cell(citation['text']))
            else:
                row.add(m.Cell(m.Link(citation['link'], citation['text'])))
            table.add(row)
        message.add(table)

    url = _definition_screenshot_url(definition)
    if url:
        message.add(m.Paragraph(m.Image(url), style_class='text-center'))

    # types contains e.g. hazard_all
    if 'types' in definition:
        for sub_definition in definition['types']:
            definition_to_message(sub_definition,
                                  message,
                                  table_of_contents,
                                  heading_level=3)

    #
    # Notes section if available
    #

    if 'notes' in definition:
        # Start a notes details group too since we have an exposure
        message.add(m.Heading(tr('Notes:'), **DETAILS_STYLE))
        message.add(m.Heading(tr('General notes:'), **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['notes']:
            if type(note) is dict:
                bullets = _add_dict_to_bullets(bullets, note)
            elif note:
                bullets.add(m.Text(note))
        message.add(bullets)
        if 'citations' in definition:
            _citations_to_message(message, definition)

    # This only for EQ
    if 'earthquake_fatality_models' in definition:
        current_function = current_earthquake_model_name()
        paragraph = m.Paragraph(
            'The following earthquake fatality models are available in '
            'InaSAFE. Note that you need to set one of these as the '
            'active model in InaSAFE Options. The currently active model is: ',
            m.ImportantText(current_function))
        message.add(paragraph)

        models_definition = definition['earthquake_fatality_models']
        for model in models_definition:
            message.add(m.Heading(model['name'], **DETAILS_SUBGROUP_STYLE))
            if 'description' in model:
                paragraph = m.Paragraph(model['description'])
                message.add(paragraph)
            for note in model['notes']:
                paragraph = m.Paragraph(note)
                message.add(paragraph)
            _citations_to_message(message, model)

    for exposure in exposure_all:
        extra_exposure_notes = specific_notes(definition, exposure)
        if extra_exposure_notes:
            title = tr(u'Notes for exposure : {exposure_name}').format(
                exposure_name=exposure['name'])
            message.add(m.Heading(title, **DETAILS_SUBGROUP_STYLE))
            bullets = m.BulletedList()
            for note in extra_exposure_notes:
                if type(note) is dict:
                    bullets = _add_dict_to_bullets(bullets, note)
                elif note:
                    bullets.add(m.Text(note))
            message.add(bullets)

    if 'continuous_notes' in definition:
        message.add(
            m.Heading(tr('Notes for continuous datasets:'),
                      **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['continuous_notes']:
            bullets.add(m.Text(note))
        message.add(bullets)

    if 'classified_notes' in definition:
        message.add(
            m.Heading(tr('Notes for classified datasets:'),
                      **DETAILS_SUBGROUP_STYLE))
        bullets = m.BulletedList()
        for note in definition['classified_notes']:
            bullets.add(m.Text(note))
        message.add(bullets)

    if 'single_event_notes' in definition:
        message.add(m.Heading(tr('Notes for single events'), **DETAILS_STYLE))
        if len(definition['single_event_notes']) < 1:
            message.add(m.Paragraph(tr('No single event notes defined.')))
        else:
            bullets = m.BulletedList()
            for note in definition['single_event_notes']:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'multi_event_notes' in definition:
        message.add(
            m.Heading(tr('Notes for multi events / scenarios:'),
                      **DETAILS_STYLE))
        if len(definition['multi_event_notes']) < 1:
            message.add(m.Paragraph(tr('No multi-event notes defined.')))
        else:
            bullets = m.BulletedList()
            for note in definition['multi_event_notes']:
                bullets.add(m.Text(note))
            message.add(bullets)

    if 'actions' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Actions:'))))
        bullets = m.BulletedList()
        for note in definition['actions']:
            if type(note) is dict:
                bullets = _add_dict_to_bullets(bullets, note)
            elif note:
                bullets.add(m.Text(note))
        message.add(bullets)

    for exposure in exposure_all:
        extra_exposure_actions = specific_actions(definition, exposure)
        if extra_exposure_actions:
            title = tr(u'Actions for exposure : {exposure_name}').format(
                exposure_name=exposure['name'])
            message.add(m.Heading(title, **DETAILS_SUBGROUP_STYLE))
            bullets = m.BulletedList()
            for note in extra_exposure_actions:
                if type(note) is dict:
                    bullets = _add_dict_to_bullets(bullets, note)
                elif note:
                    bullets.add(m.Text(note))
            message.add(bullets)

    if 'continuous_hazard_units' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Units:'))))
        table = m.Table(style_class='table table-condensed table-striped')
        row = m.Row()
        row.add(m.Cell(tr('Name'), header=True))
        row.add(m.Cell(tr('Plural'), header=True))
        row.add(m.Cell(tr('Abbreviation'), header=True))
        row.add(m.Cell(tr('Details'), header=True))
        table.add(row)
        for unit in definition['continuous_hazard_units']:
            row = m.Row()
            row.add(m.Cell(unit['name']))
            row.add(m.Cell(unit['plural_name']))
            row.add(m.Cell(unit['abbreviation']))
            row.add(m.Cell(unit['description']))
            table.add(row)
        message.add(table)

    if 'fields' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Fields:'))))
        table = _create_fields_table()

        if 'extra_fields' in definition:
            all_fields = definition['fields'] + definition['extra_fields']
        else:
            all_fields = definition['fields']

        for field in all_fields:
            _add_field_to_table(field, table)
        message.add(table)

    if 'classifications' in definition:
        message.add(m.Heading(tr('Hazard classifications'), **DETAILS_STYLE))
        message.add(
            m.Paragraph(definitions.hazard_classification['description']))
        for inasafe_class in definition['classifications']:
            definition_to_message(inasafe_class,
                                  message,
                                  table_of_contents,
                                  heading_level=3)

    if 'classes' in definition:
        message.add(m.Paragraph(m.ImportantText(tr('Classes:'))))
        is_hazard = definition['type'] == hazard_classification_type
        if is_hazard:
            table = _make_defaults_hazard_table()
        else:
            table = _make_defaults_exposure_table()

        for inasafe_class in definition['classes']:
            row = m.Row()
            if is_hazard:
                # name() on QColor returns its hex code
                if 'color' in inasafe_class:
                    colour = inasafe_class['color'].name()
                    row.add(
                        m.Cell(u'',
                               attributes='style="background: %s;"' % colour))
                else:
                    row.add(m.Cell(u' '))

            row.add(m.Cell(inasafe_class['name']))

            if is_hazard:
                if 'affected' in inasafe_class:
                    row.add(m.Cell(tr(inasafe_class['affected'])))
                else:
                    row.add(m.Cell(tr('unspecified')))

            if is_hazard:
                if inasafe_class.get('fatality_rate') > 0:
                    # we want to show the rate as a scientific notation
                    rate = html_scientific_notation_rate(
                        inasafe_class['fatality_rate'])
                    rate = u'%s%%' % rate
                    row.add(m.Cell(rate))
                elif inasafe_class.get('fatality_rate') == 0:
                    row.add(m.Cell('0%'))
                else:
                    row.add(m.Cell(tr('unspecified')))

            if is_hazard:
                if 'displacement_rate' in inasafe_class:
                    rate = inasafe_class['displacement_rate'] * 100
                    rate = u'%.0f%%' % rate
                    row.add(m.Cell(rate))
                else:
                    row.add(m.Cell(tr('unspecified')))

            if 'string_defaults' in inasafe_class:
                defaults = None
                for default in inasafe_class['string_defaults']:
                    if defaults:
                        defaults += ',%s' % default
                    else:
                        defaults = default
                row.add(m.Cell(defaults))
            else:
                row.add(m.Cell(tr('unspecified')))

            if is_hazard:
                # Min may be a single value or a dict of values so we need
                # to check type and deal with it accordingly
                if 'numeric_default_min' in inasafe_class:
                    if isinstance(inasafe_class['numeric_default_min'], dict):
                        bullets = m.BulletedList()
                        minima = inasafe_class['numeric_default_min']
                        for key, value in minima.iteritems():
                            bullets.add(u'%s : %s' % (key, value))
                        row.add(m.Cell(bullets))
                    else:
                        row.add(m.Cell(inasafe_class['numeric_default_min']))
                else:
                    row.add(m.Cell(tr('unspecified')))

            if is_hazard:
                # Max may be a single value or a dict of values so we need
                # to check type and deal with it accordingly
                if 'numeric_default_max' in inasafe_class:
                    if isinstance(inasafe_class['numeric_default_max'], dict):
                        bullets = m.BulletedList()
                        maxima = inasafe_class['numeric_default_max']
                        for key, value in maxima.iteritems():
                            bullets.add(u'%s : %s' % (key, value))
                        row.add(m.Cell(bullets))
                    else:
                        row.add(m.Cell(inasafe_class['numeric_default_max']))
                else:
                    row.add(m.Cell(tr('unspecified')))

            table.add(row)
            # Description goes in its own row with spanning
            row = m.Row()
            row.add(m.Cell(''))
            row.add(m.Cell(inasafe_class['description'], span=7))
            table.add(row)

        # For hazard classes we also add the 'not affected' class manually:
        if definition['type'] == definitions.hazard_classification_type:
            row = m.Row()
            colour = definitions.not_exposed_class['color'].name()
            row.add(m.Cell(u'', attributes='style="background: %s;"' % colour))
            description = definitions.not_exposed_class['description']
            row.add(m.Cell(description, span=7))
            table.add(row)

        message.add(table)

    if 'affected' in definition:
        if definition['affected']:
            message.add(
                m.Paragraph(
                    tr('Exposure entities in this class ARE considered affected'
                       )))
        else:
            message.add(
                m.Paragraph(
                    tr('Exposure entities in this class are NOT considered '
                       'affected')))

    if 'optional' in definition:
        if definition['optional']:
            message.add(
                m.Paragraph(
                    tr('This class is NOT required in the hazard keywords.')))
        else:
            message.add(
                m.Paragraph(
                    tr('This class IS required in the hazard keywords.')))

    return message
Пример #20
0
    def to_message(self, keywords, show_header=True):
        """Format keywords as a message object.

        .. versionadded:: 3.2

        The message object can then be rendered to html, plain text etc.


        :param keywords: Keywords to be converted to a message.
        :type keywords: dict

        :param show_header: Flag indicating if InaSAFE logo etc. should be
            added above the keywords table. Default is True.
        :type show_header: bool

        :returns: A safe message object containing a table.
        :rtype: safe.messaging.message
        """
        # This order was determined in issue #2313
        preferred_order = [
            'title',
            'layer_purpose',
            'exposure',
            'hazard',
            'hazard_category',
            'layer_geometry',
            'layer_mode',
            'vector_hazard_classification',
            'exposure_unit',
            'continuous_hazard_unit',
            'volcano_name_field',
            'road_class_field',
            'structure_class_field',
            'field',
            'value_map',  # attribute values
            'resample',
            'source',
            'url',
            'scale',
            'license',
            'date',
            'keyword_version'
        ]  # everything else in arbitrary order
        report = m.Message()
        if show_header:
            logo_element = m.Brand()
            report.add(logo_element)
            report.add(
                m.Heading(self.tr('Layer keywords:'), **styles.INFO_STYLE))
            report.add(
                m.Text(
                    self.
                    tr('The following keywords are defined for the active layer:'
                       )))

        table = m.Table(style_class='table table-condensed table-striped')
        # First render out the preferred order keywords
        for keyword in preferred_order:
            if keyword in keywords:
                value = keywords[keyword]
                row = self._keyword_to_row(keyword, value)
                keywords.pop(keyword)
                table.add(row)

        # now render out any remaining keywords in arbitrary order
        for keyword in keywords:
            value = keywords[keyword]
            row = self._keyword_to_row(keyword, value)
            table.add(row)
        report.add(table)
        return report
Пример #21
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    paragraph = m.Paragraph(
        tr('InaSAFE is free software that produces realistic natural hazard '
           'impact scenarios for better planning, preparedness and response '
           'activities. It provides a simple but rigourous way to combine data '
           'from scientists, local governments and communities to provide '
           'insights into the likely impacts of future disaster events.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('The InaSAFE \'dock panel\' helps you to run hazard impact analysis '
           'within the QGIS environment. It helps you create your hazard impact '
           'analysis question and shows the results of this analysis. If you are '
           'a new user, you may also consider using the \'Impact Function '
           'Centric Wizard\' to run the analysis. This wizard will guide you '
           'through the process of running an InaSAFE assessment, with '
           'interactive step by step instructions. You can launch the wizard '
           'by clicking on this icon in the toolbar:'),
        m.Image('file:///%s/img/icons/'
                'show-wizard.svg' % resources_path(), **SMALL_ICON_STYLE),
    )
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('You can drag and drop the dock panel to reposition it on the screen. '
           'For example, dragging the panel towards the right margin of the QGIS '
           'application will dock it to the right side of the screen.'))
    message.add(paragraph)

    message.add(
        m.Paragraph(tr('There are three main areas to the dock panel:')))
    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            # format 'the __questions__ area' for proper i18n
            tr('the %s area') %
            (m.ImportantText(tr('questions')).to_html(), )))
    bullets.add(
        m.Text(
            # format 'the __results__ area' for proper i18n
            tr('the %s area') % (m.ImportantText(tr('results')).to_html(), )))
    bullets.add(
        m.Text(
            # format 'the __buttons__ area' for proper i18n
            tr('the %s area') % (m.ImportantText(tr('buttons')).to_html(), )))
    message.add(bullets)
    message.add(
        m.Paragraph(
            tr('You can get help at any time in InaSAFE by clicking on the '
               'help buttons provided on each dock and dialog.')))

    header = m.Heading(tr('The questions area'), **INFO_STYLE)
    message.add(header)
    message.add(
        m.Paragraph(
            tr('The intention of InaSAFE is to make it easy to perform your impact '
               'analysis. We start the analysis in the questions area. This area '
               'contains three drop down menus. You create your question by using '
               'these drop down menus to select the hazard and exposure data you '
               'wish to perform the analysis on. '
               'All questions follow this form:'),
            m.EmphasizedText(
                tr('In the event of a [hazard], how many [exposure] might be '
                   '[impacted]?'))))
    message.add(
        m.Paragraph(
            tr('For example: "If there is a flood, how many buildings might be '
               'flooded?"')))
    message.add(
        m.Paragraph(
            tr('InaSAFE can be used to answer such questions for hazards such as '
               'flood, tsunami, volcanic ash fall and earthquake and exposures '
               'such as population, roads, structures, land cover etc.')))
    message.add(
        m.Paragraph(
            tr('The first step in answering these questions is to load layers that '
               'represent either hazard scenarios or exposure data into QGIS. '
               'A hazard, for example, may be represented as a raster layer in '
               'QGIS where each pixel in the raster represents the flood depth '
               'following an inundation event. An exposure layer could be '
               'represented, for example, as vector polygon data representing '
               'building outlines, or a raster outline where each pixel represents '
               'the number of people thought to be living in that cell.')))
    message.add(
        m.Paragraph(
            tr('InaSAFE will combine these two layers in a '
               'mathematical model. The results of this model will show what the '
               'effect of the hazard will be on the exposed infrastructure or '
               'people. The plugin relies on simple keyword metadata '
               'associated with each layer to determine what kind of information the '
               'layer represents. You can define these keywords by '
               'selecting a layer and then clicking the InaSAFE Keywords Wizard icon '
               'on the toolbar: '),
            m.Image(
                'file:///%s/img/icons/'
                'show-keyword-wizard.svg' % resources_path(),
                **SMALL_ICON_STYLE),
            tr('The wizard will guide you through the process of defining the '
               'keywords for that layer.')))
    message.add(
        m.Paragraph(
            tr('Aggregation is the process whereby we group the analysis results '
               'by district so that you can see how many people, roads or '
               'buildings were affected in each area. This will help you to '
               'understand where the most critical needs are.  Aggregation is '
               'optional in InaSAFE - if you do not use aggregation, the entire '
               'analysis area will be used for the data summaries. Typically '
               'aggregation layers in InaSAFE have the name of the district or '
               'reporting area as attributes. It is also possible to use extended '
               'attributes to indicate the ratio of men and women; youth, adults '
               'and elderly living in each area. Where these are provided and the '
               'exposure layer is population, InaSAFE will provide a demographic '
               'breakdown per aggregation area indicating how many men, women, etc. '
               'were probably affected in that area.')))

    header = m.Heading(tr('The results area'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('After running an analysis, the question area is hidden to maximise '
               'the amount of space allocated to the results area. You can '
               're-open the question area at any time by pressing the \'show '
               'question form\' button.')))

    message.add(
        m.Paragraph(
            tr('The results area is used to display various useful feedback items to '
               'the user. Once an impact scenario has been run, a summary table will '
               'be shown.')))

    message.add(
        m.Paragraph(
            tr('If you select an impact layer (i.e. a layer that was produced using '
               'an InaSAFE Impact Function), in the QGIS layers list, this summary '
               'will also be displayed in the results area. When you select a hazard '
               'or exposure layer in the QGIS layers list, the keywords for that '
               'layer will be shown in the results area, making it easy to '
               'understand what metadata exists for that layer.')))

    message.add(
        m.Paragraph(
            tr('The results area is also used to display status information. For '
               'example, during the analysis process, the status area will display '
               'notes about each step in the analysis process. The \'Run\' '
               'button will be activated when both a valid hazard and valid exposure '
               'layer have been added in QGIS.')))

    message.add(
        m.Paragraph(
            tr('Finally, the results area is also used to display any error messages '
               'so that you can see what went wrong and why. You may need to '
               'scroll down to view the message completely to see all of the error '
               'message details.')))

    message.add(
        m.Paragraph(
            tr('After running the impact scenario calculation, the question is '
               'automatically hidden to make the results area as large as possible. '
               'If you want to see what the question used in the analysis was, click '
               'on the \'Show question form\' button at the top of the results area.'
               )))

    message.add(
        m.Paragraph(
            tr('If you want to hide the question area again to have more space to '
               'display the results, click on the layer you just calculated '
               'with InaSAFE in the Layers list of QGIS to make it active.')))

    header = m.Heading(tr('The buttons area'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr('The buttons area contains four buttons:')))
    bullets = m.BulletedList()
    bullets.add(
        m.Text(
            m.ImportantText(tr('Help')),
            tr('- click on this if you need context help, such as the document '
               'you are reading right now!')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('About')),
            tr('- click on this to see short credits for the InaSAFE project.')
        ))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Print')),
            tr('... - click on this if you wish to create a pdf of your '
               'impact scenario project or generate a report to open in '
               'composer for further tuning. An impact layer must be active '
               'before the \'Print\' button will be enabled.')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('Run')),
            tr('- this button is enabled when the combination of hazard and '
               'exposure selected in the questions area\'s drop down menus will '
               'allow you to run a scenario.')))
    message.add(bullets)

    header = m.Heading(tr('Data conversions'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('When running a scenario, the data being used needs to be processed '
               'into a state where it is acceptable for use by InaSAFE. '
               'In particular it should be noted that:')))

    bullets = m.BulletedList()
    bullets.add(
        tr('Remote datasets will be copied locally before processing.'))
    bullets.add(
        m.Text(
            tr('All datasets will be clipped to the behaviours defined in the '
               'analysis extents dialog if you do not use an aggregation layer.'
               ),
            m.Image(
                'file:///%s/img/icons/'
                'set-extents-tool.svg' % resources_path(),
                **SMALL_ICON_STYLE)))
    bullets.add(
        m.Text(
            tr('You can visualise the area that will be used for the analysis '
               'by enabling the "Toggle Scenario Outlines" tool. When this tool '
               'is enabled, a line (green by default) will be drawn around the '
               'outermost boundary of the analysis area.'),
            m.Image(
                'file:///%s/img/icons/'
                'toggle-rubber-bands.svg' % resources_path(),
                **SMALL_ICON_STYLE)))
    bullets.add(
        m.Text(
            tr('When you have selected an aggregation layer the analysis area '
               'will be the outline of the aggregation layer. If you select one '
               'or more polygons in the aggregation layer (by using the QGIS '
               'feature selection tools), the analysis boundary will be reduced '
               'to just the outline of these selected polygons. If the "Toggle '
               'Scenario Outlines" tool is enabled, the preview of the effective '
               'analysis area will be updated to reflect the selected features.'
               ), ))
    bullets.add(
        tr('All clipped datasets will be converted (reprojected) to the '
           'Coordinate Reference System of the exposure layer '
           'before analysis.'))
    message.add(bullets)

    header = m.Heading(tr('Generating impact reports'), **INFO_STYLE)
    message.add(header)

    message.add(
        m.Paragraph(
            tr('When the impact analysis has completed you may want to generate a '
               'report. Usually the \'Print...\'  button will be enabled immediately '
               'after analysis. Selecting an InaSAFE impact layer in QGIS Layers '
               'panel will also enable it.')))

    # This adds the help content of the print dialog
    message.add(report())
    return message
Пример #22
0
def impact_attribution(keywords, inasafe_flag=False):
    """Make a little table for attribution of data sources used in impact.

    :param keywords: A keywords dict for an impact layer.
    :type keywords: dict

    :param inasafe_flag: bool - whether to show a little InaSAFE promotional
        text in the attribution output. Defaults to False.

    :returns: An html snippet containing attribution information for the impact
        layer. If no keywords are present or no appropriate keywords are
        present, None is returned.
    :rtype: safe.messaging.Message
    """
    if keywords is None:
        return None

    join_words = ' - %s ' % tr('sourced from')
    analysis_details = tr('Analysis details')
    hazard_details = tr('Hazard details')
    hazard_title_keywords = 'hazard_title'
    hazard_source_keywords = 'hazard_source'
    exposure_details = tr('Exposure details')
    exposure_title_keywords = 'exposure_title'
    exposure_source_keyword = 'exposure_source'

    if hazard_title_keywords in keywords:
        hazard_title = tr(keywords[hazard_title_keywords])
    else:
        hazard_title = tr('Hazard layer')

    if hazard_source_keywords in keywords:
        hazard_source = tr(keywords[hazard_source_keywords])
    else:
        hazard_source = tr('an unknown source')

    if exposure_title_keywords in keywords:
        exposure_title = keywords[exposure_title_keywords]
    else:
        exposure_title = tr('Exposure layer')

    if exposure_source_keyword in keywords:
        exposure_source = keywords[exposure_source_keyword]
    else:
        exposure_source = tr('an unknown source')

    report = m.Message()
    report.add(m.Heading(analysis_details, **INFO_STYLE))
    report.add(hazard_details)
    report.add(m.Paragraph(hazard_title, join_words, hazard_source))

    report.add(exposure_details)
    report.add(m.Paragraph(exposure_title, join_words, exposure_source))

    if inasafe_flag:
        report.add(m.Heading(tr('Software notes'), **INFO_STYLE))
        # noinspection PyUnresolvedReferences
        inasafe_phrase = tr(
            'This report was created using InaSAFE version %s. Visit '
            'http://inasafe.org to get your free copy of this software! %s'
        ) % (get_version(), disclaimer())

        report.add(m.Paragraph(m.Text(inasafe_phrase)))
    return report
Пример #23
0
    def to_message(self, keywords=None, show_header=True):
        """Format keywords as a message object.

        .. versionadded:: 3.2

        .. versionchanged:: 3.3 - default keywords to None

        The message object can then be rendered to html, plain text etc.

        :param keywords: Keywords to be converted to a message. Optional. If
            not passed then we will attempt to get keywords from self.layer
            if it is not None.
        :type keywords: dict

        :param show_header: Flag indicating if InaSAFE logo etc. should be
            added above the keywords table. Default is True.
        :type show_header: bool

        :returns: A safe message object containing a table.
        :rtype: safe.messaging.Message
        """
        if keywords is None and self.layer is not None:
            keywords = self.read_keywords(self.layer)
        # This order was determined in issue #2313
        preferred_order = [
            'title',
            'layer_purpose',
            'exposure',
            'hazard',
            'hazard_category',
            'layer_geometry',
            'layer_mode',
            'classification',
            'exposure_unit',
            'continuous_hazard_unit',
            'value_map',  # attribute values
            'thresholds',  # attribute values
            'value_maps',  # attribute values
            'inasafe_fields',
            'inasafe_default_values',
            'resample',
            'source',
            'url',
            'scale',
            'license',
            'date',
            'keyword_version'
        ]  # everything else in arbitrary order
        report = m.Message()
        if show_header:
            logo_element = m.Brand()
            report.add(logo_element)
            report.add(
                m.Heading(tr('Layer keywords:'), **styles.BLUE_LEVEL_4_STYLE))
            report.add(
                m.Text(
                    tr('The following keywords are defined for the active layer:'
                       )))

        table = m.Table(style_class='table table-condensed table-striped')
        # First render out the preferred order keywords
        for keyword in preferred_order:
            if keyword in keywords:
                value = keywords[keyword]
                row = self._keyword_to_row(keyword, value)
                keywords.pop(keyword)
                table.add(row)

        # now render out any remaining keywords in arbitrary order
        for keyword in keywords:
            value = keywords[keyword]
            row = self._keyword_to_row(keyword, value)
            table.add(row)

        # If the keywords class was instantiated with a layer object
        # we can add some context info not stored in the keywords themselves
        # but that is still useful to see...
        if self.layer:
            # First the CRS
            keyword = tr('Reference system')
            value = self.layer.crs().authid()
            row = self._keyword_to_row(keyword, value)
            table.add(row)
            # Next the data source
            keyword = tr('Layer source')
            value = self.layer.source()
            row = self._keyword_to_row(keyword, value, wrap_slash=True)
            table.add(row)

        # Finalise the report
        report.add(table)
        return report
Пример #24
0
    def _threshold_to_row(thresholds_keyword):
        """Helper to make a message row from a threshold

        We are expecting something like this:

        {
            'thresholds': {
                'structure': {
                    'ina_structure_flood_hazard_classification': {
                        'classes': {
                            'low': [1, 2],
                            'medium': [3, 4],
                            'high': [5, 6]
                        },
                        'active': True
                    },
                    'ina_structure_flood_hazard_4_class_classification':
                    {
                        'classes': {
                            'low': [1, 2],
                            'medium': [3, 4],
                            'high': [5, 6],
                            'very_high': [7, 8]
                        },
                        'active': False

                    }
                },
                'population': {
                    'ina_population_flood_hazard_classification': {
                        'classes': {
                            'low': [1, 2.5],
                            'medium': [2.5, 4.5],
                            'high': [4.5, 6]
                        },
                        'active': False
                    },
                    'ina_population_flood_hazard_4_class_classification':
                    {
                        'classes': {
                            'low': [1, 2.5],
                            'medium': [2.5, 4],
                            'high': [4, 6],
                            'very_high': [6, 8]
                        },
                        'active': True
                    }
                },
            },

        Each value is a list with exactly two element [a, b], where a <= b.

        :param thresholds_keyword: Value of the keyword to be rendered. This
            must be a string representation of a dict, or a dict.
        :type thresholds_keyword: basestring, dict

        :returns: A table to be added into a cell in the keywords table.
        :rtype: safe.messaging.items.table
        """
        if isinstance(thresholds_keyword, basestring):
            thresholds_keyword = literal_eval(thresholds_keyword)

        for k, v in thresholds_keyword.items():
            # If the v is not dictionary, it should be the old value maps.
            # To handle thresholds in the Impact Function.
            if not isinstance(v, dict):
                table = m.Table(style_class='table table-condensed')

                for key, value in thresholds_keyword.items():
                    row = m.Row()
                    name = definition(key)['name'] if definition(key) else key
                    row.add(m.Cell(m.ImportantText(name)))
                    pretty_value = tr('%s to %s' % (value[0], value[1]))
                    row.add(m.Cell(pretty_value))

                    table.add(row)
                return table

        table = m.Table(style_class='table table-condensed table-striped')

        i = 0
        for exposure_key, classifications in thresholds_keyword.items():
            i += 1
            exposure = definition(exposure_key)
            exposure_row = m.Row()
            exposure_row.add(m.Cell(m.ImportantText('Exposure')))
            exposure_row.add(m.Cell(m.Text(exposure['name'])))
            exposure_row.add(m.Cell(''))
            table.add(exposure_row)

            active_classification = None
            classification_row = m.Row()
            classification_row.add(m.Cell(m.ImportantText('Classification')))
            for classification, value in classifications.items():
                if value.get('active'):
                    active_classification = definition(classification)
                    classification_row.add(
                        m.Cell(active_classification['name']))
                    classification_row.add(m.Cell(''))
                    break

            if not active_classification:
                classification_row.add(m.Cell(tr('No classifications set.')))
                classification_row.add(m.Cell(''))
                continue

            table.add(classification_row)

            header = m.Row()
            header.add(m.Cell(tr('Class name')))
            header.add(m.Cell(tr('Minimum')))
            header.add(m.Cell(tr('Maximum')))
            table.add(header)
            classes = active_classification.get('classes')
            # Sort by value, put the lowest first
            classes = sorted(classes, key=lambda k: k['value'])
            for the_class in classes:
                threshold = classifications[
                    active_classification['key']]['classes'][the_class['key']]
                row = m.Row()
                row.add(m.Cell(the_class['name']))
                row.add(m.Cell(threshold[0]))
                row.add(m.Cell(threshold[1]))
                table.add(row)

            if i < len(thresholds_keyword):
                # Empty row
                empty_row = m.Row()
                empty_row.add(m.Cell(''))
                empty_row.add(m.Cell(''))
                table.add(empty_row)

        return table
Пример #25
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()

    message.add(m.Paragraph(tr(
        'In some cases you may wish to create a report containing the '
        'combined output of two impact functions for the same area for the '
        'same hazard, different exposures. For example You may carry out an '
        'assessment of the impact of a flood on population and on buildings '
        'and combine the results into a single report. The impact layer '
        'merge tool allows you to do this.'
    )))

    header = m.Heading(tr('Prerequisites'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'In order to use this tool, please bear in mind the following '
        'requirements:'
    )))
    bullets = m.BulletedList()
    bullets.add(tr(
        'Both impact layers should be loaded in your current project.'))
    bullets.add(tr(
        'Both impact layers should have been created for the same '
        'geographic region.'))
    bullets.add(tr(
        'The same aggregation area should be used for both assessments.'
    ))
    message.add(bullets)

    header = m.Heading(tr('Procedure'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'To use this tool, follow this procedure:'
    )))

    bullets = m.BulletedList()
    bullets.add(tr(
        'Run an impact assessment for an area using aggregation. e.g. '
        'Flood Impact on Buildings aggregated by municipal boundaries.'
    ))
    bullets.add(tr(
        'Run a second impact assessment for the same area using the same '
        'aggregation. e.g. Flood Impact on People aggregated by municipal '
        'boundaries.'))
    bullets.add(tr(
        'Open impact merge tool and select each impact layer from the pick '
        'lists provided.'
    ))
    bullets.add(tr(
        'Select the aggregation layer that was used to generate the first '
        'and second impact layer.'
    ))
    bullets.add(tr(
        'Select an output directory.'
    ))
    bullets.add(tr(
        'Check "Use customized report template" checkbox and select the '
        'report template file if you want to use your own template. Note '
        'that all the map composer components that are needed must be '
        'fulfilled.'
    ))
    bullets.add(tr(
        'Click OK to generate the per aggregation area combined summaries.'
    ))
    message.add(bullets)

    header = m.Heading(tr('Generated outputs'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'The tool will generate a PDF per aggregation area. The PDFs will be '
        'placed in the designated output directory after completion of the '
        'merge process.'
    )))

    message.add(m.Paragraph(tr(
        'In the case of impact assessments where no aggregation has been '
        'used, only a single pdf report is generated. In the case of impact '
        'assessments where aggregation has been used, one pdf is generated '
        'per aggregation area.')))

    message.add(m.Paragraph(
        m.ImportantText(tr('Note:')),
        m.Text(tr(
            'After report generation completes, the output directory will '
            'be opened automatically.'))
    ))

    header = m.Heading(tr('Using Customized Template'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'The default template report is located in '
        '/resources/qgis-composer-templates/merged-report.qpt. If that '
        'template does not satisfy your needs, you can use your own report '
        'template. Before using your own report template, make sure that '
        'your template contains all of these elements with id:'
    )))

    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('impact-map')),
        tr('- a QgsComposerMap')))
    bullets.add(m.Text(
        m.ImportantText(tr('safe-logo')),
        tr('- a QgsComposerPicture')))
    bullets.add(m.Text(
        m.ImportantText(tr('summary-report')),
        tr('- a QgsComposerLabel')))
    bullets.add(m.Text(
        m.ImportantText(tr('aggregation-area')),
        tr('- a QgsComposerLabel')))
    bullets.add(m.Text(
        m.ImportantText(tr('map-scale')),
        tr('- a QgsComposerScaleBar')))
    bullets.add(m.Text(
        m.ImportantText(tr('map-legend')),
        tr('- a QgsComposerLegend')))
    bullets.add(m.Text(
        m.ImportantText(tr('organisation-logo')),
        tr('- a QgsComposerPicture')))
    bullets.add(m.Text(
        m.ImportantText(tr('merged-report-table')),
        tr('- a QgsComposerHTML')))
    message.add(bullets)

    message.add(m.Paragraph(
        m.ImportantText(tr('Note:')),
        m.Text(tr(
            'You can arrange those elements in any position you want.'))
    ))

    message.add(m.Paragraph(tr(
        'If any of those elements does not exist on the report template, the '
        'tools will give you the information of what element is missing on '
        'the template.'
    )))

    header = m.Heading(tr('Map Template Elements'), **INFO_STYLE)
    message.add(header)

    message.add(m.Paragraph(tr(
        'In terms of value replacement, there are three groups of elements '
        'on the template:')))

    message.add(m.Paragraph(
        m.ImportantText(tr('Options driven elements')),
        tr(
            '- Elements that can be changed on InaSAFE Options tool. To '
            'change the value of these elements, please go to InaSAFE '
            'Option tools and change the value of the related field. '
            'Those elements are:')))

    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('organisation-logo')),
        tr(
            'This corresponds to the Organisation logo field in InaSAFE '
            'Option tools. If you do not fill this field, then the default '
            'one, supporters logo, will be used.')))
    bullets.add(m.Text(
        m.ImportantText(tr('disclaimer')),
        tr(
            'It corresponds to Disclaimer text field on InaSAFE Option '
            'tools. If you do not fill this field, then the default one will '
            'be used.')))

    message.add(m.Paragraph(
        m.ImportantText(tr('Elements containing tokens')),
        tr(
            '- the id of these element is not significant, only the token it '
            'contains. At render time, any of these tokens will be replaced. '
            'If you want to have a label containing value of these elements, '
            'enclose these elements with [] on a label i.e [impact-title] '
            'or [hazard-title]. Those elements are listed below:')))

    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('impact-title')),
        tr(
            'It indicates the title of two impacts. The value will be '
            '"first_impact_title and second_impact_title"')))
    bullets.add(m.Text(
        m.ImportantText(tr('hazard-title')),
        tr(
            'It indicates the hazard title used to generate the '
            'impact layer.')))

    message.add(m.Paragraph(
        m.ImportantText(tr(
            'Elements that are directly updated by the renderer')),
        tr(
            '- all of these elements below are generated automatically by '
            'the tool.')))

    bullets = m.BulletedList()
    bullets.add(m.Text(
        m.ImportantText(tr('impact-map')),
        tr(
            ' - contains the map of two impact layers.')))
    bullets.add(m.Text(
        m.ImportantText(tr('summary-report')),
        tr(
            ' - contains the summary of the impact from two impact layers.')))
    bullets.add(m.Text(
        m.ImportantText(tr('aggregation-area')),
        tr(
            ' - contains the name of the aggregation area.')))
    bullets.add(m.Text(
        m.ImportantText(tr('map-scale')),
        tr(
            ' - indicates the scale of the map. To work with any layer '
            'projection preferences, we encourage you to use a numeric '
            'scale bar.')))
    bullets.add(m.Text(
        m.ImportantText(tr('map-legend')),
        tr(
            ' - shows the legend of merged impact layers. The map legend '
            'on default template is set to have two columns showing each '
            'impact layer legend.')))
    bullets.add(m.Text(
        m.ImportantText(tr('merged-report-table')),
        tr(
            '- contains the detailed information of each impact.')))
    message.add(bullets)

    return message
Пример #26
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    paragraph = m.Paragraph(m.Image('file:///%s/img/screenshots/'
                                    'minimum-needs-screenshot.png' %
                                    resources_path()),
                            style_class='text-center')
    message.add(paragraph)
    message.add(
        m.Paragraph(
            tr('During and after a disaster, providing for the basic human minimum '
               'needs of food, water, hygiene and shelter is an important element of '
               'your contingency plan. InaSAFE has a customisable minimum needs '
               'system that allows you to define country or region specific '
               'requirements for compiling a needs report where the exposure '
               'layer represents population.')))
    message.add(
        m.Paragraph(
            tr('By default InaSAFE uses minimum needs defined for Indonesia - '
               'and ships with additional profiles for the Philippines and Tanzania. '
               'You can customise these or add your own region-specific profiles too.'
               )))
    message.add(
        m.Paragraph(
            tr('Minimum needs are grouped into regional or linguistic \'profiles\'. '
               'The default profile is \'BNPB_en\' - the english profile for the '
               'national disaster agency in Indonesia. '
               'You will see that this profile defines requirements for displaced '
               'persons in terms of Rice, Drinking Water, Clean Water (for bathing '
               'etc.), Family Kits (with personal hygiene items) and provision of '
               'toilets.')))
    message.add(
        m.Paragraph(
            tr('Each item in the profile can be customised or removed. For example '
               'selecting the first item in the list and then clicking on the '
               '\'pencil\' icon will show the details of how it was defined. '
               'If you scroll up and down in the panel you will see that for each '
               'item, you can set a name, description, units (in singular, '
               'plural and abbreviated forms), specify maxima and minima for the '
               'quantity of item allowed, a default and a frequency. You would use '
               'the maxima and minima to ensure that disaster managers never '
               'allocate amounts that will not be sufficient for human livelihood, '
               'and also that will not overtax the logistics operation for those '
               'providing humanitarian relief.')))
    message.add(
        m.Paragraph(
            tr('The final item in the item configuration is the \'readable '
               'sentence\' which bears special discussion. Using a simple system of '
               'tokens you can construct a sentence that will be used in the '
               'generated needs report.')))
    message.add(m.Heading(tr('Minimum needs profiles'), **INFO_STYLE))
    message.add(
        m.Paragraph(
            tr('A profile is a collection of resources that define the minimum needs '
               'for a particular country or region. Typically a profile should be '
               'based on a regional, national or international standard. The '
               'actual definition of which resources are needed in a given '
               'profile is dependent on the local conditions and customs for the '
               'area where the contingency plan is being devised.')))
    message.add(
        m.Paragraph(
            tr('For example in the middle east, rice is a staple food whereas in '
               'South Africa, maize meal is a staple food and thus the contingency '
               'planning should take these localised needs into account.')))

    message.add(m.Heading(tr('Minimum needs resources'), **INFO_STYLE))
    message.add(
        m.Paragraph(
            tr('Each item in a minimum needs profile is a resource. Each resource '
               'is described as a simple natural language sentence e.g.:')))

    message.add(
        m.EmphasizedText(
            tr('Each person should be provided with 2.8 kilograms of Rice weekly.'
               )))
    message.add(
        m.Paragraph(
            tr('By clicking on a resource entry in the profile window, and then '
               'clicking the black pencil icon you will be able to edit the '
               'resource using the resource editor. Alternatively you can create a '
               'new resource for a profile by clicking on the black + icon in '
               'the profile manager. You can also remove any resource from a '
               'profile using the - icon in the profile manager.')))

    message.add(m.Heading(tr('Resource Editor'), **INFO_STYLE))
    message.add(
        m.Paragraph(
            tr('When switching to edit or add resource mode, the minimum needs '
               'manager will be updated to show the resource editor. Each '
               'resource is described in terms of:')))
    bullets = m.BulletedList()
    bullets.add(
        m.Text(m.ImportantText(tr('resource name')), tr(' - e.g. Rice')))
    bullets.add(
        m.Text(m.ImportantText(tr('a description of the resource')),
               tr(' - e.g. Basic food')))
    bullets.add(
        m.Text(m.ImportantText(tr('unit in which the resource is provided')),
               tr(' - e.g. kilogram')))
    bullets.add(
        m.Text(m.ImportantText(tr('pluralised form of the units')),
               tr(' - e.g. kilograms')))
    bullets.add(
        m.Text(m.ImportantText(tr('abbreviation for the unit')),
               tr(' - e.g. kg')))
    bullets.add(
        m.Text(
            m.ImportantText(tr('the default allocation for the resource')),
            tr(' - e.g. 2.8. This number can be overridden on a '
               'per-analysis basis')))
    bullets.add(
        m.Text(
            m.ImportantText(
                tr('minimum allowed which is used to prevent allocating')),
            tr(' - e.g. no drinking water to displaced persons')))
    bullets.add(
        m.ImportantText(
            tr('maximum allowed which is used to set a sensible upper '
               'limit for the resource')))
    bullets.add(
        m.ImportantText(
            tr('a readable sentence which is used to compile the '
               'sentence describing the resource in reports.')))
    message.add(bullets)

    message.add(
        m.Paragraph(
            tr('These parameters are probably all fairly self explanatory, but '
               'the readable sentence probably needs further detail. The '
               'sentence is compiled using a simple keyword token replacement '
               'system. The following tokens can be used:')))

    bullets = m.BulletedList()
    bullets.add(m.Text('{{ Default }}'))
    bullets.add(m.Text('{{ Unit }}'))
    bullets.add(m.Text('{{ Units }}'))
    bullets.add(m.Text('{{ Unit abbreviation }}'))
    bullets.add(m.Text('{{ Resource name }}'))
    bullets.add(m.Text('{{ Frequency }}'))
    bullets.add(m.Text('{{ Minimum allowed }}'))
    bullets.add(m.Text('{{ Maximum allowed }}'))
    message.add(bullets)

    message.add(
        m.Paragraph(
            tr('When the token is placed in the sentence it will be replaced with '
               'the actual value at report generation time. This contrived example '
               'shows a tokenised sentence that includes all possible keywords:'
               )))

    message.add(
        m.EmphasizedText(
            tr('A displaced person should be provided with {{ %s }} '
               '{{ %s }}/{{ %s }}/{{ %s }} of {{ %s }}. Though no less than {{ %s }} '
               'and no more than {{ %s }}. This should be provided {{ %s }}.' %
               ('Default', 'Unit', 'Units', 'Unit abbreviation',
                'Resource name', 'Minimum allowed', 'Maximum allowed',
                'Frequency'))))
    message.add(
        m.Paragraph(tr('Would generate a human readable sentence like this:')))

    message.add(
        m.ImportantText(
            tr('A displaced person should be provided with 2.8 kilogram/kilograms/kg '
               'of rice. Though no less than 0 and no more than 100. This should '
               'be provided daily.')))
    message.add(
        m.Paragraph(
            tr('Once you have populated the resource elements, click the Save '
               'resource button to return to the profile view. You will see the '
               'new resource added in the profile\'s resource list.')))

    message.add(m.Heading(tr('Managing profiles'), **INFO_STYLE))
    message.add(
        m.Paragraph(
            tr('In addition to the profiles that come as standard with InaSAFE, you '
               'can create new ones, either from scratch, or based on an existing '
               'one (which you can then modify).')))
    message.add(
        m.Paragraph(
            tr('Use the New button to create new profile. When prompted, give your '
               'profile a name e.g. \'JakartaProfile\'.')))
    message.add(
        m.Paragraph(
            tr('Note: The profile must be saved in your home directory under '
               '(QGIS profile path)/minimum_needs in order '
               'for InaSAFE to successfully '
               'detect it.')))
    message.add(
        m.Paragraph(
            tr('An alternative way to create a new profile is to use the Save as to '
               'clone an existing profile. The clone profile can then be edited '
               'according to your specific needs.')))
    message.add(m.Heading(tr('Active profile'), **INFO_STYLE))
    message.add(
        m.Paragraph(
            tr('It is important to note, that which ever profile you select in the '
               'Profile pick list, will be considered active and will be used as '
               'the basis for all minimum needs analysis. You need to restart '
               'QGIS before the changed profile become active.')))
    return message