inasafe/inasafe

View on GitHub
safe/report/expressions/html_report.py

Summary

Maintainability
F
2 wks
Test Coverage
# coding=utf-8

"""QGIS Expressions which are available in the QGIS GUI interface."""

from os.path import dirname, join, exists
from xml.etree import ElementTree as ET

from qgis.core import (
    qgsfunction,
    QgsExpressionContextUtils,
    QgsProject,
    QgsLayerTreeGroup,
    QgsLayerTreeLayer)

from safe.definitions.constants import MULTI_EXPOSURE_ANALYSIS_FLAG
from safe.definitions.exposure import (
    exposure_population,
    exposure_road,
    exposure_structure,
    exposure_place,
    exposure_land_cover)
from safe.definitions.extra_keywords import extra_keyword_analysis_type
from safe.definitions.provenance import provenance_layer_analysis_impacted
from safe.definitions.reports.components import (
    analysis_question_component,
    general_report_component,
    mmi_detail_component,
    analysis_detail_component,
    action_checklist_component,
    notes_assumptions_component,
    minimum_needs_component,
    aggregation_result_component,
    aggregation_postprocessors_component,
    analysis_provenance_details_simplified_component)
from safe.utilities.i18n import tr
from safe.utilities.keyword_io import KeywordIO
from safe.utilities.utilities import generate_expression_help

__copyright__ = "Copyright 2017, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "info@inasafe.org"
__revision__ = '$Format:%H$'

group = tr('InaSAFE - HTML Elements')
container_format = (
    u'<div class="container">'
    u'   {section_content}'
    u'</div>'
)


def get_analysis_dir(exposure_key=None):
    """Retrieve an output directory of an analysis/ImpactFunction from a
    multi exposure analysis/ImpactFunction based on exposure type.

    :param exposure_key: An exposure keyword.
    :type exposure_key: str

    :return: A directory contains analysis outputs.
    :rtype: str
    """
    keyword_io = KeywordIO()
    layer_tree_root = QgsProject.instance().layerTreeRoot()
    all_groups = [
        child for child in layer_tree_root.children() if (
            isinstance(child, QgsLayerTreeGroup))]
    multi_exposure_group = None
    for group in all_groups:
        if group.customProperty(MULTI_EXPOSURE_ANALYSIS_FLAG):
            multi_exposure_group = group
            break

    if multi_exposure_group:
        multi_exposure_tree_layers = [
            child for child in multi_exposure_group.children() if (
                isinstance(child, QgsLayerTreeLayer))]
        exposure_groups = [
            child for child in multi_exposure_group.children() if (
                isinstance(child, QgsLayerTreeGroup))]

        def get_report_ready_layer(tree_layers):
            """Get a layer which has a report inn its directory.

            :param tree_layers: A list of tree layer nodes (QgsLayerTreeLayer)
            :type tree_layers: list

            :return: A vector layer
            :rtype: QgsMapLayer
            """
            for tree_layer in tree_layers:
                layer = tree_layer.layer()
                keywords = keyword_io.read_keywords(layer)
                extra_keywords_found = keywords.get('extra_keywords')
                provenance = keywords.get('provenance_data')
                if provenance:
                    exposure_keywords = provenance.get('exposure_keywords', {})
                    exposure_key_found = exposure_keywords.get('exposure')
                    if exposure_key_found and (
                            exposure_key == exposure_key_found):
                        return layer
                if not exposure_key and extra_keywords_found and (
                        extra_keywords_found[
                            extra_keyword_analysis_type['key']] == (
                                MULTI_EXPOSURE_ANALYSIS_FLAG)):
                    return layer
            return None

        layer = get_report_ready_layer(multi_exposure_tree_layers)

        if not layer:
            for exposure_group in exposure_groups:
                tree_layers = [
                    child for child in exposure_group.children() if (
                        isinstance(child, QgsLayerTreeLayer))]
                layer = get_report_ready_layer(tree_layers)
                if layer:
                    break

        if layer:
            return dirname(layer.source())

    return None


def get_impact_report_as_string(analysis_dir):
    """Retrieve an html string of table report (impact-report-output.html).

    :param analysis_dir: Directory of where the report located.
    :type analysis_dir: str

    :return: HTML string of the report.
    :rtype: str
    """
    html_report_products = [
        'impact-report-output.html',
        'multi-exposure-impact-report-output.html']
    output_dir_path = join(analysis_dir, 'output')

    for html_report_product in html_report_products:
        table_report_path = join(output_dir_path, html_report_product)
        if exists(table_report_path):
            break
        table_report_path = None

    if not table_report_path:
        return None

    # We can display an impact report.
    # We need to open the file in UTF-8, the HTML may have some accents
    with open(table_report_path, 'r', encoding='utf-8') as table_report_file:
        report = table_report_file.read()
        return report


def get_report_section(
        html_report, component_id, container_wrapper_format=container_format):
    """Get specific report section from InaSAFE analysis summary report.

    :param html_report: The html report.
    :type html_report: basestring

    :param component_id: The component key.
    :type component_id: str

    :param container_wrapper_format: A string format for wrapping the section.
    :type container_wrapper_format: basestring

    :return: Requested report section as an html.
    :rtype: basestring
    """
    no_element_error = tr('No element match the tag or component id.')

    root_element, dict_of_elements = ET.XMLID(html_report)
    section_element = dict_of_elements.get(component_id)

    if section_element:
        requested_section = container_wrapper_format.format(
            section_content=str(ET.tostring(section_element)))
        return requested_section
    else:
        return no_element_error


