def analysis_provenance_details_report_extractor(
        impact_report, component_metadata):
    """Extracting the main provenance details to its own report.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.1
    """
    context = {}
    extra_args = component_metadata.extra_args

    components_list = resolve_from_dictionary(
        extra_args, 'components_list')

    context['brand_logo'] = resource_url(
        resources_path('img', 'logos', 'inasafe-logo-white.png'))
    for key, component in components_list.iteritems():
        context[key] = jinja2_output_as_string(
            impact_report, component['key'])

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir

    return context
Exemplo n.º 2
0
def action_checklist_report_extractor(impact_report, component_metadata):
    """Extracting action checklist of the impact layer to its own report.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.1
    """
    context = {}
    extra_args = component_metadata.extra_args

    components_list = resolve_from_dictionary(
        extra_args, 'components_list')

    context['brand_logo'] = resource_url(
        resources_path('img', 'logos', 'inasafe-logo-white.png'))
    for key, component in components_list.iteritems():
        context[key] = jinja2_output_as_string(
            impact_report, component['key'])

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir

    return context
Exemplo n.º 3
0
    def assert_compare_file_control(self, control_path, actual_path):
        """Helper to compare file."""
        current_directory = safe_dir(sub_dir='../resources')
        context = {'current_directory': current_directory}
        with open(control_path) as control_file:
            template_string = control_file.read()
            template = Template(template_string)
            control_string = template.render(context).strip()

        with io.open(actual_path, encoding='utf-8') as actual_file:
            actual_string = actual_file.read().strip()
            self.assertEquals(control_string, actual_string)
Exemplo n.º 4
0
    def test_qgis_translations(self):
        """Test for qgis translations."""
        file_path = safe_dir('i18n/inasafe_id.qm')
        translator = QTranslator()
        translator.load(file_path)
        QCoreApplication.installTranslator(translator)

        expected_message = (
            'Tidak ada informasi gaya yang ditemukan pada lapisan %s')
        real_message = QCoreApplication.translate(
            '@default', 'No styleInfo was found for layer %s')
        message = 'expected %s but got %s' % (expected_message, real_message)
        self.assertEqual(expected_message, real_message, message)
Exemplo n.º 5
0
    def test_qgis_translations(self):
        """Test for qgis translations."""
        file_path = safe_dir('i18n/inasafe_id.qm')
        translator = QTranslator()
        translator.load(file_path)
        QCoreApplication.installTranslator(translator)

        expected_message = (
            'Tidak ada informasi gaya yang ditemukan pada lapisan %s')
        real_message = QCoreApplication.translate(
            '@default', 'No styleInfo was found for layer %s')
        message = 'expected %s but got %s' % (expected_message, real_message)
        self.assertEqual(expected_message, real_message, message)
Exemplo n.º 6
0
def population_chart_legend_extractor(impact_report, component_metadata):
    """Extracting legend of population chart.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.2
    """
    context = {}

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir

    """Population Charts"""

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

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

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

    return context
Exemplo n.º 7
0
    def test_map_report_component(self):
        """Test for map report component."""
        # Default qpt
        component = map_report_component(report_a4_blue, '.')
        self.assertDictEqual(component, report_a4_blue)

        # Custom qpt
        target_directory = mkdtemp()
        default_qpt = join(safe_dir('..'), 'resources',
                           'qgis-composer-templates', 'a4-portrait-blue.qpt')
        if exists(default_qpt):
            target_path = join(target_directory, split(default_qpt)[1])
            shutil.copy2(default_qpt, target_path)

        component = map_report_component(report_a4_blue, target_directory)
        self.assertTrue(component != report_a4_blue)
Exemplo n.º 8
0
    def test_update_template_component(self):
        """Test for custom template component."""
        # Default qpt
        component = update_template_component(map_report, '.')
        self.assertDictEqual(component, map_report)

        # Custom qpt
        target_directory = mkdtemp()
        default_qpt = join(safe_dir('..'), 'resources',
                           'qgis-composer-templates',
                           'inasafe-map-report-portrait.qpt')
        if exists(default_qpt):
            target_path = join(target_directory, split(default_qpt)[1])
            shutil.copy2(default_qpt, target_path)

        component = update_template_component(map_report, target_directory)
        self.assertTrue(component != map_report)
