Source code for safe.impact_reports.population_exposure_report_mixin

# coding=utf-8
"""
InaSAFE Disaster risk assessment tool developed by AusAid -
**Building Exposure Report Mixin Class**

Contact : [email protected]

.. note:: This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
"""
__author__ = 'Christian Christelis <[email protected]>'
__revision__ = '$Format:%H$'
__date__ = '05/05/2015'
__copyright__ = ('Copyright 2012, Australia Indonesia Facility for '
                 'Disaster Reduction')

from collections import OrderedDict

from safe.utilities.i18n import tr
from safe.common.utilities import format_int
from safe.impact_reports.report_mixin_base import ReportMixin

from safe.impact_functions.core import (
    evacuated_population_needs,
    population_rounding)
import safe.messaging as m
from safe.messaging import styles


[docs]class PopulationExposureReportMixin(ReportMixin): """Population specific report. """ def __init__(self): """Population specific report mixin. .. versionadded:: 3.2 ..Notes:: Expect affected population as following: _affected_population = OrderedDict([ (impact level, amount), e.g. (People in high hazard area, 1000), (People in medium hazard area, 100), (People in low hazard area, 5), )] """ self._question = '' self._total_population = 0 self._unaffected_population = 0 self._evacuation_category = 0 self._evacuation_percentage = 0 self._minimum_needs = [] self._affected_population = {} self._other_population_counts = {} self._impact_category_ordering = []
[docs] def generate_report(self): """Breakdown by building type. :returns: The report. :rtype: list """ message = m.Message() message.add(m.Paragraph(self.question)) message.add(self.impact_summary()) message.add(self.minimum_needs_breakdown()) message.add(self.action_checklist()) message.add(self.notes()) return message
[docs] def action_checklist(self): """Population action. :returns: The population breakdown report. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') message.add(m.Heading(tr('Action checklist'), **styles.INFO_STYLE)) checklist = m.BulletedList() checklist.add(tr('How will warnings be disseminated?')) checklist.add(tr('How will we reach evacuated people?')) checklist.add(tr( 'Are there enough shelters and relief items available ' 'for %s people?' % self.total_evacuated)) checklist.add(tr( 'If yes, where are they located and how will we distribute them?')) checklist.add(tr( 'If no, where can we obtain additional relief items from ' 'and how will we transport them to here?')) message.add(checklist) return message
[docs] def impact_summary(self): """The impact summary as per category :returns: The impact summary. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') table = m.Table(style_class='table table-condensed table-striped') table.caption = None row = m.Row() row.add(m.Cell( tr('Population needing evacuation <sup>1</sup>'), header=True)) evacuated = format_int(population_rounding(self.total_evacuated)) row.add(m.Cell(evacuated, align='right')) table.add(row) if len(self.impact_category_ordering): table.add(m.Row()) # add a blank line row = m.Row() row.add(m.Cell( tr('Total affected population'), header=True)) affected = format_int( population_rounding(self.total_affected_population)) row.add(m.Cell(affected, align='right')) table.add(row) for category in self.impact_category_ordering: population_in_category = self.lookup_category(category) population_in_category = format_int(population_rounding( population_in_category )) row = m.Row() row.add(m.Cell(tr(category), header=True)) row.add(m.Cell(population_in_category, align='right')) table.add(row) table.add(m.Row()) # add a blank line row = m.Row() unaffected = format_int( population_rounding(self.unaffected_population)) row.add(m.Cell(tr('Unaffected population'), header=True)) row.add(m.Cell(unaffected, align='right')) table.add(row) message.add(table) return message
[docs] def minimum_needs_breakdown(self): """Breakdown by population. :returns: The population breakdown report. :rtype: list """ message = m.Message(style_class='container') message.add(m.Heading( tr('Evacuated population minimum needs'), **styles.INFO_STYLE)) table = m.Table( style_class='table table-condensed table-striped') table.caption = None total_needs = self.total_needs for frequency, needs in total_needs.items(): row = m.Row() row.add(m.Cell( tr('Relief items to be provided %s' % frequency), header=True )) row.add(m.Cell(tr('Total'), header=True, align='right')) table.add(row) for resource in needs: row = m.Row() row.add(m.Cell(tr(resource['table name']))) row.add(m.Cell( tr(format_int(resource['amount'])), align='right' )) table.add(row) message.add(table) return message
@property def impact_category_ordering(self): """Get the ordering of the impact categories. :returns: The categories by defined or default ordering. :rtype: list """ if ( not hasattr(self, '_impact_category_ordering') or not self._impact_category_ordering): self._impact_category_ordering = self.affected_population.keys() return self._impact_category_ordering @impact_category_ordering.setter
[docs] def impact_category_ordering(self, impact_category_ordering): """Overwrite existing category ordering. :param impact_category_ordering: The new ordering. :type impact_category_ordering: list. """ self._impact_category_ordering = impact_category_ordering
@property def other_population_counts(self): """The population counts which are not explicitly included in affected. :returns: Population counts. :rtype: dict """ if not hasattr(self, '_other_population_counts'): self._other_population_counts = {} return self._other_population_counts @other_population_counts.setter
[docs] def other_population_counts(self, other_counts): """Set the other population counts. :param other_counts: Population counts. :type other_counts: dict """ if not hasattr(self, '_other_population_counts'): self._other_population_counts = {} self._other_population_counts = other_counts
@property def affected_population(self): """Get the affected population counts. :returns: Affected population counts. :rtype: dict """ if not hasattr(self, '_affected_population'): self._affected_population = OrderedDict() return self._affected_population @affected_population.setter
[docs] def affected_population(self, affected_population): """Set the affected population counts. :param affected_population: The population counts. :type affected_population: dict """ self._affected_population = affected_population
@property def question(self): """Get the impact function question. :returns: The impact function question. :rtype: basestring """ if not hasattr(self, '_question'): self._question = '' return self._question @question.setter
[docs] def question(self, question): """Set the impact function question. :param question: The question. :type question: basestring """ self._question = question
@property def unaffected_population(self): """Get the unaffected population count. :returns: The unaffected population count. :returns: int """ if not hasattr(self, '_unaffected_population'): self._unaffected_population = 0 return self._unaffected_population @unaffected_population.setter
[docs] def unaffected_population(self, unaffected_population): """Set the unaffected population count. :param unaffected_population: The unaffected population count. :return: int """ self._unaffected_population = unaffected_population
@property
[docs] def total_affected_population(self): """Get the total affected population. :returns: Total affected population. :rtype: int. """ return sum(self.affected_population.values())
[docs] def lookup_category(self, category): """Lookup a category by its name. :param category: The category to be looked up. :type category: basestring :returns: The category's count. :rtype: int .. note:: The category may be any valid category, but it also includes 'Population Not Affected', 'Unaffected Population' for unaffected as well as 'Total Impacted', 'People impacted', 'Total Population Affected' for total affected population. This diversity is to accodate existing usages, which have evolved separately. We may want to update these when we have decided on a single convention. """ if category in self.affected_population.keys(): return self.affected_population[category] if category in self.other_population_counts.keys(): return self.other_population_counts[category] if category in [ tr('Population Not Affected'), tr('Unaffected Population')]: return self.unaffected_population if category in [ tr('Total Impacted'), tr('People impacted'), tr('Total Population Affected')]: return self.total_affected_population
@property
[docs] def total_needs(self): """Get the total minimum needs based on the total evacuated. :returns: Total minimum needs. :rtype: dict """ total_population_evacuated = self.total_evacuated return evacuated_population_needs( total_population_evacuated, self.minimum_needs)
@property
[docs] def total_evacuated(self): """Get the total evacuated population. :returns: The total evacuated population. :rtype: int .. note:: The total evacuated is said to either the evacuated amount, failing that the total affected population and if applicable reduced by a evacuation percentage. """ if hasattr(self, '_evacuation_category') and self._evacuation_category: evacuated_population = self.affected_population[ self._evacuation_category] else: evacuated_population = self.total_affected_population if ( hasattr(self, '_evacuation_percentage') and self._evacuation_percentage): evacuated_population = ( evacuated_population * self._evacuation_percentage / 100) return int(evacuated_population)
@property def total_population(self): """Get the total population. :returns: The total population. :rtype: int """ if not hasattr(self, '_total_population'): self._total_population = 0 return self._total_population @total_population.setter
[docs] def total_population(self, total_population): """Set the total population. :param total_population: The total population count. :type total_population: int """ self._total_population = total_population
@property def minimum_needs(self): """Get the minimum needs as specified, or default. :returns: The minimum needs parameters. :rtype: list """ if not hasattr(self, '_minimum_needs'): self._minimum_needs = [] return self._minimum_needs @minimum_needs.setter
[docs] def minimum_needs(self, minimum_needs): """Set the minimum needs parameters list. :param minimum_needs: Minimum needs :type minimum_needs: list """ self._minimum_needs = minimum_needs