def buildPostProcessorForm(self, theParams): """Build Post Processor Tab Args: * theParams - dictionary containing element of form Returns: not applicable """ # create postprocessors tab myTab = QWidget() myFormLayout = QFormLayout(myTab) myFormLayout.setLabelAlignment(Qt.AlignLeft) self.tabWidget.addTab(myTab, self.tr('Postprocessors')) self.tabWidget.tabBar().setVisible(True) # create element for the tab myValues = {} for myLabel, myOptions in theParams.items(): myInputValues = {} # NOTE (gigih) : 'params' is assumed as dictionary if 'params' in myOptions: myGroupBox = QGroupBox() myGroupBox.setCheckable(True) myGroupBox.setTitle(get_postprocessor_human_name(myLabel)) # NOTE (gigih): is 'on' always exist?? myGroupBox.setChecked(myOptions.get('on')) myInputValues['on'] = self.bind(myGroupBox, 'checked', bool) myLayout = QFormLayout(myGroupBox) myGroupBox.setLayout(myLayout) # create widget element from 'params' myInputValues['params'] = {} for myKey, myValue in myOptions['params'].items(): myHumanName = get_postprocessor_human_name(myKey) myInputValues['params'][myKey] = self.buildWidget( myLayout, myHumanName, myValue) myFormLayout.addRow(myGroupBox, None) elif 'on' in myOptions: myCheckBox = QCheckBox() myCheckBox.setText(get_postprocessor_human_name(myLabel)) myCheckBox.setChecked(myOptions['on']) myInputValues['on'] = self.bind(myCheckBox, 'checked', bool) myFormLayout.addRow(myCheckBox, None) else: raise NotImplementedError('This case is not handled for now') myValues[myLabel] = myInputValues self.values['postprocessors'] = myValues
def build_post_processor_form(self, parameters): """Build Post Processor Tab. :param parameters: A Dictionary containing element of form :type parameters: dict """ # create postprocessors tab tab = QWidget() form_layout = QFormLayout(tab) form_layout.setLabelAlignment(Qt.AlignLeft) self.tabWidget.addTab(tab, self.tr('Postprocessors')) self.tabWidget.tabBar().setVisible(True) # create element for the tab values = OrderedDict() for label, options in parameters.items(): input_values = OrderedDict() # NOTE (gigih) : 'params' is assumed as dictionary if 'params' in options: group_box = QGroupBox() group_box.setCheckable(True) group_box.setTitle(get_postprocessor_human_name(label)) # NOTE (gigih): is 'on' always exist?? # (MB) should always be there group_box.setChecked(options.get('on')) input_values['on'] = self.bind(group_box, 'checked', bool) layout = QFormLayout(group_box) group_box.setLayout(layout) # create widget element from 'params' input_values['params'] = OrderedDict() for key, value in options['params'].items(): input_values['params'][key] = self.build_widget( layout, key, value) form_layout.addRow(group_box, None) elif 'on' in options: checkbox = QCheckBox() checkbox.setText(get_postprocessor_human_name(label)) checkbox.setChecked(options['on']) input_values['on'] = self.bind(checkbox, 'checked', bool) form_layout.addRow(checkbox, None) else: raise NotImplementedError('This case is not handled for now') values[label] = input_values self.values['postprocessors'] = values
def emit_pre_run_message(self): """Inform the user about parameters before starting the processing.""" title = tr('Processing started') details = tr( 'Please wait - processing may take a while depending on your ' 'hardware configuration and the analysis extents and data.') # trap for issue 706 try: exposure_name = self.exposure.name hazard_name = self.hazard.name # aggregation layer could be set to AOI so no check for that except AttributeError: title = tr('No valid layers') details = tr( 'Please ensure your hazard and exposure layers are set ' 'in the question area and then press run again.') message = m.Message(LOGO_ELEMENT, m.Heading(title, **WARNING_STYLE), m.Paragraph(details)) raise NoValidLayerError(message) text = m.Text( tr('This analysis will calculate the impact of'), m.EmphasizedText(hazard_name), tr('on'), m.EmphasizedText(exposure_name), ) if self.aggregation is not None: try: aggregation_name = self.aggregation.name # noinspection PyTypeChecker text.add( m.Text(tr('and bullet list the results'), m.ImportantText(tr('aggregated by')), m.EmphasizedText(aggregation_name))) except AttributeError: pass text.add('.') message = m.Message(LOGO_ELEMENT, m.Heading(title, **PROGRESS_UPDATE_STYLE), m.Paragraph(details), m.Paragraph(text)) try: # add which postprocessors will run when appropriated # noinspection PyTypeChecker post_processors_names = self.parameters['postprocessors'] post_processors = get_postprocessors(post_processors_names) message.add( m.Paragraph(tr('The following postprocessors will be used:'))) bullet_list = m.BulletedList() for name, post_processor in post_processors.iteritems(): bullet_list.add('%s: %s' % (get_postprocessor_human_name(name), post_processor.description())) message.add(bullet_list) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable pass send_static_message(self, message)
def _generate_data(self, aoi_mode=True): """Parses the postprocessing output as dictionary. :param aoi_mode: adds a Total in aggregation areas row to the calculated table :type aoi_mode: bool :returns: The dictionary of postprocessing. :rtype: dict """ result = {} 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 = {'notes': []} 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 try: empty_table = not sorted_results[0][1] except IndexError: empty_table = True if empty_table: # The table is empty. # Due to an error or because every lines were removed. table['attributes'] = [] table['fields'] = [] table['notes'].append( tr('The report "%s" is empty.') % translated_name) result['processor'] = table continue header = [str(self.attribute_title).capitalize()] for calculation_name in sorted_results[0][1]: header.append(self.tr(calculation_name)) table['attributes'] = 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 fields = [] 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. zone_name = tr('%(zone_name)s (m)' % {'zone_name': zone_name}) 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.append(value) fields.append(row) if not aoi_mode: # add the totals row row = [self.tr('Total in aggregation areas')] for _, total in postprocessor_totals.iteritems(): row.append(total) fields.append(row) table['fields'] = fields if has_no_data: table['notes'].append( 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'))) table['notes'].append( self.tr( 'Columns and rows containing only 0 or "%s" values are ' 'excluded from the tables.' % self.aggregator.get_default_keyword('NO_DATA'))) result[processor] = table return result
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 _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() if name == 'building type': table.caption = self.tr('Closed buildings') elif name == 'road type': table.caption = self.tr('Closed roads') elif name == 'people': table.caption = self.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 = 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()) 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(self.tr( 'Could not compute the %s report.' % tr(get_postprocessor_human_name(processor)).lower()))) 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 = 'Unnamed Area %s' % null_index null_index += 1 if name == 'road type': # We add the unit 'meter' as we are counting roads. zone_name = '%s (m)' % 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 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')))) caption = m.EmphasizedText(self.tr( 'Columns containing exclusively 0 and "%s" ' 'have not been shown in the table.' % self.aggregator.get_default_keyword('NO_DATA'))) message.add( m.Paragraph( caption, style_class='caption') ) return message
def _generate_data(self, aoi_mode=True): """Parses the postprocessing output as dictionary. :param aoi_mode: adds a Total in aggregation areas row to the calculated table :type aoi_mode: bool :returns: The dictionary of postprocessing. :rtype: dict """ result = {} 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 = {'notes': []} 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 try: empty_table = not sorted_results[0][1] except IndexError: empty_table = True if empty_table: # The table is empty. # Due to an error or because every lines were removed. table['attributes'] = [] table['fields'] = [] table['notes'].append( tr('The report "%s" is empty.') % translated_name) result['processor'] = table continue header = [str(self.attribute_title).capitalize()] for calculation_name in sorted_results[0][1]: header.append(self.tr(calculation_name)) table['attributes'] = 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 fields = [] 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. zone_name = tr( '%(zone_name)s (m)' % {'zone_name': zone_name}) 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.append(value) fields.append(row) if not aoi_mode: # add the totals row row = [self.tr('Total in aggregation areas')] for _, total in postprocessor_totals.iteritems(): row.append(total) fields.append(row) table['fields'] = fields if has_no_data: table['notes'].append(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'))) table['notes'].append(self.tr( 'Columns and rows containing only 0 or "%s" values are ' 'excluded from the tables.' % self.aggregator.get_default_keyword('NO_DATA'))) result[processor] = table return result
def emit_pre_run_message(self): """Inform the user about parameters before starting the processing.""" title = tr('Processing started') details = tr( 'Please wait - processing may take a while depending on your ' 'hardware configuration and the analysis extents and data.') # trap for issue 706 try: exposure_name = self.exposure.name hazard_name = self.hazard.name # aggregation layer could be set to AOI so no check for that except AttributeError: title = tr('No valid layers') details = tr( 'Please ensure your hazard and exposure layers are set ' 'in the question area and then press run again.') message = m.Message( LOGO_ELEMENT, m.Heading(title, **WARNING_STYLE), m.Paragraph(details)) raise NoValidLayerError(message) text = m.Text( tr('This analysis will calculate the impact of'), m.EmphasizedText(hazard_name), tr('on'), m.EmphasizedText(exposure_name), ) if self.aggregation is not None: try: aggregation_name = self.aggregation.name # noinspection PyTypeChecker text.add(m.Text( tr('and bullet list the results'), m.ImportantText(tr('aggregated by')), m.EmphasizedText(aggregation_name))) except AttributeError: pass text.add('.') message = m.Message( LOGO_ELEMENT, m.Heading(title, **PROGRESS_UPDATE_STYLE), m.Paragraph(details), m.Paragraph(text)) try: # add which postprocessors will run when appropriated # noinspection PyTypeChecker post_processors_names = self.parameters['postprocessors'] post_processors = get_postprocessors(post_processors_names) message.add(m.Paragraph(tr( 'The following postprocessors will be used:'))) bullet_list = m.BulletedList() for name, post_processor in post_processors.iteritems(): bullet_list.add('%s: %s' % ( get_postprocessor_human_name(name), post_processor.description())) message.add(bullet_list) except (TypeError, KeyError): # TypeError is for when function_parameters is none # KeyError is for when ['postprocessors'] is unavailable pass send_static_message(self, message)