Exemplo n.º 9
0
def infographic_layout_extractor(impact_report, component_metadata):
    """Extracting infographic result and format it with a layout.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {}
    extra_args = component_metadata.extra_args

    infographics = resolve_from_dictionary(extra_args, ['infographics'])
    provenance = impact_report.impact_function.provenance

    infographic_result = ''

    for component_key in infographics:
        result = jinja2_output_as_string(impact_report, component_key)
        if result:
            infographic_result += result

    if not infographic_result:
        return context

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir
    context['infographic_content'] = infographic_result
    version = provenance['inasafe_version']
    start_datetime = provenance['start_datetime']
    date = start_datetime.strftime('%Y-%m-%d')
    time = start_datetime.strftime('%H:%M')
    footer_format = resolve_from_dictionary(extra_args, 'footer_format')
    context['footer'] = footer_format.format(version=version,
                                             analysis_date=date,
                                             analysis_time=time)
    return context
Exemplo n.º 10
0
def infographic_layout_extractor(impact_report, component_metadata):
    """Extracting infographic result and format it with a layout.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {}
    extra_args = component_metadata.extra_args

    infographics = resolve_from_dictionary(extra_args, ['infographics'])
    provenance = impact_report.impact_function.provenance

    infographic_result = ''

    for component_key in infographics:
        result = jinja2_output_as_string(impact_report, component_key)
        if result:
            infographic_result += result

    if not infographic_result:
        return context

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir
    context['infographic_content'] = infographic_result
    version = provenance['inasafe_version']
    start_datetime = provenance['start_datetime']
    date = start_datetime.strftime('%Y-%m-%d')
    time = start_datetime.strftime('%H:%M')
    footer_format = resolve_from_dictionary(extra_args, 'footer_format')
    context['footer'] = footer_format.format(
        version=version, analysis_date=date, analysis_time=time)
    return context
Exemplo n.º 11
0
    def test_map_report_component(self):
        """Test for map report component."""
        # Default qpt
        component = map_report_component(report_a4_blue, '.')
        self.assertDictEqual(component, report_a4_blue)

        # Custom qpt
        target_directory = mkdtemp()
        default_qpt = join(
            safe_dir('..'),
            'resources',
            'qgis-composer-templates',
            'a4-portrait-blue.qpt')
        if exists(default_qpt):
            target_path = join(target_directory, split(default_qpt)[1])
            shutil.copy2(default_qpt, target_path)

        component = map_report_component(report_a4_blue, target_directory)
        self.assertTrue(component != report_a4_blue)
