def _add_field_to_table(field, table): row = m.Row() row.add(m.Cell(field['name'])) row.add(m.Cell(field['field_name'])) field_types = None if not isinstance(field['type'], list): field_types = '%s' % _type_to_string(field['type']) else: # List of field types are supported but the user will only care # about the simple types so we turn them into simple names (whole # number, decimal number etc. and then strip out the duplicates unique_list = [] # First iterate the types found in the definition to get english names for field_type in field['type']: field_type_string = _type_to_string(field_type) if field_type_string not in unique_list: unique_list.append(field_type_string) # now iterate the unque list and write to a sentence for field_type in unique_list: if field_types: field_types += ', %s' % unicode(field_type) else: field_types = unicode(field_type) row.add(m.Cell(field_types)) row.add(m.Cell(field['precision'])) table.add(row) # Description goes in its own row with spanning row = m.Row() row.add(m.Cell(field['description'] + ' ' + field['help_text'], span=6)) table.add(row)
def format_impact_summary(self): """Format impact summary. :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 if 'headings' in self.impact_summary.keys(): row = m.Row() row.add(m.Cell(self.impact_summary['headings'][0], header=True)) row.add( m.Cell(self.impact_summary['headings'][1], header=True, align='right')) table.add(row) for category in self.impact_summary['fields']: row = m.Row() row.add(m.Cell(category[0], header=True)) row.add(m.Cell(self.format_int(category[1]), align='right')) # For value field, if existed if len(category) > 2: row.add(m.Cell(self.format_int(category[2]), align='right')) table.add(row) message.add(table) return message
def impact_summary(self): """The impact summary as per category :returns: The impact summary. :rtype: safe.message.Message """ affected_categories = self.affected_road_categories 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('Summary by road type'), header=True)) for _ in affected_categories: # Add empty cell as many as affected_categories row.add(m.Cell('', header=True)) if self.add_unaffected_column: # Add empty cell for un-affected road row.add(m.Cell('', header=True)) # Add empty cell for total column row.add(m.Cell('', header=True)) table.add(row) row = m.Row() row.add(m.Cell(tr('Road Type'), header=True)) for affected_category in affected_categories: row.add(m.Cell(affected_category, header=True, align='right')) if self.add_unaffected_column: row.add(m.Cell(tr('Unaffected'), header=True, align='right')) row.add(m.Cell(tr('Total'), header=True, align='right')) table.add(row) total_affected = [0] * len(affected_categories) for (category, road_breakdown) in self.affected_road_lengths.items(): number_affected = sum(road_breakdown.values()) count = affected_categories.index(category) total_affected[count] = number_affected row = m.Row() row.add(m.Cell(tr('All (m)'))) for total_affected_value in total_affected: row.add( m.Cell(format_int(int(total_affected_value)), align='right')) if self.add_unaffected_column: row.add( m.Cell(format_int( int(self.total_road_length - sum(total_affected))), align='right')) row.add(m.Cell(format_int(int(self.total_road_length)), align='right')) table.add(row) message.add(table) return message
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
def hazard_table(self, hazard_table): """ Return updated hazard table. :param hazard_table: hazard table. :type hazard_table: Table :returns hazard_table: Updated Hazard Table :rtype area_name: Table """ hazard_table.caption = None for key, value in self.hazard_levels.iteritems(): name = self.hazard_class_mapping[key][0] # This skips reporting people not affected in No zone if key == 'wet': row = m.Row() row.add( m.Cell( tr('People within hazard field ("%s") of value "%s"') % (self.hazard_class_field, name), header=True)) value = format_int(population_rounding(value)) row.add(m.Cell(value, align='right')) elif key == 'dry': continue else: row = m.Row() row.add(m.Cell(name, header=True)) value = format_int(population_rounding(value)) row.add(m.Cell(value, align='right')) hazard_table.add(row) # Total affected population row = m.Row() row.add(m.Cell(tr('Total affected people'), header=True)) affected = format_int( population_rounding(self.total_affected_population)) row.add(m.Cell(affected, align='right')) hazard_table.add(row) # Non affected population row = m.Row() unaffected = format_int(population_rounding( self.unaffected_population)) row.add(m.Cell(tr('Unaffected people'), header=True)) row.add(m.Cell(unaffected, align='right')) hazard_table.add(row) # Total Population row = m.Row() total_population = format_int( population_rounding(self.total_population)) row.add(m.Cell(tr('Total people'), header=True)) row.add(m.Cell(total_population, align='right')) hazard_table.add(row) return hazard_table
def format_postprocessing(self): """Format postprocessing. :returns: The postprocessing. :rtype: safe.messaging.Message """ # List of post processor that can't be sum up. Issue #3118 no_total = ['Age', 'Gender', 'MinimumNeeds'] if not self.postprocessing: return False message = m.Message() for postprocessor, v in self.postprocessing.items(): table = m.Table(style_class='table table-condensed table-striped') table.caption = v['caption'] attributes = v['attributes'] if attributes: header = m.Row() # Bold and align left the 1st one. header.add(m.Cell(attributes[0], header=True, align='left')) for attribute in attributes[1:]: # Bold and align right. header.add(m.Cell(attribute, header=True, align='right')) if postprocessor not in no_total: header.add(m.Cell('Total', header=True, align='right')) table.add(header) for field in v['fields']: row = m.Row() # First column is string row.add(m.Cell(field[0])) total = 0 for value in field[1:]: try: val = int(value) total += val # Align right integers. row.add(m.Cell(self.format_int(val), align='right')) except ValueError: # Catch no data value. Align left strings. row.add(m.Cell(value, align='left')) if postprocessor not in no_total: row.add( m.Cell(self.format_int(round(total)), align='right')) table.add(row) message.add(table) for note in v['notes']: message.add(m.EmphasizedText(note)) return message
def _create_post_processor_subtable(item_list): 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 item in item_list: row = m.Row() row.add(m.Cell(item['key'])) row.add(m.Cell(item['description'])) table.add(row) return table
def impact_summary(self): """The impact summary as per category. :returns: The impact summary. :rtype: safe.messaging.Message """ affect_types = self._impact_breakdown 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('', header=True)) # intentionally empty top left cell row.add(m.Cell('Buildings affected', header=True)) for (category, building_breakdown) in self.affected_buildings.items(): total_affected = [0] * len(affect_types) for affected_breakdown in building_breakdown.values(): for affect_type, number_affected in affected_breakdown.items(): count = affect_types.index(affect_type) total_affected[count] += number_affected row = m.Row() row.add(m.Cell(tr(category), header=True)) for affected in total_affected: row.add(m.Cell(format_int(affected), align='right')) table.add(row) if len(self._affected_categories) > 1: row = m.Row() row.add(m.Cell(tr('Affected buildings'), header=True)) row.add( m.Cell(format_int(self.total_affected_buildings), align='right')) table.add(row) # Only show not affected building row if the IF does not use custom # affected categories if self._affected_categories == self.affected_buildings.keys(): row = m.Row() row.add(m.Cell(tr('Not affected buildings'), header=True)) row.add( m.Cell(format_int(self.total_unaffected_buildings), align='right')) table.add(row) row = m.Row() row.add(m.Cell(tr('Total'), header=True)) row.add(m.Cell(format_int(self.total_buildings), align='right')) table.add(row) message.add(table) return message
def format_impact_table(self): """Impact detailed report. :returns: The detailed report. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') table = m.Table(style_class='table table-condensed table-striped') table.caption = None # Table header row = m.Row() attributes = self.impact_table['attributes'] # Bold and align left the 1st one. row.add(m.Cell(attributes[0], header=True, align='left')) for attribute in attributes[1:]: # Bold and align right. row.add(m.Cell(attribute, header=True, align='right')) table.add(row) # Fields for record in self.impact_table['fields'][:-1]: row = m.Row() # Bold and align left the 1st one. row.add(m.Cell(record[0], header=True, align='left')) for content in record[1:-1]: # Align right. row.add(m.Cell(format_int(int(content)), align='right')) # Bold and align right the last one. row.add( m.Cell(format_int(int(record[-1])), header=True, align='right')) table.add(row) # Total Row row = m.Row() last_row = self.impact_table['fields'][-1] # Bold and align left the 1st one. row.add(m.Cell(last_row[0], header=True, align='left')) for content in last_row[1:]: # Bold and align right. row.add( m.Cell(format_int(int(content)), header=True, align='right')) table.add(row) message.add(table) return message
def _start_glossary_table(group): table = m.Table(style_class='table table-condensed table-striped') row = m.Row() row.add(m.Cell(tr('Term')), header_flag=True) row.add(m.Cell(tr('Description')), header_flag=True) table.add(row) return table
def generate_analysis_result_html(self): """Return a HTML table of the analysis result :return: A file path to the html file saved to disk. """ message = m.Message(style_class='report') # Table for affected population table = m.Table(style_class='table table-condensed table-striped') row = m.Row() total_people = self.tr('%s') % format_int( population_rounding(self.impact_data.total_affected_population)) estimates_idp = self.tr('%s') % format_int( population_rounding(self.impact_data.estimates_idp)) row.add( m.Cell(self.tr('Total affected population (people)'), header=True)) row.add(m.Cell(total_people, style_class="text-right")) table.add(row) row = m.Row() row.add(m.Cell(self.tr('Estimates of IDP (people)'), header=True)) row.add(m.Cell(estimates_idp, style_class="text-right")) table.add(row) message.add(table) # Table for minimum needs for k, v in self.impact_data.minimum_needs.iteritems(): section = self.tr('Relief items to be provided %s :') % k # text = m.Text(section) row = m.Row(style_class='alert-info') row.add(m.Cell(section, header=True, attributes='colspan=2')) # message.add(text) table = m.Table(header=row, style_class='table table-condensed table-striped') for e in v: row = m.Row() need_name = self.tr(e['name']) need_number = format_int(population_rounding(e['amount'])) need_unit = self.tr(e['unit']['abbreviation']) if need_unit: need_string = '%s (%s)' % (need_name, need_unit) else: need_string = need_name row.add(m.Cell(need_string, header=True)) row.add(m.Cell(need_number, style_class="text-right")) table.add(row) message.add(table) path = self.write_html_table('impact_analysis_report.html', message) return path
def _start_glossary_table(group): table = m.Table(style_class='table table-condensed table-striped') row = m.Row() # first col for icons if present row.add(m.Cell(tr('Term'), header=True)) row.add(m.Cell(tr('Description'), header=True)) row.add(m.Cell(tr(''), header=True)) table.add(row) return table
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 = self.head_row(row) table.add(row) second_row = m.Row() second_row.add(m.Cell(tr('All'))) second_row = self.total_row(second_row) table.add(second_row) break_row = m.Row() break_row.add( m.Cell(tr('Breakdown by Area'), header=True, align='right')) # intentionally empty right cells break_row.add(m.Cell('', header=True)) break_row.add(m.Cell('', header=True)) break_row.add(m.Cell('', header=True)) break_row.add(m.Cell('', header=True)) break_row.add(m.Cell('', header=True)) break_row.add(m.Cell('', header=True)) table.add(break_row) table = self.impact_calculation(table) last_row = m.Row() last_row.add(m.Cell(tr('Total'))) table.add(self.total_row(last_row)) hazard_table = m.Table( style_class='table table-condensed table-striped') hazard_table = self.hazard_table(hazard_table) message.add(hazard_table) message.add(table) return message
def _create_fields_table(): 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('Field Name'), header=True)) row.add(m.Cell(tr('Type'), header=True)) row.add(m.Cell(tr('Length'), header=True)) row.add(m.Cell(tr('Precision'), header=True)) table.add(row) return table
def _dict_to_row(keyword_value): """Helper to make a message row from a keyword where value is a dict. .. versionadded:: 3.2 Use this when constructing a table from keywords to display as part of a message object. This variant will unpack the dict and present it nicely in the keyword value area as a nested table in the cell. We are expecting keyword value would be something like this: "{'high': ['Kawasan Rawan Bencana III'], " "'medium': ['Kawasan Rawan Bencana II'], " "'low': ['Kawasan Rawan Bencana I']}" Or by passing a python dict object with similar layout to above. i.e. A string representation of a dict where the values are lists. :param keyword_value: Value of the keyword to be rendered. This must be a string representation of a dict, or a dict. :type keyword_value: basestring, dict :returns: A table to be added into a cell in the keywords table. :rtype: safe.messaging.items.table """ if isinstance(keyword_value, basestring): keyword_value = literal_eval(keyword_value) table = m.Table(style_class='table table-condensed') for key, value in keyword_value.items(): row = m.Row() # First the heading if definition(key): name = definition(key)['name'] else: name = tr(key.capitalize()) row.add(m.Cell(m.ImportantText(name))) # Then the value. If it contains more than one element we # present it as a bullet list, otherwise just as simple text if isinstance(value, (tuple, list, dict, set)): if len(value) > 1: bullets = m.BulletedList() for item in value: bullets.add(item) row.add(m.Cell(bullets)) elif len(value) == 0: row.add(m.Cell("")) else: row.add(m.Cell(value[0])) else: row.add(m.Cell(value)) table.add(row) return table
def _make_defaults_exposure_table(): """Build headers for a table related to exposure classes. :return: A table with headers. :rtype: m.Table """ 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 values'), header=True)) table.add(row) return table
def _make_defaults_table(): table = m.Table(style_class='table table-condensed table-striped') row = m.Row() # first row is for colour - we dont use a header here as some tables # do not have colour... row.add(m.Cell(tr('')), header_flag=True) row.add(m.Cell(tr('Name')), header_flag=True) row.add(m.Cell(tr('Affected')), header_flag=True) row.add(m.Cell(tr('Displacement rate')), header_flag=True) row.add(m.Cell(tr('Default values')), header_flag=True) row.add(m.Cell(tr('Default min')), header_flag=True) row.add(m.Cell(tr('Default max')), header_flag=True) table.add(row) return table
def _tabulate_zero_impact(self): thresholds = self.parameters['thresholds'].value message = m.Message() table = m.Table(style_class='table table-condensed table-striped') row = m.Row() label = m.ImportantText( tr('People in %.1f m of water') % thresholds[-1]) content = '%s' % format_int(self.total_evacuated) row.add(m.Cell(label)) row.add(m.Cell(content)) table.add(row) table.caption = self.question message.add(table) message = message.to_html(suppress_newlines=True) return message
def _make_defaults_hazard_table(): """Build headers for a table related to hazard classes. :return: A table with headers. :rtype: m.Table """ table = m.Table(style_class='table table-condensed table-striped') row = m.Row() # first row is for colour - we dont use a header here as some tables # do not have colour... row.add(m.Cell(tr(''), header=True)) row.add(m.Cell(tr('Name'), header=True)) row.add(m.Cell(tr('Affected'), header=True)) row.add(m.Cell(tr('Fatality rate'), header=True)) row.add(m.Cell(tr('Displacement rate'), header=True)) row.add(m.Cell(tr('Default values'), header=True)) row.add(m.Cell(tr('Default min'), header=True)) row.add(m.Cell(tr('Default max'), header=True)) table.add(row) return table
def impact_row(self, area_id, affected, percent_affected, single_total_area, number_people_affected, percent_people_affected): """Adds the calculated results into respective impact row :param area_id: Area id :type area_id: int :param affected: table with first and second row :type affected: Table :param percent_affected: percentage of affected area :type percent_affected:float :param single_total_area: total area of the land :type single_total_area:float :param number_people_affected: number of people affected :type number_people_affected:float :param percent_people_affected: percentage of people affected in the area :type percent_people_affected:float :return row: the new impact row :rtype row: Row """ area_name = self.area_name(area_id) row = m.Row() row.add(m.Cell(area_name)) row.add(m.Cell(format_int(int(affected)), align='right')) row.add(m.Cell("%.1f%%" % percent_affected, align='right')) row.add(m.Cell(format_int(int(single_total_area)), align='right')) row.add(m.Cell(format_int(int(number_people_affected)), align='right')) row.add(m.Cell("%.1f%%" % percent_people_affected, align='right')) row.add( m.Cell(format_int(int(self.areas_population[area_id])), align='right')) return row
def no_population_impact_message(question): """Create a message that indicates that no population were impacted. :param question: A question sentence that will be used as the table caption. :type question: basestring :returns: An html document containing a nice message saying nobody was impacted. :rtype: basestring """ message = m.Message() table = m.Table(style_class='table table-condensed table-striped') row = m.Row() label = m.ImportantText(tr('People impacted')) content = 0 row.add(m.Cell(label)) row.add(m.Cell(content)) table.add(row) table.caption = question message.add(table) message = message.to_html(suppress_newlines=True) return message
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
def field_mapping_help_content(): """Helper method that returns just the content in extent mode. This method was added so that the text could be reused in the wizard. :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() paragraph = m.Paragraph( tr('Field mapping describes the process of matching one or more fields ' 'in an attribute table to a concept in InaSAFE. The field mappings ' 'tool InaSAFE allows you to match concepts such as "elderly", ' '"disabled people", "pregnant" and so on to their counterpart fields ' 'in either an aggregation layer or an exposure population vector ' 'layer.')) message.add(paragraph) paragraph = m.Paragraph( m.ImportantText( 'Note: It is not possible to use this tool with raster population ' 'exposure data, but ratios defined in aggregation layers will be ' 'used when raster exposure population data is used.')) message.add(paragraph) paragraph = m.Paragraph(m.Image('file:///%s/img/screenshots/' 'demographic-concepts-screenshot.png' % resources_path()), style_class='text-center') message.add(paragraph) paragraph = m.Paragraph( tr('The illustration above shows the principle behind InaSAFE\'s ' 'demographic breakdown reporting system. The idea here is to support ' 'the production of a detailed demographic breakdown when carrying out ' 'an analysis with a population exposure vector dataset. So for ' 'example instead of simply reporting on the total number of people ' 'exposed to a hazard, we want to break down the affected population ' 'into distinct demographic groups. In InaSAFE by default we consider ' 'three groups:')) message.add(paragraph) bullets = m.BulletedList() bullets.add( m.Paragraph( m.ImportantText(tr('Gender: ')), tr('The gender group reports on gender specific demographics ' 'including things like the number of women of child bearing age, ' 'number of pregnant women, number of lactating women and so on.' ))) bullets.add( m.Paragraph( m.ImportantText(tr('Age: ')), tr('The age group reports on age specific demographics including ' 'things like the number of infants, children, young adults, ' 'adults elderly people and so on.'))) bullets.add( m.Paragraph( m.ImportantText(tr('Vulnerable people: ')), tr('The vulnerable people group reports on specific demographics ' 'relating to vulnerability including things like the number of ' 'infants, elderly people, disabled people and so on.'))) message.add(bullets) paragraph = m.Paragraph( tr('In the diagram above, you can see that we have an "age" group ' '(column on the right) which, for purposes of illustration, has two ' 'age classes: "infant" and "child" (center column). These age classes ' 'are defined in InaSAFE metadata and there are actually five classes ' 'in a default installation. In the left hand column you can see a ' 'number of columns listed from the attribute table. In this example ' 'our population data contains columns for different age ranges (' '0-1, 1-2, 2-4, 4-6). The field mapping tool can be used in order ' 'to combine the data in the "0 - 1" and "1 - 2" columns into a ' 'new column called "infant". In the next section of this document we ' 'enumerate the different groups and concepts that InaSAFE supports ' 'when generating demographic breakdowns.')) message.add(paragraph) paragraph = m.Paragraph( tr('When the tool is used, it will write additional data to the ' 'exposure or aggregation layer keywords so that your preferred ' 'concept mappings will be used when reports are generated after the ' 'analysis is carried out. You should note the following special ' 'characteristics of the field mapping tool when used for aggregation ' 'datasets versus when used for vector population exposure datasets:' )) message.add(paragraph) paragraph = m.Paragraph( m.ImportantText(tr('Aggregation datasets: ')), tr('For aggregation datasets, the field mapping tool uses global ' 'defaults (see the InaSAFE Options Dialog documentation for more ' 'details) or dataset level defaults to determine which ratios ' 'should be used to calculate concept values. For example, in the ' 'age group the aggregation dataset may specify that infants ' 'should by calculated as a ratio of 0.1% of the total population. ' 'Note that for aggregation datasets you can only use ratios, ' 'not counts.')) message.add(paragraph) paragraph = m.Paragraph( m.ImportantText(tr('Vector population exposure datasets: ')), tr('For exposure datasets, ratios are not supported, only counts. ' 'The field mappings carried out here will be used to generate ' 'new columns during a pre-processing step before the actual ' 'analysis is carried out.')) message.add(paragraph) paragraph = m.Paragraph( tr('The interplay between default ratios, aggregation layer ' 'provided ratios and population exposure layers is illustrated ' 'in the table below.')) message.add(paragraph) table = m.Table(style_class='table table-condensed table-striped') row = m.Row() row.add(m.Cell(tr('Aggregation'), header=True)) row.add(m.Cell(tr('Raster'), header=True)) row.add(m.Cell(tr('Vector, no counts'), header=True)) row.add(m.Cell(tr('Vector with counts'), header=True)) row.add(m.Cell(tr('Notes'), header=True)) table.add(row) row = m.Row([ tr('No aggregation'), tr('Use global default ratio'), tr('Use global default ratio'), tr('Use count to determine ratio'), tr(''), ]) table.add(row) row = m.Row([ tr('Aggregation, ratio not set'), tr('Use global default ratio'), tr('Do nothing'), tr('Use count to determine ratio'), tr(''), ]) table.add(row) row = m.Row([ tr('Aggregation, ratio value set'), tr('Use aggregation layer ratio'), tr('Use aggregation layer ratio'), tr('Use count to determine ratio'), tr(''), ]) table.add(row) row = m.Row([ tr('Aggregation, ratio field mapping set'), tr('Use aggregation layer ratio'), tr('Use aggregation layer ratio'), tr('Use count to determine ratio'), tr(''), ]) table.add(row) message.add(table) return message
def _value_maps_row(value_maps_keyword): """Helper to make a message row from a value maps. Expected keywords: 'value_maps': { 'structure': { 'ina_structure_flood_hazard_classification': { 'classes': { 'low': [1, 2, 3], 'medium': [4], 'high': [5, 6] }, 'active': True }, 'ina_structure_flood_hazard_4_class_classification': { 'classes': { 'low': [1], 'medium': [2, 3, 4], 'high': [5, 6, 7], 'very_high': [8] }, 'active': False } }, 'population': { 'ina_population_flood_hazard_classification': { 'classes': { 'low': [1], 'medium': [2, 3], 'high': [4, 5, 6] }, 'active': False }, 'ina_population_flood_hazard_4_class_classification': { 'classes': { 'low': [1, 2], 'medium': [3, 4], 'high': [4, 5, 6], 'very_high': [6, 7, 8] }, 'active': True } }, } :param value_maps_keyword: Value of the keyword to be rendered. This must be a string representation of a dict, or a dict. :type value_maps_keyword: basestring, dict :returns: A table to be added into a cell in the keywords table. :rtype: safe.messaging.items.table """ if isinstance(value_maps_keyword, basestring): value_maps_keyword = literal_eval(value_maps_keyword) table = m.Table(style_class='table table-condensed table-striped') i = 0 for exposure_key, classifications in value_maps_keyword.items(): i += 1 exposure = definition(exposure_key) exposure_row = m.Row() exposure_row.add(m.Cell(m.ImportantText(tr('Exposure')))) exposure_row.add(m.Cell(exposure['name'])) table.add(exposure_row) classification_row = m.Row() classification_row.add( m.Cell(m.ImportantText(tr('Classification')))) active_classification = None for classification, value in classifications.items(): if value.get('active'): active_classification = definition(classification) if active_classification.get('name'): classification_row.add( m.Cell(active_classification['name'])) break if not active_classification: classification_row.add(m.Cell(tr('No classifications set.'))) continue table.add(classification_row) header = m.Row() header.add(m.Cell(tr('Class name'))) header.add(m.Cell(tr('Values'))) 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: value_map = classifications[ active_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) if i < len(value_maps_keyword): # Empty row empty_row = m.Row() empty_row.add(m.Cell('')) empty_row.add(m.Cell('')) table.add(empty_row) return table
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 other contexts. .. 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( 'With this tool you can set up numerous scenarios and run them all in ' 'one go. A typical use case may be where you define a number of e.g. ' 'flood impact scenarios all using a standard data set e.g. ' 'flood.shp. As new flood data becomes available you replace flood.shp ' 'and rerun the scenarios using the batch runner. Using this approach ' 'you can quickly produce regional contingency plans as your ' 'understanding of hazards changes. When you run the batch of ' 'scenarios, pdf reports are generated automatically and all placed in ' 'a single common directory making it easy for you to browse and ' 'disseminate the reports produced.'))) message.add(m.Paragraph(tr( 'When the batch process completes, it will also produce a summary ' 'report like this:'))) table = m.Table(style_class='table table-condensed table-striped') row = m.Row(m.Cell(tr('InaSAFE Batch Report File')), header=True) table.add(row) table.add(m.Row(m.Cell('P: gempa bumi Sumatran fault (Mw7.8)'))) table.add(m.Row(m.Cell('P: gempa di Yogya tahun 2006'))) table.add(m.Row(m.Cell('P: banjir jakarta 2007'))) table.add(m.Row(m.Cell('P: Tsunami di Maumere (Mw 8.1)'))) table.add(m.Row(m.Cell('P: gempa Mw6.5 Palu-Koro Fault'))) table.add(m.Row(m.Cell('P: gunung merapi meletus'))) table.add(m.Row(m.Cell('-----------------------------'))) table.add(m.Row(m.Cell(tr('Total passed: 6')))) table.add(m.Row(m.Cell(tr('Total failed: 0')))) table.add(m.Row(m.Cell(tr('Total tasks: 6')))) message.add(table) # message.add(m.Paragraph(tr( # 'For advanced users there is also the ability to batch run python ' # 'scripts using this tool, but this should be considered an ' # 'experimental</strong> feature still at this stage.'))) message.add(m.Paragraph(tr( 'Before running the Batch Runner you might want to use the \'save ' 'scenario\' tool to first save some scenarios on which you ' 'can let the batch runner do its work. This tool lets you run saved ' 'scenarios in one go. It lets you select scenarios or let run all ' 'scenarios in one go.'))) return 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 _generate_tables(self, aoi_mode=True): """Parses the postprocessing output as one table per postprocessor. TODO: This should rather return json and then have a helper method to make html from the JSON. :param aoi_mode: adds a Total in aggregation areas row to the calculated table :type aoi_mode: bool :returns: The html. :rtype: str """ message = m.Message() for processor, results_list in self.output.iteritems(): self.current_output_postprocessor = processor # results_list is for example: # [ # (PyQt4.QtCore.QString(u'Entire area'), OrderedDict([ # (u'Total', {'value': 977536, 'metadata': {}}), # (u'Female population', {'value': 508319, 'metadata': {}}), # (u'Weekly hygiene packs', {'value': 403453, 'metadata': { # 'description': 'Females hygiene packs for weekly use'}}) # ])) # ] # sorting using the first indicator of a postprocessor sorted_results = sorted( results_list, key=self._sort_no_data, reverse=True) # init table has_no_data = False table = m.Table( style_class='table table-condensed table-striped') table.caption = self.tr('Detailed %s report') % (tr( get_postprocessor_human_name(processor)).lower()) # Dirty hack to make "evacuated" comes out in the report. # Currently only MinimumNeeds that calculate from evacuation # percentage. if processor == 'MinimumNeeds': if 'evacuation_percentage' in self.function_parameters.keys(): table.caption = self.tr( 'Detailed %s report (for people needing ' 'evacuation)') % ( tr(get_postprocessor_human_name(processor)).lower() ) else: table.caption = self.tr( 'Detailed %s report (affected people)') % ( tr(get_postprocessor_human_name(processor)).lower() ) if processor in ['Gender', 'Age']: table.caption = self.tr( 'Detailed %s report (affected people)') % ( tr(get_postprocessor_human_name(processor)).lower()) header = m.Row() header.add(str(self.attribute_title).capitalize()) for calculation_name in sorted_results[0][1]: header.add(self.tr(calculation_name)) table.add(header) # used to calculate the totals row as per issue #690 postprocessor_totals = OrderedDict() for zone_name, calc in sorted_results: row = m.Row(zone_name) for indicator, calculation_data in calc.iteritems(): value = calculation_data['value'] value = str(unhumanize_number(value)) if value == self.aggregator.get_default_keyword('NO_DATA'): has_no_data = True value += ' *' try: postprocessor_totals[indicator] += 0 except KeyError: postprocessor_totals[indicator] = 0 else: value = int(value) try: postprocessor_totals[indicator] += value except KeyError: postprocessor_totals[indicator] = value row.add(format_int(value)) table.add(row) if not aoi_mode: # add the totals row row = m.Row(self.tr('Total in aggregation areas')) for _, total in postprocessor_totals.iteritems(): row.add(format_int(total)) table.add(row) # add table to message message.add(table) if has_no_data: message.add(m.EmphasizedText(self.tr( '* "%s" values mean that there where some problems while ' 'calculating them. This did not affect the other ' 'values.') % ( self.aggregator.get_default_keyword( 'NO_DATA')))) return message
def _generate_tables(self, aoi_mode=True): """Parses the postprocessing output as one table per postprocessor. TODO: This should rather return json and then have a helper method to make html from the JSON. :param aoi_mode: adds a Total in aggregation areas row to the calculated table :type aoi_mode: bool :returns: The html. :rtype: str """ message = m.Message() for processor, results_list in self.output.iteritems(): self.current_output_postprocessor = processor # results_list is for example: # [ # (PyQt4.QtCore.QString(u'Entire area'), OrderedDict([ # (u'Total', {'value': 977536, 'metadata': {}}), # (u'Female population', {'value': 508319, 'metadata': {}}), # (u'Weekly hygiene packs', {'value': 403453, 'metadata': { # 'description': 'Females hygiene packs for weekly use'}}) # ])) # ] # sorting using the first indicator of a postprocessor sorted_results = sorted(results_list, key=self._sort_no_data, reverse=True) # init table has_no_data = False table = m.Table(style_class='table table-condensed table-striped') name = get_postprocessor_human_name(processor).lower() translated_name = tr(name) if name == 'building type': table.caption = tr('Closed buildings') elif name == 'road type': table.caption = tr('Closed roads') elif name == 'people': table.caption = tr('Affected people') # Dirty hack to make "evacuated" come out in the report. # Currently only MinimumNeeds that calculate from evacuation # percentage. if processor == 'MinimumNeeds': if 'evacuation_percentage' in self.function_parameters.keys(): table.caption = tr( 'Detailed %s report ' '(for people needing evacuation)') % translated_name else: table.caption = tr('Detailed %s report ' '(affected people)') % translated_name if processor in ['Gender', 'Age']: table.caption = tr('Detailed %s report ' '(affected people)') % translated_name empty_table = not sorted_results[0][1] if empty_table: # Due to an error? The table is empty. message.add(table) message.add( m.EmphasizedText( tr('Could not compute the %s report.') % translated_name)) continue header = m.Row() header.add(str(self.attribute_title).capitalize()) for calculation_name in sorted_results[0][1]: header.add(self.tr(calculation_name)) table.add(header) # used to calculate the totals row as per issue #690 postprocessor_totals = OrderedDict() null_index = 0 # counting how many null value in the data for zone_name, calc in sorted_results: if isinstance(zone_name, QPyNullVariant): # I have made sure that the zone_name won't be Null in # run method. But just in case there is something wrong. zone_name = tr('Unnamed Area %s' % null_index) null_index += 1 if name == 'road type': # We add the unit 'meter' as we are counting roads. # proper format for i186 zone_name = tr('%(zone_name)s (m)') % { 'zone_name': tr(zone_name) } row = m.Row(zone_name) for indicator, calculation_data in calc.iteritems(): value = calculation_data['value'] value = str(unhumanize_number(value)) if value == self.aggregator.get_default_keyword('NO_DATA'): has_no_data = True try: postprocessor_totals[indicator] += 0 except KeyError: postprocessor_totals[indicator] = 0 else: value = int(value) try: postprocessor_totals[indicator] += value except KeyError: postprocessor_totals[indicator] = value row.add(format_int(value)) table.add(row) if not aoi_mode: # add the totals row row = m.Row(self.tr('Total in aggregation areas')) for _, total in postprocessor_totals.iteritems(): row.add(format_int(total)) table.add(row) # add table to message message.add(table) if has_no_data: message.add( m.EmphasizedText( self. tr('"%s" values mean that there where some problems while ' 'calculating them. This did not affect the other ' 'values.') % (self.aggregator.get_default_keyword('NO_DATA')))) caption = m.EmphasizedText( self.tr( 'Columns and rows containing only 0 or "%s" values are ' 'excluded from the tables.' % self.aggregator.get_default_keyword('NO_DATA'))) message.add(m.Paragraph(caption, style_class='caption')) return message
def _keyword_to_row(self, keyword, value, wrap_slash=False): """Helper to make a message row from a keyword. .. versionadded:: 3.2 Use this when constructing a table from keywords to display as part of a message object. :param keyword: The keyword to be rendered. :type keyword: str :param value: Value of the keyword to be rendered. :type value: basestring :param wrap_slash: Whether to replace slashes with the slash plus the html <wbr> tag which will help to e.g. wrap html in small cells if it contains a long filename. Disabled by default as it may cause side effects if the text contains html markup. :type wrap_slash: bool :returns: A row to be added to a messaging table. :rtype: safe.messaging.items.row.Row """ row = m.Row() # Translate titles explicitly if possible if keyword == 'title': value = tr(value) # # See #2569 if keyword == 'url': if isinstance(value, QUrl): value = value.toString() if keyword == 'date': if isinstance(value, QDateTime): value = value.toString('d MMM yyyy') elif isinstance(value, datetime): value = value.strftime('%d %b %Y') # we want to show the user the concept name rather than its key # if possible. TS keyword_definition = definition(keyword) if keyword_definition is None: keyword_definition = tr(keyword.capitalize().replace('_', ' ')) else: try: keyword_definition = keyword_definition['name'] except KeyError: # Handling if name is not exist. keyword_definition = keyword_definition['key'].capitalize() keyword_definition = keyword_definition.replace('_', ' ') # We deal with some special cases first: # In this case the value contains a DICT that we want to present nicely if keyword in [ 'value_map', 'inasafe_fields', 'inasafe_default_values' ]: value = self._dict_to_row(value) elif keyword == 'value_maps': value = self._value_maps_row(value) elif keyword == 'thresholds': value = self._threshold_to_row(value) # In these KEYWORD cases we show the DESCRIPTION for # the VALUE keyword_definition elif keyword in ['classification']: # get the keyword_definition for this class from definitions value = definition(value) value = value['description'] # In these VALUE cases we show the DESCRIPTION for # the VALUE keyword_definition elif value in []: # get the keyword_definition for this class from definitions value = definition(value) value = value['description'] # In these VALUE cases we show the NAME for the VALUE # keyword_definition elif value in [ 'multiple_event', 'single_event', 'point', 'line', 'polygon' 'field' ]: # get the name for this class from definitions value = definition(value) value = value['name'] # otherwise just treat the keyword as literal text else: # Otherwise just directly read the value value = get_string(value) key = m.ImportantText(keyword_definition) row.add(m.Cell(key)) row.add(m.Cell(value, wrap_slash=wrap_slash)) return row