def aggregation_postprocessors_extractor(impact_report, component_metadata):
    """Extracting aggregate result of demographic.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {
        'sections': OrderedDict()
    }

    """Initializations."""

    extra_args = component_metadata.extra_args
    # Find out aggregation report type
    aggregation_summary = impact_report.aggregation_summary
    analysis_layer = impact_report.analysis
    analysis_layer_fields = impact_report.analysis.keywords['inasafe_fields']
    use_rounding = impact_report.impact_function.use_rounding
    use_aggregation = bool(impact_report.impact_function.provenance[
        'aggregation_layer'])
    provenance = impact_report.impact_function.provenance
    exposure_keywords = provenance['exposure_keywords']

    # Get exposure type definition
    exposure_type = definition(exposure_keywords['exposure'])

    # this entire section is only for population exposure type
    if not exposure_type == exposure_population:
        return context

    # check zero displaced (there will be no output to display)
    try:
        displaced_field_name = analysis_layer_fields[displaced_field['key']]
        total_displaced = value_from_field_name(
            displaced_field_name, analysis_layer)

        zero_displaced = False
        if total_displaced == 0:
            zero_displaced = True
    except KeyError:
        # in case no displaced field
        # let each section handled itself
        zero_displaced = False

    context['component_key'] = component_metadata.key
    context['use_aggregation'] = use_aggregation
    context['header'] = resolve_from_dictionary(
        extra_args, 'header')

    group_header_format = resolve_from_dictionary(
        extra_args, ['defaults', 'group_header_format'])

    section_header_format = resolve_from_dictionary(
        extra_args,
        ['defaults', 'section_header_format'])
    if not use_aggregation:
        section_header_format = resolve_from_dictionary(
            extra_args, ['defaults', 'section_header_format_no_aggregation'])

    """Age Groups."""
    age_items = {
        'group': age_displaced_count_group,
        'group_header': group_header_format.format(
            header_name=age_displaced_count_group['header_name']),
        'fields': [postprocessor_output_field(p) for p in age_postprocessors]
    }

    # check age_fields exists
    for field in age_items['fields']:
        if field['key'] in analysis_layer_fields:
            no_age_field = False
            break
    else:
        no_age_field = True

    context['sections']['age'] = []
    age_section_header = section_header_format.format(
        header_name=age_displaced_count_group['header_name'])
    if zero_displaced:
        context['sections']['age'].append(
            {
                'header': age_section_header,
                'empty': True,
                'message': resolve_from_dictionary(
                    extra_args, ['defaults', 'zero_displaced_message'])
            }
        )
    elif no_age_field:
        context['sections']['age'].append(
            {
                'header': age_section_header,
                'empty': True,
                'message': resolve_from_dictionary(
                    extra_args, ['defaults', 'no_age_rate_message'])
            }
        )
    else:
        context['sections']['age'].append(
            create_section(
                aggregation_summary,
                analysis_layer,
                age_items,
                age_section_header,
                use_aggregation=use_aggregation,
                use_rounding=use_rounding,
                extra_component_args=extra_args)
        )

    """Gender Groups."""
    gender_items = {
        'group': gender_displaced_count_group,
        'group_header': group_header_format.format(
            header_name=gender_displaced_count_group['header_name']),
        'fields': [
            postprocessor_output_field(p) for p in gender_postprocessors]
    }

    # check gender_fields exists
    for field in gender_items['fields']:
        if field['key'] in analysis_layer_fields:
            no_gender_field = False
            break
    else:
        no_gender_field = True

    context['sections']['gender'] = []
    gender_section_header = section_header_format.format(
        header_name=gender_displaced_count_group['header_name'])
    if zero_displaced:
        context['sections']['gender'].append(
            {
                'header': gender_section_header,
                'empty': True,
                'message': resolve_from_dictionary(
                    extra_args, ['defaults', 'zero_displaced_message'])
            }
        )
    elif no_gender_field:
        context['sections']['gender'].append(
            {
                'header': gender_section_header,
                'empty': True,
                'message': resolve_from_dictionary(
                    extra_args, ['defaults', 'no_gender_rate_message'])
            }
        )
    else:
        context['sections']['gender'].append(
            create_section(
                aggregation_summary,
                analysis_layer,
                gender_items,
                gender_section_header,
                use_aggregation=use_aggregation,
                use_rounding=use_rounding,
                extra_component_args=extra_args)
        )

    """Vulnerability Groups."""
    context['sections']['vulnerability'] = []
    for vulnerability_group in vulnerability_displaced_count_groups:
        vulnerability_items = {
            'group': vulnerability_group,
            'group_header': group_header_format.format(
                header_name=vulnerability_group['header_name']),
            'fields': [field for field in vulnerability_group['fields']]
        }

        # check vulnerability_fields exists
        for field in vulnerability_items['fields']:
            if field['key'] in analysis_layer_fields:
                no_vulnerability_field = False
                break
        else:
            no_vulnerability_field = True

        vulnerability_section_header = section_header_format.format(
            header_name=vulnerability_group['header_name'])
        if zero_displaced:
            context['sections']['vulnerability'].append(
                {
                    'header': vulnerability_section_header,
                    'empty': True,
                    'message': resolve_from_dictionary(
                        extra_args, ['defaults', 'zero_displaced_message'])
                }
            )
        elif no_vulnerability_field:
            context['sections']['vulnerability'].append(
                {
                    'header': vulnerability_section_header,
                    'empty': True,
                    'message': resolve_from_dictionary(
                        extra_args,
                        ['defaults', 'no_vulnerability_rate_message'])
                }
            )
        else:
            context['sections']['vulnerability'].append(
                create_section(
                    aggregation_summary,
                    analysis_layer,
                    vulnerability_items,
                    vulnerability_section_header,
                    use_aggregation=use_aggregation,
                    use_rounding=use_rounding,
                    extra_component_args=extra_args)
            )

    """Minimum Needs."""
    context['sections']['minimum_needs'] = []
    minimum_needs_section_header = resolve_from_dictionary(
        extra_args, ['sections', 'minimum_needs', 'header'])
    # Don't show minimum needs if there is no displaced
    if zero_displaced:
        context['sections']['minimum_needs'].append(
            {
                'header': minimum_needs_section_header,
                'empty': True,
                'message': resolve_from_dictionary(
                    extra_args, ['defaults', 'zero_displaced_message'])
            }
        )
    # Only provides minimum needs breakdown if there is aggregation layer
    elif use_aggregation:
        # minimum needs should provide unit for column headers
        units_label = []
        minimum_needs_items = {
            'group_header': 'Minimum needs breakdown',
            'fields': minimum_needs_fields + additional_minimum_needs
        }

        for field in minimum_needs_items['fields']:
            unit = None
            if field.get('need_parameter'):
                need = field['need_parameter']
                if isinstance(need, ResourceParameter):
                    unit_abbreviation = need.unit.abbreviation
            elif field.get('unit'):
                need_unit = field.get('unit')
                unit_abbreviation = need_unit.get('abbreviation')

            if unit_abbreviation:
                unit_format = '{unit}'
                unit = unit_format.format(
                    unit=unit_abbreviation)
            units_label.append(unit)

        context['sections']['minimum_needs'].append(
            create_section(
                aggregation_summary,
                analysis_layer,
                minimum_needs_items,
                minimum_needs_section_header,
                units_label=units_label,
                use_rounding=use_rounding,
                extra_component_args=extra_args)
        )
    else:
        sections_not_empty = True
        for _, values in list(context['sections'].items()):
            for value in values:
                if value.get('rows'):
                    break
                else:
                    sections_not_empty = False

        context['sections_not_empty'] = sections_not_empty

    return context
Extracting aggregate result of demographic.

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

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

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0