Exemplo n.º 12
0
def impact_table_extractor(impact_report, component_metadata):
    """Extracting impact summary of the impact layer.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {}
    extra_args = component_metadata.extra_args
    debug_mode = impact_report.impact_function.debug_mode

    components_list = resolve_from_dictionary(
        extra_args, 'components_list')

    # TODO: Decide either to use it or not
    if not debug_mode:
        # only show experimental MMI Detail when in debug mode
        components_list.pop('mmi_detail', None)

    context['brand_logo'] = resource_url(
        resources_path('img', 'logos', 'inasafe-logo-white.png'))
    for key, component in components_list.iteritems():
        context[key] = jinja2_output_as_string(
            impact_report, component['key'])

    resources_dir = safe_dir(sub_dir='../resources')
    context['inasafe_resources_base_dir'] = resources_dir

    return context
Exemplo n.º 13
0
    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_component
]

# Standard HTML output for impact report
standard_impact_report_metadata_html = {
    'key': 'analysis-result-html',
    'name': 'analysis-result-html',
    'template_folder': safe_dir(sub_dir='../resources/report-templates/'),
    'components': impact_report_component_metadata + [
        population_chart_svg_component,
        population_chart_png_component,
        population_infographic_component,
        # Infographic Layout HTML
        {
            'key': 'infographic-layout',
            'type': jinja2_component_type,
            'processor': jinja2_renderer,
            'extractor': infographic_layout_extractor,
            'output_format': Jinja2ComponentsMetadata.OutputFormat.File,
            'output_path': 'infographic.html',
            'extra_args': {
                'infographics': [population_infographic_component['key']],
                'footer_format': tr(
Exemplo n.º 14
0
def get_qgis_app(requested_locale='en_US', qsetting=''):
    """ Start one QGIS application to test against.

    :param locale: The locale we want the qgis to launch with.
    :type locale: str

    :param qsetting: String to specify the QSettings. By default,
        use empty string.
    :type qsetting: str

    :returns: Handle to QGIS app, canvas, iface and parent. If there are any
        errors the tuple members will be returned as None.
    :rtype: (QgsApplication, CANVAS, IFACE, PARENT)

    If QGIS is already running the handle to that app will be returned.
    """
    global QGIS_APP, PARENT, IFACE, CANVAS  # pylint: disable=W0603

    from qgis.PyQt.QtCore import QSettings
    if qsetting:
        settings = QSettings(qsetting)
    else:
        settings = QSettings()

    default_user_directory = setting('defaultUserDirectory')
    current_locale = general_setting(
        'locale/userLocale', default='en_US', qsettings=settings)
    locale_match = current_locale == requested_locale

    if iface and locale_match:
        from qgis.core import QgsApplication
        QGIS_APP = QgsApplication
        CANVAS = iface.mapCanvas()
        PARENT = iface.mainWindow()
        IFACE = iface

    try:
        from qgis.core import QgsApplication
        from qgis.gui import QgsMapCanvas  # pylint: disable=no-name-in-module
        # noinspection PyPackageRequirements
        from qgis.PyQt import QtWidgets, QtCore  # pylint: disable=W0621
        # noinspection PyPackageRequirements
        from qgis.PyQt.QtCore import QCoreApplication, QSettings
        from safe.test.qgis_interface import QgisInterface
    except ImportError:
        return None, None, None, None

    if qsetting:
        settings = QSettings(qsetting)
    else:
        settings = QSettings()

    if not QGIS_APP:
        gui_flag = True  # All test will run qgis in gui mode

        # AG: For testing purposes, we use our own configuration file
        # instead of using the QGIS apps conf of the host
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setOrganizationName('QGIS')
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setOrganizationDomain('qgis.org')
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setApplicationName('QGIS2InaSAFETesting')

        # We disabled message bars for now for extent selector as
        # we don't have a main window to show them in TS - version 3.2
        set_setting('show_extent_warnings', False, settings)
        set_setting('showRubberBands', True, settings)
        set_setting('show_extent_confirmations', False, settings)
        set_setting('analysis_extents_mode', HAZARD_EXPOSURE, settings)
        if default_user_directory:
            set_setting(
                'defaultUserDirectory', default_user_directory, settings)

        # noinspection PyPep8Naming
        if 'argv' in dir(sys):
            QGIS_APP = QgsApplication([p.encode('utf-8')
                                       for p in sys.argv], gui_flag)
        else:
            QGIS_APP = QgsApplication([], gui_flag)

        # Make sure QGIS_PREFIX_PATH is set in your env if needed!
        QGIS_APP.initQgis()

        # Initialize processing
        processing.Processing.initialize()

        s = QGIS_APP.showSettings()
        LOGGER.debug(s)

    if not locale_match:
        """Setup internationalisation for the plugin."""

        # Save some settings
        set_general_setting('locale/overrideFlag', True, settings)
        set_general_setting('locale/userLocale', requested_locale, settings)

        locale_name = str(requested_locale).split('_')[0]
        # Also set the system locale to the user overridden local
        # so that the inasafe library functions gettext will work
        # .. see:: :py:func:`common.utilities`
        os.environ['LANG'] = str(locale_name)

        inasafe_translation_path = os.path.join(
            safe_dir('i18n'), 'inasafe_' + str(locale_name) + '.qm')

        if os.path.exists(inasafe_translation_path):
            if isinstance(QGIS_APP, sip.wrappertype):
                translator = QTranslator()
            else:
                translator = QTranslator(QGIS_APP)
            result = translator.load(inasafe_translation_path)
            if not result:
                message = 'Failed to load translation for %s' % locale_name
                raise Exception(message)
            # noinspection PyTypeChecker,PyCallByClass
            QCoreApplication.installTranslator(translator)

        # at the end, reload InaSAFE modules so it will get translated too
        reload_inasafe_modules()

    if PARENT is None:
        # noinspection PyPep8Naming
        PARENT = QtWidgets.QWidget()

    if CANVAS is None:
        # noinspection PyPep8Naming
        CANVAS = QgsMapCanvas(PARENT)
        CANVAS.resize(QtCore.QSize(400, 400))

    if IFACE is None:
        # QgisInterface is a stub implementation of the QGIS plugin interface
        # noinspection PyPep8Naming
        IFACE = QgisInterface(CANVAS)

    return QGIS_APP, CANVAS, IFACE, PARENT
Exemplo n.º 15
0
    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,
    analysis_provenance_details_component
]

# Standard HTML output for impact report
standard_impact_report_metadata_html = {
    'key':
    'analysis-result-html',
    'name':
    'analysis-result-html',
    'template_folder':
    safe_dir(sub_dir='../resources/report-templates/'),
    'components':
    impact_report_component_metadata + [
        population_chart_svg_component,
        population_chart_png_component,
        population_infographic_component,
        # TODO: Make a beautiful infographic using qpt template
        # We need to disable infographic for now :(
        # Infographic Layout HTML
        # {
        #     'key': 'infographic-layout',
        #     'type': jinja2_component_type,
        #     'processor': jinja2_renderer,
        #     'extractor': infographic_layout_extractor,
        #     'output_format': Jinja2ComponentsMetadata.OutputFormat.File,
        #     'output_path': 'infographic.html',
    'template': 'hello-world.html',
    'tags': [
        final_product_tag,
        table_product_tag,
        html_product_tag
    ],
    'extra_args': {
        'header': tr('Hello World!')
    }
}

# third step (specifying what components to use)
hello_world_metadata_html = {
    'key': 'hello-world-result-html',
    'name': 'hello-world-result-html',
    'template_folder': safe_dir('report/test/fixtures'),
    'components': [hello_world_component]
}


class TestHelloWorldReport(unittest.TestCase):

    @classmethod
    def fixtures_dir(cls, path):
        """Helper to return fixture path."""
        directory_name = os.path.dirname(__file__)
        return os.path.join(directory_name, 'fixtures', path)

    def test_hello_world_report(self):
        """Test for creating hello world report.