##
# For QGIS < 2.18.13 and QGIS < 2.14.19, docstrings are used in the QGIS GUI
# in the Expression dialog and also in the InaSAFE Help dialog.
#
# For QGIS >= 2.18.13, QGIS >= 2.14.19 and QGIS 3, the translated variable will
# be used in QGIS.
# help_text is used for QGIS 2.18 and 2.14
# helpText is used for QGIS 3 : https://github.com/qgis/QGIS/pull/5059
##

description = tr('Retrieve default InaSAFE HTML resources (style and script) '
                 'from InaSAFE analysis report of current selected analysis.')
examples = {
    'inasafe_html_resources()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def default_inasafe_html_resources(feature, parent):
    """Retrieve default InaSAFE HTML resources (style and script).
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report,
        component_id='inasafe-html-resources',
        container_wrapper_format=u'{section_content}')

    return requested_html_report


description = tr('Retrieve an HTML table report of current selected analysis.')
examples = {
    'analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def analysis_summary_report(feature, parent):
    """Retrieve an HTML table report of current selected analysis.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope(
        QgsProject.instance())
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    return get_impact_report_as_string(analysis_dir)


description = tr('Retrieve the analysis question section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'analysis_question_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def analysis_question_report(feature, parent):
    """Retrieve the analysis question section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=analysis_question_component['key'])

    return requested_html_report


description = tr('Retrieve the general report section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'general_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def general_report(feature, parent):
    """Retrieve the general report section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=general_report_component['key'])

    return requested_html_report


description = tr('Retrieve the mmi detail section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'mmi_detail_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def mmi_detail_report(feature, parent):
    """Retrieve the mmi detail section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=mmi_detail_component['key'])

    return requested_html_report


description = tr('Retrieve the analysis detail section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'analysis_detail_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def analysis_detail_report(feature, parent):
    """Retrieve the analysis detail section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=analysis_detail_component['key'])

    return requested_html_report


description = tr('Retrieve the action checklist section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'action_checklist_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def action_checklist_report(feature, parent):
    """Retrieve the action checklist section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=action_checklist_component['key'])

    return requested_html_report


description = tr('Retrieve the notes assumptions section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'notes_assumptions_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def notes_assumptions_report(feature, parent):
    """Retrieve the notes assumptions section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=notes_assumptions_component['key'])

    return requested_html_report


description = tr('Retrieve the minimum needs section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'minimum_needs_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def minimum_needs_report(feature, parent):
    """Retrieve the minimum needs section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=minimum_needs_component['key'])

    return requested_html_report


description = tr('Retrieve the aggregation result section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'aggregation_result_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def aggregation_result_report(feature, parent):
    """Retrieve the aggregation result section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report, component_id=aggregation_result_component['key'])

    return requested_html_report


description = tr('Retrieve the aggregation postprocessors section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'aggregation_postprocessors_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def aggregation_postprocessors_report(feature, parent):
    """Retrieve the aggregation postprocessors section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report,
        component_id=aggregation_postprocessors_component['key'])

    return requested_html_report


description = tr('Retrieve the analysis provenance details section from '
                 'InaSAFE analysis report of current selected analysis.')
examples = {
    'analysis_provenance_details_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def analysis_provenance_details_report(feature, parent):
    """Retrieve the analysis provenance details section from InaSAFE report.
    """
    _ = feature, parent  # NOQA
    project_context_scope = QgsExpressionContextUtils.projectScope()
    key = provenance_layer_analysis_impacted['provenance_key']
    if not project_context_scope.hasVariable(key):
        return None

    analysis_dir = dirname(project_context_scope.variable(key))
    complete_html_report = get_impact_report_as_string(analysis_dir)

    requested_html_report = get_report_section(
        complete_html_report,
        component_id=analysis_provenance_details_simplified_component['key'])

    return requested_html_report


description = tr('Retrieve an HTML population analysis table report from '
                 'a multi exposure analysis.')
examples = {
    'population_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def population_analysis_summary_report(feature, parent):
    """Retrieve an HTML population analysis table report from a multi exposure
    analysis.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir(exposure_population['key'])
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None


description = tr('Retrieve an HTML road analysis table report from '
                 'a multi exposure analysis.')
examples = {
    'road_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def road_analysis_summary_report(feature, parent):
    """Retrieve an HTML road analysis table report from a multi exposure
    analysis.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir(exposure_road['key'])
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None


description = tr('Retrieve an HTML structure analysis table report from '
                 'a multi exposure analysis.')
examples = {
    'structure_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def structure_analysis_summary_report(feature, parent):
    """Retrieve an HTML structure analysis table report from a multi exposure
    analysis.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir(exposure_structure['key'])
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None


description = tr('Retrieve an HTML place analysis table report from '
                 'a multi exposure analysis.')
examples = {
    'place_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def place_analysis_summary_report(feature, parent):
    """Retrieve an HTML place analysis table report from a multi exposure
    analysis.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir(exposure_place['key'])
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None


description = tr('Retrieve an HTML land cover analysis table report from '
                 'a multi exposure analysis.')
examples = {
    'land_cover_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def land_cover_analysis_summary_report(feature, parent):
    """Retrieve an HTML land cover analysis table report from a multi exposure
    analysis.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir(exposure_land_cover['key'])
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None


description = tr('Retrieve an HTML multi exposure analysis table report.')
examples = {
    'multi_exposure_analysis_summary_report()': None
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group=group, usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def multi_exposure_analysis_summary_report(feature, parent):
    """Retrieve an HTML multi exposure analysis table report.
    """
    _ = feature, parent  # NOQA
    analysis_dir = get_analysis_dir()
    if analysis_dir:
        return get_impact_report_as_string(analysis_dir)
    return None