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
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)
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
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
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
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
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))
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
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
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
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
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
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()
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
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
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
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
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)
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
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
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
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
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
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
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
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