def testClickHandler(self):
        """
        Tests the request population callback
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=fld2:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN', 1000])
        layer.dataProvider().addFeatures([f])
        quota_layer = make_quota_layer()
        registry = LinzElectoralDistrictRegistry(
            source_layer=layer,
            quota_layer=quota_layer,
            electorate_type='',
            source_field='fld1',
            title_field='fld2')

        dock = RedistrictingDockWidget(IFACE)
        handler = LinzRedistrictGuiHandler(redistrict_dock=dock, district_registry=registry)
        handler.request_population_callback = None
        handler.show_stats_for_district('test4')
        handler.request_population()

        def callback(electorate):
            """
            Dummy callback
            """
            callback.electorate = electorate

        callback.electorate = None
        handler.request_population_callback = callback
        handler.request_population()
        self.assertEqual(callback.electorate, 'test4')
    def paint(self, painter, option, widget):  # pylint: disable=missing-docstring, unused-argument, too-many-locals
        if self.image is not None:
            painter.drawImage(0, 0, self.image)
            return

        image_size = self.canvas.mapSettings().outputSize()
        self.image = QImage(image_size.width(), image_size.height(), QImage.Format_ARGB32)
        self.image.fill(0)
        image_painter = QPainter(self.image)
        render_context = QgsRenderContext.fromQPainter(image_painter)

        image_painter.setRenderHint(QPainter.Antialiasing, True)

        rect = self.canvas.mapSettings().visibleExtent()
        request = QgsFeatureRequest()
        request.setFilterRect(rect)
        request.setFilterExpression(QgsExpression.createFieldEqualityExpression('type', self.task))

        for f in self.electorate_layer.getFeatures(request):
            #    pole, dist = f.geometry().clipped(rect).poleOfInaccessibility(rect.width() / 30)
            pixel = self.toCanvasCoordinates(f.geometry().clipped(rect).centroid().asPoint())

            estimated_pop = self.new_populations[f.id()] if f.id() in self.new_populations else \
            self.original_populations[f.id()]

            variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(self.quota, estimated_pop)
            text_string = ['{}'.format(f['name']),
                           '{}'.format(int(estimated_pop)),
                           '{}{}%'.format('+' if variance > 0 else '', variance)]
            QgsTextRenderer().drawText(QPointF(pixel.x(), pixel.y()), 0, QgsTextRenderer.AlignCenter,
                                       text_string, render_context, self.text_format)

        image_painter.end()

        painter.drawImage(0, 0, self.image)
    def testDialog(self):
        """
        Test dialog functionality
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=code:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int&field=electorate_id_stats:string&field=expected_regions:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN', 1, False])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest3", 'GN', 2, True])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'GN', 1, False])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld1',
                                            title_field='fld1')

        dlg = DeprecateElectorateDialog(electorate_registry=reg)
        self.assertIsNotNone(dlg)

        self.assertEqual(
            [dlg.list.item(r).text() for r in range(dlg.list.count())],
            ['test4', '*test2', 'test3'])
 def testDistrictTypeString(self):
     """
     Test district_type_title
     """
     self.assertEqual(
         LinzElectoralDistrictRegistry.district_type_title('GN'),
         'General North Island')
     self.assertEqual(
         LinzElectoralDistrictRegistry.district_type_title('GS'),
         'General South Island')
     self.assertEqual(
         LinzElectoralDistrictRegistry.district_type_title('M'), 'Māori')
     try:
         LinzElectoralDistrictRegistry.district_type_title('X')
         assert 'Unexpected success - expecting assert'
     except:  # noqa, pylint: disable=bare-except
         pass
 def testVariationExceedsTolerance(self):
     """
     Test determining if an electorate's variation exceeds the tolerance
     """
     self.assertFalse(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=50000, population=51000))
     self.assertFalse(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=50000, population=49000))
     self.assertTrue(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=100000, population=105000))
     self.assertTrue(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=100000, population=95000))
     self.assertTrue(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=50000, population=56000))
     self.assertTrue(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=50000, population=44000))
     self.assertFalse(
         LinzElectoralDistrictRegistry.variation_exceeds_allowance(
             quota=50000, population=NULL))
 def testVariationFromQuota(self):
     """
     Test calculating variation from quota
     """
     self.assertEqual(
         LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
             quota=50000, population=51000), 2)
     self.assertEqual(
         LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
             quota=50000, population=55000), 10)
     self.assertEqual(
         LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
             quota=50000, population=49000), -2)
     self.assertEqual(
         LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
             quota=50000, population=45000), -10)
     self.assertIsNone(
         LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
             quota=50000, population=NULL))
    def testPopulations(self):
        """
        Test retrieving populations for districts
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=fld2:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN', 1000])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest3", 'GS', 2000])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'M', 3000])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld1',
                                            title_field='fld1')

        self.assertEqual(reg.get_estimated_population('test4'), 1000)
        self.assertEqual(reg.get_estimated_population('test2'), 2000)
        self.assertEqual(reg.get_estimated_population('test3'), 3000)
        try:
            reg.get_estimated_population('X')
            assert 'Unexpected success - expecting assert'
        except:  # noqa, pylint: disable=bare-except
            pass
    def testCodes(self):
        """
        Test retrieving codes for electorates
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=code:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test1", "xtest1", 'GN'])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest2", 'GS'])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'M'])
        ok, (f, f2, f3) = layer.dataProvider().addFeatures([f, f2, f3])
        self.assertTrue(ok)
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld1',
                                            title_field='fld1')

        self.assertEqual(reg.get_code_for_electorate(f.id()), 'xtest1')
        self.assertEqual(reg.get_code_for_electorate(f2.id()), 'xtest2')
        self.assertEqual(reg.get_code_for_electorate(f3.id()), 'xtest3')
        try:
            reg.get_code_for_electorate(-10)
            assert 'Unexpected success - expecting assert'
        except:  # noqa, pylint: disable=bare-except
            pass
    def testDeprecation(self):
        """
        Test deprecating electorates
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=fld1:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, "code4", "test4", 'GN', 1000, True])
        f2 = QgsFeature()
        f2.setAttributes([2, "code2", "test2", 'GN', 2000, False])
        f3 = QgsFeature()
        f3.setAttributes([3, "code3", "test3", 'GN', 3000, True])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='electorate_id',
                                            title_field='fld1')

        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [True, False, True])
        reg.toggle_electorate_deprecation(1)
        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [False, False, True])
        reg.toggle_electorate_deprecation(1)
        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [True, False, True])
        reg.toggle_electorate_deprecation(2)
        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [True, True, True])
        reg.toggle_electorate_deprecation(3)
        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [True, True, False])
    def testCreateElectorate(self):
        """
        Test creating electorates
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=fld1:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int&field=electorate_id_stats:string&field=expected_regions:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, "code4", "test4", 'GN', 1000, 0])
        f2 = QgsFeature()
        f2.setAttributes([2, "code2", "test2", 'GN', 2000, 0])
        f3 = QgsFeature()
        f3.setAttributes([3, "code3", "test3", 'M', 3000, 1])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='electorate_id',
                                            title_field='fld1')

        res, error = reg.create_electorate('test2', 'test2', 1)
        self.assertFalse(res)
        self.assertIn('already exists', error)

        # duplicate name check is across electorate types
        res, error = reg.create_electorate('test3', 'test3', 1)
        self.assertFalse(res)
        self.assertIn('already exists', error)

        # valid
        res, error = reg.create_electorate('code5', 'test5', 1)
        self.assertTrue(res)
        self.assertFalse(error)
        self.assertEqual([f['electorate_id'] for f in layer.getFeatures()],
                         [1, 2, 3, 4])
        self.assertEqual([f['fld1'] for f in layer.getFeatures()],
                         ['test4', 'test2', 'test3', 'test5'])
        self.assertEqual([f['code'] for f in layer.getFeatures()],
                         ['code4', 'code2', 'code3', 'code5'])
        self.assertEqual([f['type'] for f in layer.getFeatures()],
                         ['GN', 'GN', 'M', 'GN'])
        self.assertEqual([f['deprecated'] for f in layer.getFeatures()],
                         [0, 0, 1, 0])
        self.assertEqual([f['scenario_id'] for f in layer.getFeatures()],
                         [NULL, NULL, NULL, 1])
        self.assertEqual(
            [f['electorate_id_stats'] for f in layer.getFeatures()],
            [NULL, NULL, NULL, 'Ncode5'])
        self.assertEqual([f['expected_regions'] for f in layer.getFeatures()],
                         [NULL, NULL, NULL, 1])

        res, error = reg.create_electorate('test5', 'test5', 1)
        self.assertFalse(res)
        self.assertIn('already exists', error)
    def __init__(self,
                 electorate_registry: LinzElectoralDistrictRegistry,
                 parent=None):
        super().__init__(parent)

        self.electorate_registry = electorate_registry

        self.setWindowTitle(self.tr('Deprecate Electorate'))

        layout = QVBoxLayout()

        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(
            self.tr('Search for {}').format(
                electorate_registry.type_string_sentence()))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([
            electorate_registry.source_field_index,
            electorate_registry.title_field_index,
            electorate_registry.deprecated_field_index
        ])
        self.list = QListWidget()
        for f in electorate_registry.source_layer.getFeatures(request):
            title = f[electorate_registry.title_field_index]
            code = f[electorate_registry.source_field_index]
            deprecated = f[electorate_registry.deprecated_field_index]

            if deprecated:
                title = '*' + title
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, code)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        self.setLayout(layout)
        self.list.itemDoubleClicked.connect(self.accept)
    def __init__(self,
                 registry: LinzElectoralDistrictRegistry,
                 context: LinzRedistrictingContext,
                 parent=None):
        super().__init__(parent)
        self.existing_names = list(registry.district_titles().keys())
        self.existing_codes = [
            f['code'] for f in registry.source_layer.getFeatures()
        ]

        self.setWindowTitle(
            self.tr('Create New {} Electorate').format(
                context.get_name_for_current_task()))

        dialog_layout = QVBoxLayout()
        label = QLabel(
            self.tr('Enter name for new {} electorate:').format(
                context.get_name_for_current_task()))
        dialog_layout.addWidget(label)

        self.name_line_edit = QLineEdit()
        self.name_line_edit.setPlaceholderText('Electorate name')
        dialog_layout.addWidget(self.name_line_edit)

        label = QLabel(self.tr('Enter code for new electorate:'))
        dialog_layout.addWidget(label)

        self.code_line_edit = QLineEdit()
        self.code_line_edit.setPlaceholderText('Electorate code')
        dialog_layout.addWidget(self.code_line_edit)

        self.feedback_label = QLabel()
        dialog_layout.addWidget(self.feedback_label)

        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                           | QDialogButtonBox.Cancel)
        dialog_layout.addWidget(self.button_box)
        self.button_box.rejected.connect(self.reject)
        self.button_box.accepted.connect(self.accept)

        self.name_line_edit.textChanged.connect(self.name_changed)
        self.code_line_edit.textChanged.connect(self.code_changed)

        self.setLayout(dialog_layout)
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
    def testGetStatsNzValues(self):
        """
        Test retrieving cached stats nz values
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=fld1:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, "code4", "test4", 'GN', 1000, True])
        f2 = QgsFeature()
        f2.setAttributes([2, "code2", "test2", 'GN', 2000, False])
        f3 = QgsFeature()
        f3.setAttributes([3, "code3", "test3", 'GN', 3000, True])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='electorate_id',
                                            title_field='fld1')

        reg.update_stats_nz_values(1, {
            'currentPopulation': 1111,
            'varianceYear1': 1.5,
            'varianceYear2': -1.1
        })

        self.assertEqual(reg.get_stats_nz_calculations(1), {
            'currentPopulation': 1111,
            'varianceYear1': 1.5,
            'varianceYear2': -1.1
        })
        self.assertEqual(
            reg.get_stats_nz_calculations(2), {
                'currentPopulation': NULL,
                'varianceYear1': NULL,
                'varianceYear2': NULL
            })
    def testVectorDistrictAtPoint(self):
        """
        Test getting vector layer district at point
        """
        layer = QgsVectorLayer(
            "Polygon?crs=EPSG:4326&field=fld1:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["GN district", "GN"])
        f.setGeometry(
            QgsGeometry.fromWkt('Polygon((1 10, 10 10, 10 20, 1 20, 1 10))'))
        f2 = QgsFeature()
        f2.setAttributes(["M district", "M"])
        f2.setGeometry(
            QgsGeometry.fromWkt('Polygon((1 10, 10 10, 10 20, 1 20, 1 10))'))
        layer.dataProvider().addFeatures([f, f2])

        quota_layer = make_quota_layer()
        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='fld1',
                                            title_field='fld1')

        self.assertEqual(
            reg.get_district_at_point(
                QgsRectangle(5, 15, 5.2, 16.2),
                QgsCoordinateReferenceSystem('EPSG:4326')), 'GN district')
        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='M',
                                            source_field='fld1',
                                            title_field='fld1')
        self.assertEqual(
            reg.get_district_at_point(
                QgsRectangle(5, 15, 5.2, 16.2),
                QgsCoordinateReferenceSystem('EPSG:4326')), 'M district')
    def testExportTask(self):  # pylint: disable=too-many-locals, too-many-statements
        """
        Test export task
        """
        layer = make_scenario_layer()
        mb_electorate_layer = QgsVectorLayer(
            "NoGeometry?field=id:int&field=scenario_id:int&field=meshblock_number:int&field=gn_id:int&field=gs_id:int&field=m_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, 1, 11, 1, 0, 7])
        f2 = QgsFeature()
        f2.setAttributes([2, 1, 12, 2, 0, 7])
        f3 = QgsFeature()
        f3.setAttributes([3, 1, 13, 2, 0, 7])
        f4 = QgsFeature()
        f4.setAttributes([4, 1, 14, 3, 4, 8])
        f5 = QgsFeature()
        f5.setAttributes([5, 1, 15, 0, 5, 8])
        f6 = QgsFeature()
        f6.setAttributes([6, 1, 16, 0, 5, 8])
        mb_electorate_layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6])

        reg = ScenarioRegistry(
            source_layer=layer,
            id_field='id',
            name_field='name',
            meshblock_electorate_layer=mb_electorate_layer
        )
        electorate_layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=type:string&field=estimated_pop:int&field=scenario_id:int&field=deprecated:int&field=invalid:int&field=invalid_reason:string&field=name:string&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, "test1", 'GN', 1, 0, 0, 1, 'old invalid'])
        f2 = QgsFeature()
        f2.setAttributes([2, "test2", 'GN', 1, 0, 0, 1, 'old invalid 2'])
        f3 = QgsFeature()
        f3.setAttributes([3, "test3", 'GN', 1, 0, 0, 1, 'old invalid 3'])
        f4 = QgsFeature()
        f4.setAttributes([4, "test4", 'GS', 1, 0, 0, 1, 'old invalid 4'])
        f5 = QgsFeature()
        f5.setAttributes([5, "test5", 'GS', 1, 0, 0, 1, 'old invalid 5'])
        f6 = QgsFeature()
        f6.setAttributes([6, "test6", 'GS', 1, 0, 0, 1, 'old invalid 6'])
        f7 = QgsFeature()
        f7.setAttributes([7, "test7", 'M', 1, 0, 0, 1, 'old invalid 7'])
        f8 = QgsFeature()
        f8.setAttributes([8, "test8", 'M', 1, 0, 0, 1, 'old invalid 8'])
        electorate_layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6, f7, f8])

        meshblock_layer = QgsVectorLayer(
            "Polygon?crs=EPSG:4326&field=MeshblockNumber:string&field=offline_pop_m:int&field=offline_pop_gn:int&field=offline_pop_gs:int&field=staged_electorate:int&field=offshore:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["11", 5, 58900, 0])
        f.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        f2 = QgsFeature()
        f2.setAttributes(["12", 6, 57000, 0])
        f2.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        f3 = QgsFeature()
        f3.setAttributes(["13", 7, 2000, 0])
        f3.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        f4 = QgsFeature()
        f4.setAttributes(["14", 8, 0, 20])
        f4.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        f5 = QgsFeature()
        f5.setAttributes(["15", 9, 0, 30])
        f5.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        f6 = QgsFeature()
        f6.setAttributes(["16", 10, 0, 40])
        f6.setGeometry(QgsGeometry.fromWkt('Polygon((1 1, 2 1, 2 2, 1 2, 1 1))'))
        self.assertTrue(meshblock_layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6]))

        quota_layer = make_quota_layer()
        user_log_layer = make_user_log_layer()
        f = QgsFeature()
        f.setAttributes([1, NULL, 'user', 'v1', 1, '11', 'GN', 1, 2])
        user_log_layer.dataProvider().addFeature(f)

        electorate_registry = LinzElectoralDistrictRegistry(source_layer=electorate_layer, source_field='electorate_id',
                                                            title_field='code', electorate_type='GN',
                                                            quota_layer=quota_layer)

        out_file = '/tmp/test.gpkg'
        task = ExportTask(task_name='', dest_file=out_file, electorate_registry=electorate_registry,
                          meshblock_layer=meshblock_layer,
                          meshblock_number_field_name='MeshblockNumber', scenario_registry=reg, scenario=1,
                          user_log_layer=user_log_layer)

        self.assertTrue(task.run(), task.message)

        out_electorate_layer = QgsVectorLayer('{}|layername=electorates'.format(out_file), 'electorates', 'ogr')
        self.assertTrue(out_electorate_layer.isValid())
        self.assertEqual([f.attributes() for f in out_electorate_layer.getFeatures()], [[1, 'GN', 'test1', NULL],
                                                                                        [2, 'GN', 'test2', NULL],
                                                                                        [3, 'GN', 'test3', NULL],
                                                                                        [4, 'GS', 'test4', NULL],
                                                                                        [5, 'GS', 'test5', NULL],
                                                                                        [6, 'M', 'test7', NULL],
                                                                                        [7, 'M', 'test8', NULL]])
        self.assertEqual([f.geometry().asWkt() for f in out_electorate_layer.getFeatures()],
                         ['Polygon ((1 1, 2 1, 2 2, 1 2, 1 1))',
                          'Polygon ((2 1, 1 1, 1 2, 2 2, 2 1))',
                          'Polygon ((1 1, 2 1, 2 2, 1 2, 1 1))',
                          'Polygon ((1 1, 2 1, 2 2, 1 2, 1 1))',
                          'Polygon ((2 1, 1 1, 1 2, 2 2, 2 1))',
                          'Polygon ((2 1, 1 1, 1 2, 2 2, 2 1))',
                          'Polygon ((2 1, 1 1, 1 2, 2 2, 2 1))'])
        out_mb_layer = QgsVectorLayer('{}|layername=meshblocks'.format(out_file), 'electorates', 'ogr')
        self.assertTrue(out_mb_layer.isValid())
        self.assertEqual([f.attributes() for f in out_mb_layer.getFeatures()], [[1, 11, 'test1', NULL, 'test7'],
                                                                                [2, 12, 'test2', NULL, 'test7'],
                                                                                [3, 13, 'test2', NULL, 'test7'],
                                                                                [4, 14, 'test3', 'test4', 'test8'],
                                                                                [5, 15, NULL, 'test5', 'test8'],
                                                                                [6, 16, NULL, 'test5', 'test8']])
        out_log_layer = QgsVectorLayer('{}|layername=user_log'.format(out_file), 'electorates', 'ogr')
        self.assertTrue(out_log_layer.isValid())
        self.assertEqual([f.attributes() for f in out_log_layer.getFeatures()],
                         [[1, 1, NULL, 'user', 'v1', 1, '11', 'GN', 1, 2]])
    def testLinzDistrictRegistry(self):
        """
        Test a LinzDistrictRegistry
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=fld2:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN', 1000])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest3", 'GS', 2000])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'M', 3000])
        f4 = QgsFeature()
        f4.setAttributes(["test1", NULL, 'GN', 4000])
        f5 = QgsFeature()
        f5.setAttributes(["test2", "xtest2", 'GS', 5000])
        f6 = QgsFeature()
        f6.setAttributes(["test5", "xtest5", 'GN', 5000, True])
        layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld1',
                                            title_field='fld1')
        self.assertEqual(reg.district_list(),
                         ['test1', 'test2', 'test3', 'test4'])

        self.assertEqual(reg.get_district_type('test1'), 'GN')
        self.assertEqual(reg.get_district_type('test2'), 'GS')
        self.assertEqual(reg.get_district_type('test3'), 'M')
        self.assertEqual(reg.get_district_type('test4'), 'GN')

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld2',
                                            title_field='fld2')
        self.assertEqual(reg.district_list(), ['xtest1', 'xtest2', 'xtest3'])

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='fld1',
                                            title_field='fld2')
        self.assertEqual(reg.district_list(), ['test1', 'test4'])
        self.assertEqual(reg.district_titles(), {
            NULL: 'test1',
            'xtest1': 'test4'
        })
        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GS',
                                            source_field='fld1',
                                            title_field='fld2')
        self.assertEqual(reg.district_list(), ['test2'])
        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='M',
                                            source_field='fld1',
                                            title_field='fld2')
        self.assertEqual(reg.district_list(), ['test3'])
    def testUpdateStatsNzValues(self):
        """
        Test setting the Stats NZ stored api values for electorates
        """
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=fld1:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, "code4", "test4", 'GN', 1000, True])
        f2 = QgsFeature()
        f2.setAttributes([2, "code2", "test2", 'GN', 2000, False])
        f3 = QgsFeature()
        f3.setAttributes([3, "code3", "test3", 'GN', 3000, True])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='GN',
                                            source_field='electorate_id',
                                            title_field='fld1')

        reg.update_stats_nz_values(1, {
            'currentPopulation': 1111,
            'varianceYear1': 1.5,
            'varianceYear2': -1.1
        })
        self.assertEqual(
            [f.attributes()[-4:-1] for f in layer.getFeatures()],
            [[1111, 1.5, -1.1], [NULL, NULL, NULL], [NULL, NULL, NULL]])
        reg.update_stats_nz_values(2, {
            'currentPopulation': 1112,
            'varianceYear1': 1.6,
            'varianceYear2': -1.2
        })
        self.assertEqual(
            [f.attributes()[-4:-1] for f in layer.getFeatures()],
            [[1111, 1.5, -1.1], [1112, 1.6, -1.2], [NULL, NULL, NULL]])

        reg.flag_stats_nz_updating(3)
        self.assertEqual(
            [f.attributes()[-4:-1] for f in layer.getFeatures()],
            [[1111, 1.5, -1.1], [1112, 1.6, -1.2], [-1, NULL, NULL]])
        reg.update_stats_nz_values(3, {
            'currentPopulation': 1113,
            'varianceYear1': 1.7,
            'varianceYear2': -1.3
        })
        self.assertEqual(
            [f.attributes()[-4:-1] for f in layer.getFeatures()],
            [[1111, 1.5, -1.1], [1112, 1.6, -1.2], [1113, 1.7, -1.3]])
        reg.flag_stats_nz_updating(1)
        self.assertEqual(
            [f.attributes()[-4:-1] for f in layer.getFeatures()],
            [[-1, NULL, NULL], [1112, 1.6, -1.2], [1113, 1.7, -1.3]])