Exemplo n.º 17
0
    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,
    analysis_provenance_details_component
]

# Standard HTML output for impact report
standard_impact_report_metadata_html = {
    'key': 'analysis-result-html',
    'name': 'analysis-result-html',
    'template_folder': safe_dir(sub_dir='../resources/report-templates/'),
    'components': impact_report_component_metadata + [
        population_chart_svg_component,
        population_chart_png_component,
        population_infographic_component,
        # TODO: Make a beautiful infographic using qpt template
        # We need to disable infographic for now :(
        # Infographic Layout HTML
        # {
        #     'key': 'infographic-layout',
        #     'type': jinja2_component_type,
        #     'processor': jinja2_renderer,
        #     'extractor': infographic_layout_extractor,
        #     'output_format': Jinja2ComponentsMetadata.OutputFormat.File,
        #     'output_path': 'infographic.html',
        #     'extra_args': {
Exemplo n.º 18
0
    'template': 'hello-world.html',
    'tags': [
        final_product_tag,
        table_product_tag,
        html_product_tag
    ],
    'extra_args': {
        'header': tr('Hello World!')
    }
}

# third step (specifying what components to use)
hello_world_metadata_html = {
    'key': 'hello-world-result-html',
    'name': 'hello-world-result-html',
    'template_folder': safe_dir('report/test/fixtures'),
    'components': [hello_world_component]
}


class TestHelloWorldReport(unittest.TestCase):

    """Test about report generation, from scratch."""

    @classmethod
    def fixtures_dir(cls, path):
        """Helper to return fixture path."""
        directory_name = os.path.dirname(__file__)
        return os.path.join(directory_name, 'fixtures', path)

    def test_hello_world_report(self):
Exemplo n.º 19
0
def get_qgis_app(requested_locale='en_US', qsetting=''):
    """ Start one QGIS application to test against.

    :param locale: The locale we want the qgis to launch with.
    :type locale: str

    :param qsetting: String to specify the QSettings. By default,
        use empty string.
    :type qsetting: str

    :returns: Handle to QGIS app, canvas, iface and parent. If there are any
        errors the tuple members will be returned as None.
    :rtype: (QgsApplication, CANVAS, IFACE, PARENT)

    If QGIS is already running the handle to that app will be returned.
    """
    global QGIS_APP, PARENT, IFACE, CANVAS  # pylint: disable=W0603

    from qgis.PyQt.QtCore import QSettings
    if qsetting:
        settings = QSettings(qsetting)
    else:
        settings = QSettings()

    default_user_directory = setting('defaultUserDirectory')
    current_locale = general_setting(
        'locale/userLocale', default='en_US', qsettings=settings)
    locale_match = current_locale == requested_locale

    if iface and locale_match:
        from qgis.core import QgsApplication
        QGIS_APP = QgsApplication
        CANVAS = iface.mapCanvas()
        PARENT = iface.mainWindow()
        IFACE = iface

    try:
        from qgis.core import QgsApplication
        from qgis.gui import QgsMapCanvas  # pylint: disable=no-name-in-module
        # noinspection PyPackageRequirements
        from qgis.PyQt import QtWidgets, QtCore  # pylint: disable=W0621
        # noinspection PyPackageRequirements
        from qgis.PyQt.QtCore import QCoreApplication, QSettings
        from safe.test.qgis_interface import QgisInterface
    except ImportError:
        return None, None, None, None

    if qsetting:
        settings = QSettings(qsetting)
    else:
        settings = QSettings()

    if not QGIS_APP:
        gui_flag = True  # All test will run qgis in gui mode

        # AG: For testing purposes, we use our own configuration file
        # instead of using the QGIS apps conf of the host
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setOrganizationName('QGIS')
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setOrganizationDomain('qgis.org')
        # noinspection PyCallByClass,PyArgumentList
        QCoreApplication.setApplicationName('QGIS2InaSAFETesting')

        # We disabled message bars for now for extent selector as
        # we don't have a main window to show them in TS - version 3.2
        set_setting('show_extent_warnings', False, settings)
        set_setting('showRubberBands', True, settings)
        set_setting('show_extent_confirmations', False, settings)
        set_setting('analysis_extents_mode', HAZARD_EXPOSURE, settings)
        if default_user_directory:
            set_setting(
                'defaultUserDirectory', default_user_directory, settings)

        # noinspection PyPep8Naming
        if 'argv' in dir(sys):
            QGIS_APP = QgsApplication([p.encode('utf-8')
                                       for p in sys.argv], gui_flag)
        else:
            QGIS_APP = QgsApplication([], gui_flag)

        # Make sure QGIS_PREFIX_PATH is set in your env if needed!
        QGIS_APP.initQgis()

        # Initialize processing
        processing.Processing.initialize()

        s = QGIS_APP.showSettings()
        LOGGER.debug(s)

    if not locale_match:
        """Setup internationalisation for the plugin."""

        # Save some settings
        set_general_setting('locale/overrideFlag', True, settings)
        set_general_setting('locale/userLocale', requested_locale, settings)

        locale_name = str(requested_locale).split('_')[0]
        # Also set the system locale to the user overridden local
        # so that the inasafe library functions gettext will work
        # .. see:: :py:func:`common.utilities`
        os.environ['LANG'] = str(locale_name)

        inasafe_translation_path = os.path.join(
            safe_dir('i18n'), 'inasafe_' + str(locale_name) + '.qm')

        if os.path.exists(inasafe_translation_path):
            if isinstance(QGIS_APP, sip.wrappertype):
                translator = QTranslator()
            else:
                translator = QTranslator(QGIS_APP)
            result = translator.load(inasafe_translation_path)
            if not result:
                message = 'Failed to load translation for %s' % locale_name
                raise Exception(message)
            # noinspection PyTypeChecker,PyCallByClass
            QCoreApplication.installTranslator(translator)

        # at the end, reload InaSAFE modules so it will get translated too
        reload_inasafe_modules()

    if PARENT is None:
        # noinspection PyPep8Naming
        PARENT = QtWidgets.QWidget()

    if CANVAS is None:
        # noinspection PyPep8Naming
        CANVAS = QgsMapCanvas(PARENT)
        CANVAS.resize(QtCore.QSize(400, 400))

    if IFACE is None:
        # QgisInterface is a stub implementation of the QGIS plugin interface
        # noinspection PyPep8Naming
        IFACE = QgisInterface(CANVAS)

    return QGIS_APP, CANVAS, IFACE, PARENT