示例#18
0
    def testDialog(self):
        """
        Test dialog functionality
        """
        scenario_layer = make_scenario_layer()
        scenario_registry = ScenarioRegistry(source_layer=scenario_layer,
                                             id_field='id',
                                             name_field='name',
                                             meshblock_electorate_layer=None)

        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=code:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN'])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest3", 'GS'])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'M'])
        layer.dataProvider().addFeatures([f, f2, f3])
        quota_layer = make_quota_layer()

        reg = LinzElectoralDistrictRegistry(source_layer=layer,
                                            quota_layer=quota_layer,
                                            electorate_type='',
                                            source_field='fld1',
                                            title_field='fld1')

        context = LinzRedistrictingContext(scenario_registry=scenario_registry)
        dlg = CreateElectorateDialog(registry=reg, context=context)
        self.assertIsNotNone(dlg)

        self.assertFalse(
            dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())

        dlg.name_line_edit.setText('new district')
        dlg.code_line_edit.setText('new code')
        self.assertEqual(dlg.name(), 'new district')
        self.assertEqual(dlg.code(), 'new code')

        # dupe name
        dlg.name_line_edit.setText('test4')
        self.assertFalse(
            dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertIn('already exists', dlg.feedback_label.text())
        dlg.name_line_edit.setText('test99')
        self.assertTrue(dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertFalse(dlg.feedback_label.text())
        dlg.name_line_edit.setText('')
        self.assertFalse(
            dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertIn('must be entered', dlg.feedback_label.text())
        dlg.name_line_edit.setText('test99')
        self.assertTrue(dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertFalse(dlg.feedback_label.text())

        # dupe code
        dlg.code_line_edit.setText('xtest1')
        self.assertFalse(
            dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertIn('already exists', dlg.feedback_label.text())
        dlg.code_line_edit.setText('test99')
        self.assertTrue(dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        self.assertFalse(dlg.feedback_label.text())
        dlg.code_line_edit.setText('')
        self.assertIn('must be entered', dlg.feedback_label.text())
        self.assertFalse(
            dlg.button_box.button(QDialogButtonBox.Ok).isEnabled())
        dlg.code_line_edit.setText('test99')
        self.assertFalse(dlg.feedback_label.text())
    def testValidationTask(self):  # pylint: disable=too-many-locals, too-many-statements
        """
        Test validation task
        """
        layer = make_scenario_layer()
        mb_electorate_layer = QgsVectorLayer(
            "NoGeometry?field=id:int&field=scenario_id:int&field=meshblock_number:int&field=gn_id:int&field=gs_id:int&field=m_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([1, 1, 11, 1, 0, 7])
        f2 = QgsFeature()
        f2.setAttributes([2, 1, 12, 2, 0, 0])
        f3 = QgsFeature()
        f3.setAttributes([3, 1, 13, 2, 0, 0])
        f4 = QgsFeature()
        f4.setAttributes([4, 1, 14, 3, 4, 0])
        f5 = QgsFeature()
        f5.setAttributes([5, 1, 15, 3, 5, 0])
        f6 = QgsFeature()
        f6.setAttributes([6, 1, 16, 2, 5, 0])
        mb_electorate_layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6])

        reg = ScenarioRegistry(source_layer=layer,
                               id_field='id',
                               name_field='name',
                               meshblock_electorate_layer=mb_electorate_layer)
        electorate_layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=electorate_id:int&field=code:string&field=type:string&field=estimated_pop:int&field=scenario_id:int&field=deprecated:int&field=invalid:int&field=invalid_reason:string&field=name:string&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=electorate_id_stats:string&field=expected_regions:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes([
            1, "test1", 'GN', 1, 0, 0, 1, 'old invalid', NULL, NULL, NULL,
            NULL, NULL, 1
        ])
        f2 = QgsFeature()
        f2.setAttributes([
            2, "test2", 'GN', 1, 0, 0, 1, 'old invalid 2', NULL, NULL, NULL,
            NULL, NULL, 2
        ])
        f3 = QgsFeature()
        f3.setAttributes([
            3, "test3", 'GN', 1, 0, 0, 1, 'old invalid 3', NULL, NULL, NULL,
            NULL, NULL, 2
        ])
        f4 = QgsFeature()
        f4.setAttributes([
            4, "test4", 'GS', 1, 0, 0, 1, 'old invalid 4', NULL, NULL, NULL,
            NULL, NULL, 2
        ])
        f5 = QgsFeature()
        f5.setAttributes([
            5, "test5", 'GS', 1, 0, 0, 1, 'old invalid 5', NULL, NULL, NULL,
            NULL, NULL, 1
        ])
        f6 = QgsFeature()
        f6.setAttributes([
            6, "test6", 'GS', 1, 0, 0, 1, 'old invalid 6', NULL, NULL, NULL,
            NULL, NULL, 1
        ])
        f7 = QgsFeature()
        f7.setAttributes([
            7, "test7", 'M', 1, 0, 1, 1, 'old invalid 7', NULL, NULL, NULL,
            NULL, NULL, 1
        ])
        f8 = QgsFeature()
        # deprecated
        f8.setAttributes([
            8, "test8", 'M', 1, 0, 1, 1, 'old invalid 8', NULL, NULL, NULL,
            NULL, NULL, 1
        ])
        electorate_layer.dataProvider().addFeatures(
            [f, f2, f3, f4, f5, f6, f7, f8])

        meshblock_layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=MeshblockNumber:string&field=offline_pop_m:int&field=offline_pop_gn:int&field=offline_pop_gs:int&field=staged_electorate:int&field=offshore:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["11", 5, 58900, 0, NULL, 0])
        f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(1, 2)))
        f2 = QgsFeature()
        f2.setAttributes(["12", 6, 57000, 0, NULL, 0])
        f2.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(2, 3)))
        f3 = QgsFeature()
        f3.setAttributes(["13", 7, 2000, 0, NULL, 0])
        f3.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(4, 5)))
        f4 = QgsFeature()
        f4.setAttributes(["14", 8, 0, 20, NULL, 0])
        f4.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(6, 7)))
        f5 = QgsFeature()
        f5.setAttributes(["15", 9, 0, 30, NULL, 0])
        f5.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(8, 9)))
        f6 = QgsFeature()
        f6.setAttributes(["16", 10, 0, 40, NULL, 1])
        f6.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10, 11)))
        meshblock_layer.dataProvider().addFeatures([f, f2, f3, f4, f5, f6])

        quota_layer = make_quota_layer()

        electorate_registry = LinzElectoralDistrictRegistry(
            source_layer=electorate_layer,
            source_field='electorate_id',
            title_field='code',
            electorate_type='GN',
            quota_layer=quota_layer)

        task = ValidationTask(task_name='',
                              electorate_registry=electorate_registry,
                              meshblock_layer=meshblock_layer,
                              meshblock_number_field_name='MeshblockNumber',
                              scenario_registry=reg,
                              scenario=1,
                              task='GN')
        self.assertEqual(
            [f.attributes()[:9] for f in electorate_layer.getFeatures()],
            [[1, 'test1', 'GN', 1, 0, 0, NULL, NULL, NULL],
             [2, 'test2', 'GN', 1, 0, 0, NULL, NULL, NULL],
             [3, 'test3', 'GN', 1, 0, 0, NULL, NULL, NULL],
             [4, 'test4', 'GS', 1, 0, 0, 1, 'old invalid 4', NULL],
             [5, 'test5', 'GS', 1, 0, 0, 1, 'old invalid 5', NULL],
             [6, 'test6', 'GS', 1, 0, 0, 1, 'old invalid 6', NULL],
             [7, 'test7', 'M', 1, 0, 1, 1, 'old invalid 7', NULL],
             [8, 'test8', 'M', 1, 0, 1, 1, 'old invalid 8', NULL]])

        self.assertTrue(task.run())
        self.assertEqual(len(task.results), 5)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_ID], 2)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_NAME],
                         'test2')
        self.assertEqual(task.results[0][ValidationTask.ERROR],
                         'Electorate is non-contiguous')
        self.assertEqual(
            task.results[0][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'MultiPoint ((2 3),(4 5),(10 11))')
        self.assertEqual(task.results[1][ValidationTask.ELECTORATE_ID], 2)
        self.assertEqual(task.results[1][ValidationTask.ELECTORATE_NAME],
                         'test2')
        self.assertEqual(task.results[1][ValidationTask.ERROR],
                         'Contiguous part 1')
        self.assertEqual(
            task.results[1][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'Point (2 3)')
        self.assertEqual(task.results[2][ValidationTask.ELECTORATE_ID], 2)
        self.assertEqual(task.results[2][ValidationTask.ELECTORATE_NAME],
                         'test2')
        self.assertEqual(task.results[2][ValidationTask.ERROR],
                         'Contiguous part 2')
        self.assertEqual(
            task.results[2][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'Point (4 5)')
        self.assertEqual(task.results[3][ValidationTask.ELECTORATE_ID], 2)
        self.assertEqual(task.results[3][ValidationTask.ELECTORATE_NAME],
                         'test2')
        self.assertEqual(task.results[3][ValidationTask.ERROR],
                         'Contiguous part 3')
        self.assertEqual(
            task.results[3][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'Point (10 11)')
        self.assertEqual(task.results[4][ValidationTask.ELECTORATE_ID], 3)
        self.assertEqual(task.results[4][ValidationTask.ELECTORATE_NAME],
                         'test3')
        self.assertEqual(task.results[4][ValidationTask.ERROR],
                         'Outside quota tolerance')
        self.assertEqual(
            [f.attributes()[:9] for f in electorate_layer.getFeatures()],
            [[1, 'test1', 'GN', 58900, 1, 0, 0, NULL, NULL],
             [
                 2, 'test2', 'GN', 1, 0, 0, 1, 'Electorate is non-contiguous',
                 NULL
             ], [
                 3, 'test3', 'GN', 1, 0, 0, 1, 'Outside quota tolerance', NULL
             ], [4, 'test4', 'GS', 1, 0, 0, 1, 'old invalid 4', NULL],
             [5, 'test5', 'GS', 1, 0, 0, 1, 'old invalid 5', NULL],
             [6, 'test6', 'GS', 1, 0, 0, 1, 'old invalid 6', NULL],
             [7, 'test7', 'M', 1, 0, 1, 1, 'old invalid 7', NULL],
             [8, 'test8', 'M', 1, 0, 1, 1, 'old invalid 8', NULL]])

        electorate_registry = LinzElectoralDistrictRegistry(
            source_layer=electorate_layer,
            source_field='electorate_id',
            title_field='code',
            electorate_type='GN',
            quota_layer=quota_layer)

        task = ValidationTask(task_name='',
                              electorate_registry=electorate_registry,
                              meshblock_layer=meshblock_layer,
                              meshblock_number_field_name='MeshblockNumber',
                              scenario_registry=reg,
                              scenario=1,
                              task='GS')

        self.assertTrue(task.run())
        self.assertEqual(len(task.results), 7)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_ID], 4)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_NAME],
                         'test4')
        self.assertEqual(task.results[0][ValidationTask.ERROR],
                         'Outside quota tolerance')
        self.assertEqual(task.results[1][ValidationTask.ELECTORATE_ID], 4)
        self.assertEqual(task.results[1][ValidationTask.ELECTORATE_NAME],
                         'test4')
        self.assertEqual(task.results[1][ValidationTask.ERROR],
                         'Electorate has less parts than expected')
        self.assertEqual(task.results[2][ValidationTask.ELECTORATE_ID], 5)
        self.assertEqual(task.results[2][ValidationTask.ELECTORATE_NAME],
                         'test5')
        self.assertEqual(task.results[2][ValidationTask.ERROR],
                         'Outside quota tolerance')
        self.assertEqual(task.results[3][ValidationTask.ELECTORATE_ID], 5)
        self.assertEqual(task.results[3][ValidationTask.ELECTORATE_NAME],
                         'test5')
        self.assertEqual(task.results[3][ValidationTask.ERROR],
                         'Electorate is non-contiguous')
        self.assertEqual(
            task.results[3][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'MultiPoint ((8 9),(10 11))')
        self.assertEqual(task.results[4][ValidationTask.ELECTORATE_ID], 5)
        self.assertEqual(task.results[4][ValidationTask.ELECTORATE_NAME],
                         'test5')
        self.assertEqual(task.results[4][ValidationTask.ERROR],
                         'Contiguous part 1')
        self.assertEqual(
            task.results[4][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'Point (8 9)')
        self.assertEqual(task.results[5][ValidationTask.ELECTORATE_ID], 5)
        self.assertEqual(task.results[5][ValidationTask.ELECTORATE_NAME],
                         'test5')
        self.assertEqual(task.results[5][ValidationTask.ERROR],
                         'Contiguous part 2')
        self.assertEqual(
            task.results[5][ValidationTask.ELECTORATE_GEOMETRY].asWkt(0),
            'Point (10 11)')
        self.assertEqual(task.results[6][ValidationTask.ELECTORATE_ID], 6)
        self.assertEqual(task.results[6][ValidationTask.ELECTORATE_NAME],
                         'test6')
        self.assertEqual(task.results[6][ValidationTask.ERROR],
                         'Outside quota tolerance')
        self.assertEqual([
            f.attributes()[:9] for f in electorate_layer.getFeatures()
        ], [[1, 'test1', 'GN', 58900, 1, 0, 0, NULL, NULL],
            [
                2, 'test2', 'GN', 1, 0, 0, 1, 'Electorate is non-contiguous',
                NULL
            ], [3, 'test3', 'GN', 1, 0, 0, 1, 'Outside quota tolerance', NULL],
            [
                4, 'test4', 'GS', 1, 0, 0, 1,
                'Electorate has less parts than expected', NULL
            ],
            [
                5, 'test5', 'GS', 1, 0, 0, 1, 'Electorate is non-contiguous',
                NULL
            ], [6, 'test6', 'GS', 1, 0, 0, 1, 'Outside quota tolerance', NULL],
            [7, 'test7', 'M', 1, 0, 1, 1, 'old invalid 7', NULL],
            [8, 'test8', 'M', 1, 0, 1, 1, 'old invalid 8', NULL]])

        electorate_registry = LinzElectoralDistrictRegistry(
            source_layer=electorate_layer,
            source_field='electorate_id',
            title_field='code',
            electorate_type='M',
            quota_layer=quota_layer)

        task = ValidationTask(task_name='',
                              electorate_registry=electorate_registry,
                              meshblock_layer=meshblock_layer,
                              meshblock_number_field_name='MeshblockNumber',
                              scenario_registry=reg,
                              scenario=1,
                              task='M')

        self.assertTrue(task.run())
        self.assertEqual(len(task.results), 1)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_ID], 7)
        self.assertEqual(task.results[0][ValidationTask.ELECTORATE_NAME],
                         'test7')
        self.assertEqual(task.results[0][ValidationTask.ERROR],
                         'Deprecated electorate has meshblocks assigned')
        self.assertEqual([
            f.attributes()[:9] for f in electorate_layer.getFeatures()
        ], [[1, 'test1', 'GN', 58900, 1, 0, 0, NULL, NULL],
            [
                2, 'test2', 'GN', 1, 0, 0, 1, 'Electorate is non-contiguous',
                NULL
            ], [3, 'test3', 'GN', 1, 0, 0, 1, 'Outside quota tolerance', NULL],
            [
                4, 'test4', 'GS', 1, 0, 0, 1,
                'Electorate has less parts than expected', NULL
            ],
            [
                5, 'test5', 'GS', 1, 0, 0, 1, 'Electorate is non-contiguous',
                NULL
            ], [6, 'test6', 'GS', 1, 0, 0, 1, 'Outside quota tolerance', NULL],
            [
                7, 'test7', 'M', 1, 0, 1, 1,
                'Deprecated electorate has meshblocks assigned', NULL
            ], [8, 'test8', 'M', 0, 1, 1, 0, NULL, NULL]])
    def testShowStats(self):  # pylint: disable=too-many-statements
        """Test show stats for district"""
        layer = QgsVectorLayer(
            "Point?crs=EPSG:4326&field=fld1:string&field=fld2:string&field=type:string&field=estimated_pop:int&field=deprecated:int&field=stats_nz_pop:int&field=stats_nz_var_20:int&field=stats_nz_var_23:int&field=scenario_id:int",
            "source", "memory")
        f = QgsFeature()
        f.setAttributes(["test4", "xtest1", 'GN', 1000])
        f2 = QgsFeature()
        f2.setAttributes(["test2", "xtest3", 'GS', 2000])
        f3 = QgsFeature()
        f3.setAttributes(["test3", "xtest3", 'M', 63000])
        layer.dataProvider().addFeatures([f, f2, f3])

        quota_layer = make_quota_layer()
        registry = LinzElectoralDistrictRegistry(
            source_layer=layer,
            quota_layer=quota_layer,
            electorate_type='',
            source_field='fld1',
            title_field='fld2')

        dock = RedistrictingDockWidget(IFACE)
        handler = LinzRedistrictGuiHandler(redistrict_dock=dock, district_registry=registry)

        handler.show_stats_for_district('')
        self.assertFalse(dock.frame.toPlainText())
        handler.show_stats_for_district('test4')
        self.assertIn('Statistics for xtest1', dock.frame.toPlainText())
        self.assertIn('General North Island', dock.frame.toPlainText())
        self.assertNotIn('updating', dock.frame.toPlainText())
        self.assertIn('59000', dock.frame.toPlainText())
        self.assertIn('1000*', dock.frame.toPlainText())
        self.assertIn('-98%', dock.frame.toPlainText())
        self.assertIn('color:#ff0000', dock.frame.toHtml())
        self.assertIn('estimated population available', dock.frame.toPlainText())

        handler.show_stats_for_district('test3')
        self.assertIn('Statistics for xtest3', dock.frame.toPlainText())
        self.assertIn('Māori', dock.frame.toPlainText())
        self.assertNotIn('updating', dock.frame.toPlainText())
        self.assertIn('61000', dock.frame.toPlainText())
        self.assertIn('63000*', dock.frame.toPlainText())
        self.assertIn('+3%', dock.frame.toPlainText())
        self.assertNotIn('color:#ff0000', dock.frame.toHtml())
        self.assertIn('estimated population available', dock.frame.toPlainText())

        registry.flag_stats_nz_updating('test4')
        handler.show_stats_for_district('test4')
        self.assertIn('Statistics for xtest1', dock.frame.toPlainText())
        self.assertIn('General North Island', dock.frame.toPlainText())
        self.assertIn('updating', dock.frame.toPlainText())
        self.assertNotIn('1000*', dock.frame.toPlainText())
        self.assertNotIn('-98%', dock.frame.toPlainText())
        self.assertNotIn('color:#ff0000', dock.frame.toHtml())
        self.assertNotIn('estimated population available', dock.frame.toPlainText())

        registry.update_stats_nz_values('test3', {'currentPopulation': 1111,
                                                  'varianceYear1': 1.5,
                                                  'varianceYear2': -1.1})
        handler.show_stats_for_district('test3')
        self.assertIn('Statistics for xtest3', dock.frame.toPlainText())
        self.assertIn('Māori', dock.frame.toPlainText())
        self.assertNotIn('updating', dock.frame.toPlainText())
        self.assertIn('61000', dock.frame.toPlainText())
        self.assertIn('1111', dock.frame.toPlainText())
        self.assertIn('1.5', dock.frame.toPlainText())
        self.assertIn('-1.1', dock.frame.toPlainText())
        self.assertIn('-98%', dock.frame.toPlainText())
        self.assertIn('color:#ff0000', dock.frame.toHtml())
        self.assertNotIn('estimated population available', dock.frame.toPlainText())
    def selection_changed(self):
        """
        Triggered when the selection in the meshblock layer changes
        """
        if not self.task or not self.district_registry:
            return

        request = QgsFeatureRequest().setFilterFids(
            self.meshblock_layer.selectedFeatureIds()).setFlags(
                QgsFeatureRequest.NoGeometry)

        counts = defaultdict(int)
        for f in self.meshblock_layer.getFeatures(request):
            electorate = f['staged_electorate']
            if self.task == 'GN':
                pop = f['offline_pop_gn']
            elif self.task == 'GS':
                pop = f['offline_pop_gs']
            else:
                pop = f['offline_pop_m']
            counts[electorate] += pop

        html = """<h3>Target Electorate: <a href="#">{}</a></h3><p>""".format(
            self.district_registry.get_district_title(self.target_electorate))

        request = QgsFeatureRequest()
        request.setFilterExpression(
            QgsExpression.createFieldEqualityExpression('type', self.task))
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes(
            ['electorate_id', 'estimated_pop', 'stats_nz_pop'],
            self.district_registry.source_layer.fields())
        original_populations = {}
        for f in self.district_registry.source_layer.getFeatures(request):
            estimated_pop = f['stats_nz_pop']
            if estimated_pop is None or estimated_pop == NULL:
                # otherwise just use existing estimated pop as starting point
                estimated_pop = f['estimated_pop']
            original_populations[f['electorate_id']] = estimated_pop

        overall = 0
        for electorate, pop in counts.items():
            if self.target_electorate:
                if electorate != self.target_electorate:
                    overall += pop

                    # use stats nz pop as initial estimate, if available
                    estimated_pop = original_populations[electorate]

                    estimated_pop -= pop
                    variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
                        self.quota, estimated_pop)

                    html += """\n{}: <span style="font-weight:bold">-{}</span> (after: {}, {}{}%)<br>""".format(
                        self.district_registry.get_district_title(electorate),
                        pop, int(estimated_pop), '+' if variance > 0 else '',
                        variance)
            else:
                html += """\n{}: <span style="font-weight:bold">{}</span><br>""".format(
                    self.district_registry.get_district_title(electorate), pop)
        if self.target_electorate:
            estimated_pop = original_populations[self.target_electorate]

            estimated_pop += overall
            variance = LinzElectoralDistrictRegistry.get_variation_from_quota_percent(
                self.quota, estimated_pop)

            html += """\n{}: <span style="font-weight:bold">+{}</span> (after: {}, {}{}%)<br>""".format(
                self.district_registry.get_district_title(
                    self.target_electorate), overall, int(estimated_pop),
                '+' if variance > 0 else '', variance)

        html += '</p>'

        self.frame.setHtml(html)