From b0e2850693bd2d2e3e8628dd71e026cdc733731b Mon Sep 17 00:00:00 2001 From: Joaquim d'Souza Date: Thu, 26 Feb 2026 16:35:28 +0100 Subject: [PATCH 1/6] fix: move null is zero to data source, fix bugs --- .../1772117945260_data_source_null_is_zero.ts | 13 ++++++ .../[id]/components/ConfigurationForm.tsx | 13 ++++++ src/app/map/[id]/components/Legend.tsx | 40 ++++++++++++++----- .../VisualisationPanel/VisualisationPanel.tsx | 23 +---------- src/app/map/[id]/data.ts | 3 -- src/app/map/[id]/utils/mapView.ts | 1 - src/server/models/DataSource.ts | 1 + src/server/models/MapView.ts | 1 - src/server/stats/index.ts | 18 ++++----- src/server/trpc/routers/area.ts | 1 - src/server/trpc/routers/dataSource.ts | 1 + 11 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 migrations/1772117945260_data_source_null_is_zero.ts diff --git a/migrations/1772117945260_data_source_null_is_zero.ts b/migrations/1772117945260_data_source_null_is_zero.ts new file mode 100644 index 00000000..22c69bb9 --- /dev/null +++ b/migrations/1772117945260_data_source_null_is_zero.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { Kysely } from "kysely"; + +export async function up(db: Kysely): Promise { + await db.schema + .alterTable("dataSource") + .addColumn("nullIsZero", "boolean", (col) => col.defaultTo(false)) + .execute(); +} + +export async function down(db: Kysely): Promise { + await db.schema.alterTable("dataSource").dropColumn("nullIsZero").execute(); +} diff --git a/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx b/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx index e4d241f3..baa3c883 100644 --- a/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx +++ b/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx @@ -55,6 +55,7 @@ export default function ConfigurationForm({ const [isPublic, setIsPublic] = useState(dataSource.public); const [naIsNull, setNaIsNull] = useState(dataSource.naIsNull || false); + const [nullIsZero, setNullIsZero] = useState(dataSource.nullIsZero || false); // Form state const [loading, setLoading] = useState(false); @@ -111,6 +112,7 @@ export default function ConfigurationForm({ dateFormat, public: isPublic, naIsNull, + nullIsZero, }); }; @@ -193,6 +195,17 @@ export default function ConfigurationForm({ setNaIsNull(v)} /> + + setNullIsZero(v)} + /> + + v && v !== "null" && v !== "undefined"), ); + const valueLabels = + dataSource?.columnMetadata.find( + (c) => c.name === viewConfig.areaDataColumn, + )?.valueLabels || {}; + return (
{Object.keys(colorScheme.colorMap) @@ -155,15 +160,25 @@ export default function Legend() { } return a < b ? -1 : 1; }) - .map((key) => ( -
-
- {key} -
- ))} + .map((key) => { + let label = valueLabels[key]; + if ( + !label && + areaStats?.primary?.columnType === ColumnType.Number && + Number(key) === 0 + ) { + label = valueLabels[""]; + } + return ( +
+
+ {label || key} +
+ ); + })}
0; if (hasValueLabels) { - numTicks = Object.keys(valueLabels).length; + values = Object.keys(valueLabels) + .map(Number) + .filter((v) => v >= colorScheme.minValue && v <= colorScheme.maxValue) + .toSorted(); + numTicks = values.length; denom = Math.max(numTicks - 1, 1); - values = Object.keys(valueLabels).map(Number).toSorted(); } return ( diff --git a/src/app/map/[id]/components/controls/VisualisationPanel/VisualisationPanel.tsx b/src/app/map/[id]/components/controls/VisualisationPanel/VisualisationPanel.tsx index 1810c940..f3ac3041 100644 --- a/src/app/map/[id]/components/controls/VisualisationPanel/VisualisationPanel.tsx +++ b/src/app/map/[id]/components/controls/VisualisationPanel/VisualisationPanel.tsx @@ -168,8 +168,6 @@ export default function VisualisationPanel({ dataSource?.geocodingConfig, viewConfig.areaSetGroupCode, ); - const showEmptyZeroSwitch = !isCount && columnOneIsNumber; - const showStyle = !viewConfig.areaDataSecondaryColumn; const canSelectColorScale = isCount || columnOneIsNumber; const canSelectColorScheme = canSelectColorScale && !isCategorical; @@ -443,25 +441,6 @@ export default function VisualisationPanel({ )} - - {showEmptyZeroSwitch && ( -
- - - - updateViewConfig({ areaDataNullIsZero: v }) - } - /> -
- )}
{!viewConfig.areaDataSourceId && (
@@ -532,7 +511,7 @@ export default function VisualisationPanel({ } > diff --git a/src/app/map/[id]/data.ts b/src/app/map/[id]/data.ts index 8ead31a8..53396827 100644 --- a/src/app/map/[id]/data.ts +++ b/src/app/map/[id]/data.ts @@ -50,7 +50,6 @@ export const useAreaStats = () => { areaDataColumn: column, areaDataSecondaryColumn: secondaryColumn, areaDataSourceId: dataSourceId, - areaDataNullIsZero: nullIsZero, areaSetGroupCode, } = viewConfig; @@ -118,7 +117,6 @@ export const useAreaStats = () => { dataSourceId, column: columnOrCount, secondaryColumn: secondaryColumn, - nullIsZero, includeColumns, boundingBox: requiresBoundingBox ? boundingBox : null, }, @@ -134,7 +132,6 @@ export const useAreaStats = () => { dataSourceId, column, secondaryColumn, - nullIsZero, includeColumns, ]); diff --git a/src/app/map/[id]/utils/mapView.ts b/src/app/map/[id]/utils/mapView.ts index ea05dd92..82ab9a5f 100644 --- a/src/app/map/[id]/utils/mapView.ts +++ b/src/app/map/[id]/utils/mapView.ts @@ -9,7 +9,6 @@ export const createNewViewConfig = (): MapViewConfig => { return { areaDataSourceId: "", areaDataColumn: "", - areaDataNullIsZero: true, areaSetGroupCode: undefined, mapStyleName: MapStyleName.Light, showLabels: true, diff --git a/src/server/models/DataSource.ts b/src/server/models/DataSource.ts index 656548dc..dbe84588 100644 --- a/src/server/models/DataSource.ts +++ b/src/server/models/DataSource.ts @@ -255,6 +255,7 @@ export const dataSourceSchema = z.object({ createdAt: z.date(), dateFormat: z.string(), naIsNull: z.boolean().optional(), + nullIsZero: z.boolean().optional(), }); export type DataSource = z.infer; diff --git a/src/server/models/MapView.ts b/src/server/models/MapView.ts index 10d8a08b..c3a36e61 100644 --- a/src/server/models/MapView.ts +++ b/src/server/models/MapView.ts @@ -130,7 +130,6 @@ export const mapViewConfigSchema = z.object({ areaDataSourceId: z.string(), areaDataColumn: z.string(), areaDataSecondaryColumn: z.string().optional(), - areaDataNullIsZero: z.boolean().optional(), areaSetGroupCode: areaSetGroupCode.nullish(), secondaryAreaSetCode: areaSetCode.nullish(), choroplethOpacityPct: z.number().optional(), diff --git a/src/server/stats/index.ts b/src/server/stats/index.ts index d3301a8a..b7c7563c 100644 --- a/src/server/stats/index.ts +++ b/src/server/stats/index.ts @@ -45,7 +45,6 @@ export const getAreaStats = async ({ calculationType, column, secondaryColumn, - nullIsZero, includeColumns, boundingBox = null, }: { @@ -54,7 +53,6 @@ export const getAreaStats = async ({ calculationType: CalculationType; column: string; secondaryColumn?: string; - nullIsZero?: boolean; includeColumns?: string[] | null; boundingBox?: BoundingBox | null; }): Promise => { @@ -110,7 +108,6 @@ export const getAreaStats = async ({ areaSetCode, calculationType, column, - nullIsZero, boundingBox, }); const valueRange = getValueRange(primaryStats.stats); @@ -128,7 +125,6 @@ export const getAreaStats = async ({ areaSetCode, calculationType, column: secondaryColumn, - nullIsZero, boundingBox, }); const secondaryValueRange = getValueRange(secondaryStats.stats); @@ -254,14 +250,12 @@ const getColumnValueByArea = async ({ areaSetCode, calculationType, column, - nullIsZero, boundingBox, }: { dataSource: DataSource; areaSetCode: AreaSetCode; calculationType: CalculationType; column: string; - nullIsZero: boolean | undefined; boundingBox: BoundingBox | null; }) => { const columnDef = dataSource.columnDefs.find((c) => c.name === column); @@ -269,17 +263,23 @@ const getColumnValueByArea = async ({ throw new Error(`Data source column not found: ${column}`); } - // Coalesce empty values to 0 if set - const numberSelect = nullIsZero + // Coalesce numeric empty values to 0 if set + const numberSelect = dataSource.nullIsZero ? sql`(COALESCE(NULLIF(json->>${column}, ''), '0'))::float` : sql`(NULLIF(json->>${column}, ''))::float`; + // Coalesce mode empty values to 0 if the column is numeric and nullIsZero is set + const modeSelect = + columnDef.type === ColumnType.Number && dataSource.nullIsZero + ? sql`MODE () WITHIN GROUP (ORDER BY COALESCE(NULLIF(json->>${column}, ''), '0'))` + : sql`MODE () WITHIN GROUP (ORDER BY NULLIF(json->>${column}, ''))`; + // Select is always MODE for ColumnType !== Number const isMode = calculationType === CalculationType.Mode || columnDef.type !== ColumnType.Number; const valueSelect = isMode - ? sql`MODE () WITHIN GROUP (ORDER BY json->>${column})`.as("value") + ? modeSelect.as("value") : db.fn(calculationType, [numberSelect]).as("value"); const query = db diff --git a/src/server/trpc/routers/area.ts b/src/server/trpc/routers/area.ts index 855de5a1..ace68a32 100644 --- a/src/server/trpc/routers/area.ts +++ b/src/server/trpc/routers/area.ts @@ -38,7 +38,6 @@ export const areaRouter = router({ calculationType: z.nativeEnum(CalculationType), column: z.string(), secondaryColumn: z.string().optional(), - nullIsZero: z.boolean().optional(), includeColumns: z.array(z.string()).optional().nullable(), boundingBox: boundingBoxSchema.nullish(), }), diff --git a/src/server/trpc/routers/dataSource.ts b/src/server/trpc/routers/dataSource.ts index 99fb59d3..211717ef 100644 --- a/src/server/trpc/routers/dataSource.ts +++ b/src/server/trpc/routers/dataSource.ts @@ -239,6 +239,7 @@ export const dataSourceRouter = router({ dateFormat: input.dateFormat, public: input.public, naIsNull: input.naIsNull, + nullIsZero: input.nullIsZero, } as DataSourceUpdate; logger.info( From 8115d808374e8950990e526650669ced3a723c8e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:46:38 +0000 Subject: [PATCH 2/6] Initial plan From c4d7e3b8dd82ada2e7fb0fb44ef56ba0d0e34168 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 15:51:36 +0000 Subject: [PATCH 3/6] fix: invalidate dataSource.byId, listReadable, and area.stats after config save Co-authored-by: joaquimds <12935136+joaquimds@users.noreply.github.com> --- .../[id]/components/ConfigurationForm.tsx | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx b/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx index baa3c883..c7fd681e 100644 --- a/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx +++ b/src/app/(private)/data-sources/[id]/components/ConfigurationForm.tsx @@ -70,12 +70,26 @@ export default function ConfigurationForm({ // Notify parent of autoImport status change onAutoImportChange?.(autoImport); - // Invalidate webhook status to refetch and show/hide errors - await queryClient.invalidateQueries({ - queryKey: trpc.dataSource.checkWebhookStatus.queryKey({ - dataSourceId: dataSource.id, + // Invalidate cached data source queries and derived stats so + // map rendering and other parts of the app reflect the new config + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: trpc.dataSource.checkWebhookStatus.queryKey({ + dataSourceId: dataSource.id, + }), }), - }); + queryClient.invalidateQueries({ + queryKey: trpc.dataSource.byId.queryKey({ + dataSourceId: dataSource.id, + }), + }), + queryClient.invalidateQueries({ + queryKey: trpc.dataSource.listReadable.queryKey(), + }), + queryClient.invalidateQueries({ + queryKey: trpc.area.stats.queryKey(), + }), + ]); if (redirectToParent) { router.push(`/data-sources/${dataSource.id}`); From ec7ce72703c1373b0fbbf3fb8c0af6ebf91bcf2f Mon Sep 17 00:00:00 2001 From: Joaquim d'Souza Date: Thu, 26 Feb 2026 19:07:31 +0100 Subject: [PATCH 4/6] feat: add area stats test --- src/server/stats/index.ts | 3 + tests/feature/importDataSource.test.ts | 18 +- tests/feature/stats.test.ts | 666 +++++++++++++++++++++++++ tests/resources/sampleAreas.psql | 2 + tests/resources/stats.csv | 12 +- tests/setup.ts | 3 + 6 files changed, 694 insertions(+), 10 deletions(-) create mode 100644 tests/feature/stats.test.ts diff --git a/src/server/stats/index.ts b/src/server/stats/index.ts index b7c7563c..51767cbf 100644 --- a/src/server/stats/index.ts +++ b/src/server/stats/index.ts @@ -221,6 +221,7 @@ export const getMaxColumnByArea = async ( ${caseWhen.end()} AS max_column FROM data_record WHERE data_source_id = ${dataSourceId} + AND geocode_result->'areas'->>${areaSetCode} IS NOT NULL AND ${getBoundingBoxSQL(boundingBox)} AND ${hasNonNullValueCondition} AND ${caseWhen.end()} IS NOT NULL @@ -289,6 +290,7 @@ const getColumnValueByArea = async ({ valueSelect, ]) .where("dataRecord.dataSourceId", "=", dataSource.id) + .where(sql`geocode_result->'areas'->>${areaSetCode} IS NOT NULL`) .where(getBoundingBoxSQL(boundingBox)) .groupBy("areaCode"); @@ -310,6 +312,7 @@ export const getRecordCountByArea = async ( ({ fn }) => fn.countAll().as("value"), ]) .where("dataRecord.dataSourceId", "=", dataSourceId) + .where(sql`geocode_result->'areas'->>${areaSetCode} IS NOT NULL`) .where(getBoundingBoxSQL(boundingBox)) .groupBy("areaCode"); diff --git a/tests/feature/importDataSource.test.ts b/tests/feature/importDataSource.test.ts index 9f0c9475..a81fe107 100644 --- a/tests/feature/importDataSource.test.ts +++ b/tests/feature/importDataSource.test.ts @@ -222,8 +222,12 @@ describe("importDataSource tests", () => { expect(columnDefs).toEqual([ { name: "Code", type: ColumnType.String }, + { name: "Con %", type: ColumnType.Number }, { name: "Electorate", type: ColumnType.Number }, + { name: "Lab %", type: ColumnType.Number }, { name: "Name", type: ColumnType.String }, + { name: "Segment", type: ColumnType.Number }, + { name: "Segment name", type: ColumnType.String }, ]); const areaStats = await getAreaStats({ @@ -247,17 +251,21 @@ describe("importDataSource tests", () => { }); expect(stats).toEqual([ - { - areaCode: "E14001070", - value: 71002, - }, { areaCode: "E14001071", value: 79169, }, { areaCode: "E14001088", - value: 72198, + value: 72199, + }, + { + areaCode: "E14001092", + value: 71002, + }, + { + areaCode: "E14001225", + value: 72481, }, ]); }); diff --git a/tests/feature/stats.test.ts b/tests/feature/stats.test.ts new file mode 100644 index 00000000..2245e3d7 --- /dev/null +++ b/tests/feature/stats.test.ts @@ -0,0 +1,666 @@ +import { afterAll, beforeAll, describe, expect, test } from "vitest"; +import { MAX_COLUMN_KEY } from "@/constants"; +import importDataSource from "@/server/jobs/importDataSource"; +import { AreaSetCode } from "@/server/models/AreaSet"; +import { + ColumnType, + DataSourceRecordType, + DataSourceType, + GeocodingType, +} from "@/server/models/DataSource"; +import { CalculationType } from "@/server/models/MapView"; +import { + createDataSource, + deleteDataSource, + updateDataSource, +} from "@/server/repositories/DataSource"; +import { upsertOrganisation } from "@/server/repositories/Organisation"; +import { getAreaStats } from "@/server/stats"; + +describe("getAreaStats tests", () => { + let dataSourceId: string; + + beforeAll(async () => { + const org = await upsertOrganisation({ + name: "Test Stats Org", + }); + + const dataSource = await createDataSource({ + name: "Test Import Stats CSV Source", + autoEnrich: false, + autoImport: false, + recordType: DataSourceRecordType.Data, + config: { + type: DataSourceType.CSV, + url: "file://tests/resources/stats.csv", + }, + columnDefs: [], + columnMetadata: [], + columnRoles: { nameColumns: [] }, + enrichments: [], + geocodingConfig: { + type: GeocodingType.Code, + column: "Code", + areaSetCode: AreaSetCode.WMC24, + }, + organisationId: org.id, + public: false, + }); + + dataSourceId = dataSource.id; + await importDataSource({ dataSourceId: dataSource.id }); + }); + + afterAll(async () => { + if (dataSourceId) { + await deleteDataSource(dataSourceId); + } + }); + + const sortStats = (stats: { areaCode: string; value?: unknown }[]) => { + stats.sort((a, b) => (a.areaCode < b.areaCode ? -1 : 1)); + }; + + describe("nullIsZero = false (default)", () => { + test("Sum", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Sum, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 79169, + minValue: 71002, + stats: [ + { areaCode: "E14001071", value: 79169 }, + { areaCode: "E14001088", value: 72199 }, + { areaCode: "E14001092", value: 71002 }, + { areaCode: "E14001225", value: 72481 }, + ], + }, + }); + }); + + test("Average", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Avg, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Avg, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 79169, + minValue: 71002, + stats: [ + { areaCode: "E14001071", value: 79169 }, + { areaCode: "E14001088", value: 72199 }, + { areaCode: "E14001092", value: 71002 }, + { areaCode: "E14001225", value: 72481 }, + ], + }, + }); + }); + + test("Mode", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment", + columnType: ColumnType.Number, + maxValue: 3, + minValue: 1, + stats: [ + { areaCode: "E14001071", value: 1 }, + { areaCode: "E14001088", value: 2 }, + { areaCode: "E14001202", value: 1 }, + { areaCode: "E14001225", value: 3 }, + ], + }, + }); + }); + + test("Mode (Segment name)", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment name", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment name", + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: "Normal" }, + { areaCode: "E14001088", value: "Weird" }, + { areaCode: "E14001202", value: "Normal" }, + { areaCode: "E14001225", value: "Unusual" }, + ], + }, + }); + }); + + test("Highest value column", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Sum, + column: MAX_COLUMN_KEY, + includeColumns: ["Lab %", "Con %"], + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: MAX_COLUMN_KEY, + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: "Con %" }, + { areaCode: "E14001088", value: "Lab %" }, + { areaCode: "E14001092", value: "Lab %" }, + { areaCode: "E14001202", value: "Lab %" }, + { areaCode: "E14001225", value: "Con %" }, + ], + }, + }); + }); + }); + + describe("nullIsZero = true", () => { + beforeAll(async () => { + await updateDataSource(dataSourceId, { nullIsZero: true }); + }); + + afterAll(async () => { + await updateDataSource(dataSourceId, { nullIsZero: false }); + }); + + test("Sum", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Sum, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 79169, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: 79169 }, + { areaCode: "E14001088", value: 72199 }, + { areaCode: "E14001092", value: 71002 }, + { areaCode: "E14001202", value: 0 }, + { areaCode: "E14001225", value: 72481 }, + ], + }, + }); + }); + + test("Average", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Avg, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Avg, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 79169, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: 79169 }, + { areaCode: "E14001088", value: 72199 }, + { areaCode: "E14001092", value: 71002 }, + { areaCode: "E14001202", value: 0 }, + { areaCode: "E14001225", value: 72481 }, + ], + }, + }); + }); + + test("Mode", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment", + columnType: ColumnType.Number, + maxValue: 3, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: 1 }, + { areaCode: "E14001088", value: 2 }, + { areaCode: "E14001092", value: 0 }, + { areaCode: "E14001202", value: 1 }, + { areaCode: "E14001225", value: 3 }, + ], + }, + }); + }); + + test("Mode (Segment name)", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.WMC24, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment name", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.WMC24, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment name", + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { areaCode: "E14001071", value: "Normal" }, + { areaCode: "E14001088", value: "Weird" }, + // Even when nullIsZero is true, empty string values are still filtered out + // because the meaning is unknown + // { areaCode: "E14001092", value: "" }, + { areaCode: "E14001202", value: "Normal" }, + { areaCode: "E14001225", value: "Unusual" }, + ], + }, + }); + }); + }); + + describe("UKR18 - nullIsZero = false (default)", () => { + test("Sum", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Sum, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 151368, + minValue: 72481, + stats: [ + { + areaCode: "E15000007", + value: 72481, + }, + { + areaCode: "E15000008", + value: 151368, + }, + ], + }, + }); + }); + + test("Average", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Avg, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Avg, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 75684, + minValue: 72481, + stats: [ + { + areaCode: "E15000007", + value: 72481, + }, + { + areaCode: "E15000008", + value: 75684, + }, + ], + }, + }); + }); + + test("Mode", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment", + columnType: ColumnType.Number, + maxValue: 3, + minValue: 1, + stats: [ + { + areaCode: "E15000007", + value: 3, + }, + { + areaCode: "E15000008", + value: 1, + }, + ], + }, + }); + }); + + test("Mode (Segment name)", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment name", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment name", + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { + areaCode: "E15000007", + value: "Unusual", + }, + { + areaCode: "E15000008", + value: "Normal", + }, + ], + }, + }); + }); + + test("Highest value column", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Sum, + column: MAX_COLUMN_KEY, + includeColumns: ["Lab %", "Con %"], + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: MAX_COLUMN_KEY, + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { areaCode: "E15000007", value: "Con %" }, + { areaCode: "E15000008", value: "Lab %" }, + ], + }, + }); + }); + }); + + describe("UKR18 - nullIsZero = true", () => { + beforeAll(async () => { + await updateDataSource(dataSourceId, { nullIsZero: true }); + }); + + afterAll(async () => { + await updateDataSource(dataSourceId, { nullIsZero: false }); + }); + + test("Sum", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Sum, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Sum, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 151368, + minValue: 72481, + stats: [ + { + areaCode: "E15000007", + value: 72481, + }, + { + areaCode: "E15000008", + value: 151368, + }, + ], + }, + }); + }); + + test("Average", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Avg, + column: "Electorate", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Avg, + dataSourceId, + primary: { + column: "Electorate", + columnType: ColumnType.Number, + maxValue: 72481, + minValue: 50456, + stats: [ + { + areaCode: "E15000007", + value: 72481, + }, + { + areaCode: "E15000008", + value: 50456, + }, + ], + }, + }); + }); + + test("Mode", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment", + columnType: ColumnType.Number, + maxValue: 3, + minValue: 1, + stats: [ + { + areaCode: "E15000007", + value: 3, + }, + { + areaCode: "E15000008", + value: 1, + }, + ], + }, + }); + }); + + test("Mode (Segment name)", async () => { + const areaStats = await getAreaStats({ + areaSetCode: AreaSetCode.UKR18, + dataSourceId, + calculationType: CalculationType.Mode, + column: "Segment name", + }); + + const stats = areaStats.primary?.stats ?? []; + sortStats(stats); + + expect(areaStats).toEqual({ + areaSetCode: AreaSetCode.UKR18, + calculationType: CalculationType.Mode, + dataSourceId, + primary: { + column: "Segment name", + columnType: ColumnType.String, + maxValue: 0, + minValue: 0, + stats: [ + { + areaCode: "E15000007", + value: "Unusual", + }, + { + areaCode: "E15000008", + value: "Normal", + }, + ], + }, + }); + }); + }); +}); diff --git a/tests/resources/sampleAreas.psql b/tests/resources/sampleAreas.psql index 2e1494a7..b8914cff 100644 --- a/tests/resources/sampleAreas.psql +++ b/tests/resources/sampleAreas.psql @@ -33,8 +33,10 @@ INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001092' INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001095', 'Birmingham Hodge Hill and Solihull North', '0103000020E610000001000000970000009C1666BE690EFCBFE639FDB8A8414A4055DDFB31A6F7FBBF2C97A2195A414A40C15C5AE61AE7FBBF04F27E51F4404A40DC256F953DBBFBBF845A56BCFE3E4A40BD37E19170C5FBBF469F2759F93E4A40CFAD85C9EBC4FBBF2F7D0845F13E4A40F0902E823CC9FBBFB6D7B22C073F4A40FE6E6B0771CFFBBFAC75EB5DF13E4A406872B62B52D3FBBF5664FED0F03E4A407EF7E2C6A7DFFBBF60591FC9353F4A40404740A4EDE3FBBF80208E392B3F4A405746D3CC5AE6FBBF6A36ADF2523F4A408DCD673063F6FBBF40FEA18D453F4A40E20A534B80FBFBBFB47532C3373F4A40F799396D81FCFBBF34638DE02A3F4A40A034EA541205FCBF725C69232B3F4A4082B003F80C06FCBFF6EE9D964B3F4A40D3FB4288C61FFCBF28A3FBE0533F4A400A969A44B624FCBFDCF7301A633F4A407AF125358C28FCBFC101FAF5523F4A40DC8F2A8A562AFCBF19EDE9D1313F4A4036A5A4A0652CFCBF4EA9A4682F3F4A4077BC1A9B162DFCBFB6800E1BEC3E4A40312FC2DA7D2FFCBF633EE9B2C43E4A40FE5F70669F32FCBF722527DABB3E4A40C75EED457B32FCBF6B2D9ABD893E4A4010F1D284842DFCBF41C1D1CA7F3E4A40DDF0AE45EE37FCBF3425B5A2593E4A4087971F9AA135FCBFA22CCBCE493E4A406AF632806A31FCBFB25F2DF9473E4A40437834FC032FFCBFEFC95D6B503E4A40572185357C2CFCBF2E94FF17433E4A40A26A786C5A2FFCBF5F6A64C81B3E4A4007A93194712DFCBF67770ED5173E4A407D5D9B6BA52AFCBF54F4D1ABF23D4A4037E08738CE26FCBFC786C440F03D4A40035646700D25FCBFC0BE9647DB3D4A4030D82D9A2A28FCBF988C70C0D03D4A406BFC24CF5624FCBF051A17BC7D3D4A404970E5595E28FCBF9ECC46017D3D4A407FB22AE83124FCBF76DA26843F3D4A4076C2279B3923FCBF1BD59566003D4A406226F1658125FCBF5B3B5E86FF3C4A40D7B24CF78725FCBF780B0734DA3C4A40FB5A40869D17FCBF6519BDEF653C4A407E215FC2681BFCBF80C4D8645B3C4A40D04CBE6C3026FCBF9F55E1AB063C4A40F11F39B24548FCBFDC3005038A3C4A40F80DA657854CFCBF2FC187A0843C4A40F382BDB6804CFCBFCFB745D5723C4A40718D64E3A448FCBF23ACF05B713C4A401FBB980C1F48FCBF6779C1CA4A3C4A4001E8E0588C4DFCBF98D607584A3C4A4001EA247BC24CFCBF876647EF1A3C4A4052EAFA69F248FCBF36557F0A153C4A40A4E1137E274DFCBFB98562A00D3C4A4062A477B5714EFCBFC51ACED2FB3B4A4055D2C12A6C61FCBF84CDE1C4B53B4A40DC3C6FFD7763FCBF26CF6A7CA83B4A403231911E0C61FCBF26FAD1309B3B4A40CFFAA5AB8F6CFCBFCE84A3AD6E3B4A4085A5C986A871FCBF58DB785A743B4A403ACE7AF38882FCBFD10EF214413B4A40972EE4C74485FCBFB9B169B0593B4A40EB4FBC0CFF8AFCBF6AA5545C593B4A40A87AA0BFBC87FCBFBC05B9345F3B4A405BC409AD758BFCBFC8825C898C3B4A4040B3EE78C389FCBF6772DEC78F3B4A4051514D92DA8BFCBF67F530FB9C3B4A402D68E298598BFCBF3E329406AC3B4A40B73B68BE0E90FCBFADBFD7C5AD3B4A407479D25AFE96FCBF39F6D823E63B4A4050C0A6F2949BFCBFFCA0196AFA3B4A404982261DE9A2FCBF10A3E490E13B4A40E27A10D5A4ACFCBF7A2229B1113C4A40A0C425733BADFCBFB50C8AF6483C4A4021E507FEE8B0FCBF03356D90683C4A40065DE6E1D5AEFCBF04A1B767733C4A40536631EF06B4FCBF118DBCD9923C4A40B480B6CF58ABFCBF7F489EA8D43C4A40C133E1EC4CA9FCBF5D9554BCCD3C4A40F876AAA3CDA7FCBF2520256DE43C4A4034AD04DFB2AEFCBF214E1472EA3C4A40A41BFC6A95AAFCBF7ADD30AD0C3D4A404A3E3669D5CFFCBF51E4F8953A3D4A407B35380CAACEFCBF9A7D9EE8433D4A405E00D4DB49D0FCBF394A29694B3D4A405DA53B7098CAFCBF34B69C12603D4A40F804891E28CCFCBF5ACE6271813D4A408887C9F797C2FCBF6D7091B2BB3D4A401F9D9A4BA1BEFCBF63E39E64B23D4A4076B2F0AF18BDFCBFD5419F7CD43D4A40ADFF9B5671DCFCBF07726163FF3D4A404E8C174CE2F4FCBFD56E9FEA143E4A40A2C23C2C2D13FDBFE2B96207223E4A40CF1627A1A414FDBF18D0393B183E4A40649299B68817FDBF5E632208B23D4A40A71D610DC722FDBF12790D1F403D4A407EA0A2609422FDBF7188087F323D4A404F21455F4F27FDBF8557BBF5183D4A40BFE9EE92D827FDBF125E30CC033D4A4000C16BDD0624FDBF9D8C8E7AC03C4A4074886AF6BE24FDBF532B6979A53C4A40D2BCD33E6328FDBF17220E66883C4A40EC715A573634FDBFC6EB42665E3C4A40441DC488CB38FDBF61419722333C4A402693E0D8853DFDBF4CBCFAD11E3C4A409E5A4D062742FDBF59A17BDA2E3C4A40C8757F36843FFDBFE17B80603D3C4A408ECD40F95F42FDBFE2576431493C4A40C4156D3C0F45FDBFAE6F11CA393C4A40AD826E535A46FDBFE0808D933F3C4A40F1E5D03A1844FDBF10E1E75F523C4A40BD2368A7754CFDBF4A6314BB753C4A40DB6899D81E50FDBFAD19D4EF803C4A40BAB192950D52FDBF32C8DBE66D3C4A40449B2125675EFDBF5EEFF7A2863C4A4054085A48A96AFDBFC9EA35B8B03C4A4070961A9B2968FDBF4E4562DEC23C4A40B61C4BDB5172FDBFEF56C3A6E63C4A40BDEFFFB3A477FDBF55DA4311E93C4A4050BA556A0770FDBFE220B0E8F13C4A40CA54D6530476FDBF34F43F47433D4A405A3D74E8888EFDBFA92F399F2F3D4A40761C69CFDC8EFDBF50BA00E8393D4A40823C1226EE8BFDBFAC77233C3C3D4A40843094B1B690FDBFC4A1ECC1D43D4A40425FD7951194FDBF9594CDD8D13D4A400CCB37F13D95FDBFDE45EF98E63D4A4002982FE52F4BFDBFFCA8131C163E4A404CF440D27B4AFDBFCFB2F321503E4A4087DCDC4C4B50FDBF94188E8C723E4A40F3AF75470D4FFDBFE2BEF08F973E4A406F8047271351FDBF5964C0A09F3E4A4090049DFB794FFDBFF5CE3B8AAB3E4A40223338CF3C87FDBF1EE20BD83B3F4A4014C50C3E76A3FDBF7FEC885DAA3F4A402EE3B46FC6B7FDBF07CA19D9DA3F4A408E892BD9D8A9FDBFA1523E8D0B404A404C259C419399FDBF50F4EC9831404A40743ADA784A4DFDBFF13D80CFA2404A40E2EA09917B2BFDBF4C28A80ED6404A40233ACC4179ECFCBF54C647D621414A4027B19C947ECEFCBF0743B5E730414A4058EC240C9BAAFCBFA809A47E2C414A4065A701AAC58DFCBFB1DC882645414A40B9F73BB4AC60FCBF807CE518B2414A40CBBE51C9B54BFCBF9DFD12BDD2414A40CDBB407FFC3AFCBFC27824E7DA414A40E585FFF3FB2AFCBF377E5F76D5414A409C1666BE690EFCBFE639FDB8A8414A40', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001097', 'Birmingham Northfield', '0103000020E610000001000000A50000004EB2FAA3655BFFBF9E85F28414394A409C511E5EAB5DFFBF747FFF4E0E394A409105C871B35BFFBFCC7148E9ED384A401F8D1CCB3156FFBF0C3513C5F0384A400E73B0585A50FFBFF27C7315D0384A40DC30F147C051FFBF77135396CF384A40146B2A46C551FFBF64E6FB01C2384A40CA0E36A2304BFFBF273399DE64384A404EBDD3478A40FFBF3F7CB26D1D384A4079E3F9823121FFBF8A567574B9374A40326429DB2423FFBF734DDF7099374A40FA6589E69E4CFFBF1CBCE4C659364A40DF674E48475EFFBF4F02E1D602364A40D3C964CF2358FFBF36B1345ED6354A4024F0D197235DFFBFADAE54359F354A40733D03B8415AFFBF40CF0D0386354A4024A22FA8CD49FFBFEB67B25CB5354A40C4B30C59AC46FFBFF157CD677A354A4035DF41B47146FFBFCFE7B7184B354A40971C1C296A4DFFBF84EF91CF30354A40C36CF178F24FFFBF7D52237415354A4083C0A14F3958FFBF97D35D9001354A402216A37D9058FFBF3CD5401AAB344A40E049B641295BFFBF01B135F89B344A402227BDCACD5FFFBFD85B22999A344A40087B5A02665AFFBF880AA14A8A344A40E157849ADD44FFBF83BB9B4DB9344A407218DD819F2FFFBFD3D755CFD7344A4033FB093CB6F4FEBF233216BFFA344A406148D37DE3E0FEBF475C489313354A406759634D6BC9FEBFE57E867152354A40FC3FEF2CC5ABFEBF59968FF8DA354A40DA4507A397A7FEBF78E31B09B7354A4070F1A4BC04A6FEBFB103CE668D354A403DBE656D99ABFEBF5DBB835F7A354A40E150183F62AFFEBF2979AAAD3C354A404B5C308A24A9FEBFEF6AFDD22B354A4096BE78FE3DA0FEBF0DF1EBAE2A354A40163EBE1A43A5FEBF9F5B2A7102354A404081B6E9C7B5FEBF8B41DF0BE5344A40CB9EE665DBB4FEBFCC75C871BA344A40AE914355ABAAFEBFE9185AA367344A402FC9BE817AAAFEBFF7793A9C45344A40F55CD6B3699EFEBFD26150CA27344A40F2DF764B939FFEBF6BE6C0750D344A40112DD1A167A2FEBF1E7F0F8A10344A406A5145CF14A5FEBF1C50A4DE9A334A403043391DDBA9FEBF9B4BB9C865334A40CB9C3CDA57B1FEBF9C22369337334A400BFC45B47EB2FEBFBEF1E3FAF7324A4085EB17EF4CC2FEBF46CF262CCC324A403715ED042AC5FEBF739AE081B7324A40CCEB038245CAFEBFB92BE1D9C6324A40FA09591F34DAFEBFA7FE606452324A40908C1C0763D9FEBF597ABD414A324A40EAC04A5AC4E9FEBF48899F0FFC314A40670449FDFCECFEBFDCB68B2FF6314A40934A476244F4FEBF79CB4C62A6314A40F24317914BF2FEBFEE2E6A3D9B314A40A63B2EFAF3F2FEBF9F132FF690314A40402539AC45FBFEBFCCB145DBA6314A40A8BC4804E508FFBF620EA86ADE314A402901E8BDCD1EFFBFD43FD53AFE314A40A3E1A23C9D3DFFBFB3D72B5558324A406EB13B8A0042FFBF8D4B30F859324A4083F97EA4B357FFBF298FD01DB2314A405FDFDC5BBC59FFBF1FF580EAB1314A402A56AB615D5AFFBFAAC16166CA314A40B1B430D8105EFFBF433A15DBCD314A4016BABF405770FFBF6D6E403633324A4009D437A63776FFBF8B3186FED9314A40908825195180FFBFE2F844C0D9314A4065E569DEDB80FFBFA21C91B5D1314A4059CE35FEF483FFBFFA206354E1314A408A1702975B87FFBF50169246E0314A40020C352CC28BFFBF25E846D1D1314A40FE2626852F8FFFBF5306E71EAE314A402A64BE00F79BFFBFC49C5FDA8C314A409BD0C99F0EA5FFBF55DDC146A0314A405D711B2850A8FFBF0AC108119A314A401551C359AFA9FFBFB410DECAA5314A40A82FD703EDBCFFBFCA3B9E2A94314A40FFBF0C80A0CFFFBF00A2496237314A405037932371CFFFBF353109EB14314A408E58437913D4FFBF2086D34FF5304A4003880178D6E2FFBF1979B1C9E3304A4016CDDCB54CEAFFBFBB223047C9304A409DFB8C6219F9FFBF69672F82C7304A4078BD0DDC5C0000C05B65B352D9304A40F219839E9E0200C085D66BA918314A40C494F2DA790400C0CCDE807B17314A40523C5971850400C04BB959752D314A40C7CA5D40880800C02C9AC58025314A406737EBB83D0A00C0BC21F6FA2B314A40A70CE157910C00C0295474A55D314A40CC637A5A531100C0E50CDAF887314A40A33E7000401700C054A2D85C6E314A408CC982D1851A00C0115126EEC0314A40474ED6AE5F1B00C02BC12A752F324A40B90973760C1D00C0A05680F375324A40EB469277DF1E00C0F534678084324A40EDCC57D69D2200C0A6C2042577324A40459C140B042500C0A88D48AD8D324A40D2D358E0CA2700C0CAF024EDBF324A40C5F9270CCA2B00C043DB937C36334A406F75244E503300C0E3F658F748334A40646CC6D3E13800C01FF7EE1340334A404FD65BAD653E00C05BC0A7294C334A405A049E5EE54400C0DB96437B7F334A40536978CC974400C0A410560B93334A40BF70337E763900C0406547F405344A4071E4D0C5F03700C044B6384A1C344A40726B4CCFD93700C0C71BC9322B344A403C7B04040F3900C076C0399035344A4073229CF7633800C0B93F10C83C344A409D1753D8833100C0959D90F303344A401345137E6E2F00C0218AE15D01344A40F475DD361D2D00C0115C989312344A40DD27CB2BF02900C007B4063D14344A403A74639BF02300C0B9931020EA334A40636B1360DD1E00C019233DD8FB334A4071FA2CE03B1D00C0E6ECC1A714344A405DFDC8F8F61900C0A66A0DF322344A40E2CBF57D4A1900C0755BB00E40344A4060A3E5D33A1A00C07020753969344A40CE481735531600C0CF1F8F696B344A405ADCCF66E91200C085703BC386344A400DC389F2941000C0571D35A74D344A40FFA1E629310D00C00BD981825E344A401C29C43C800900C0126A036E61344A403209AFBA9A0800C0DEFA3DA49A344A404D54CCCEB60500C005255D1991344A409752DF06410400C0D5BE976E97344A408F51A4FA120200C071D1C5FD84344A404792104355FAFFBF3FE4C5EF72344A40EC64087163F8FFBF5E14DF797A344A40296CF083F4F6FFBF3C273F2472344A406AB65A8E68F3FFBF8722E17786344A407C8DC85503EEFFBF67F785AACC344A40E9E5568054E4FFBF442622F5EF344A404757681371DBFFBF8F333683EF344A401025C0EC60D2FFBFDB9624F301354A409B1395CBCEC6FFBF2A269C4857354A40C4637324BBC2FFBFA6D1F8434E354A40D021ECF6A9C1FFBFE06CA6A26B354A40F5DE8D3FB0BCFFBF155DA9C985354A40233677B416A9FFBF153450C1B7354A40E53ADBF0AF99FFBFA41D2AB7D1354A408F7A0204EDA6FFBFC75899BC30364A40DF8FC8FC3FABFFBFE7A498D038364A40CC30F8BA04B1FFBF69E5628D5B364A404CD7BE5E67AFFFBF4101BC7368364A40FC6A8D060C9BFFBF3F0ED38AA0364A40B5DC7386319CFFBF0137B539DA364A409D29901F759AFFBF27AAD28143374A40D9A8D994B79FFFBFAE75C866A2374A405C40DC37319EFFBF40B9307601384A4007D599386FAAFFBF7BBABD9880384A40C42D1803D3A9FFBF2B10B1B99B384A408E41FFD2E9A0FFBFCE9930DBE3384A40FDBCCDDA75A1FFBFA58A46E2F3384A402D4E82668C85FFBF06E6A1112C394A4084D98FF3DC84FFBFCF0BDB301C394A400C5FE2EB6370FFBF40AF3DA227394A404EB2FAA3655BFFBF9E85F28414394A40', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001102', 'Blackburn', '0103000020E610000001000000BF000000312A0665F9B903C09EC097B1F1E34A40D672C7F7D0B303C018F0D93CA8E34A402DEDF7C177AE03C0ECD711F98CE34A40FE81528227A503C097816B7F16E34A40CA5DE9C2D5A003C0A1F47272F1E24A40918C9E3900A003C0CFA4AAC2C5E24A4018C002CAFAA003C04ED29E43B3E24A4032B38E88619E03C03EA5FBCC83E24A40C93358B20E9203C055CD7D9C1FE24A40B888C9B3A09503C0C054333109E24A40AEE31AFA5F9703C09A9A32D8D7E14A401B9634BA779B03C0D9DEA575A6E14A409B32E7877D9D03C0EA9FF92D7DE14A402228A724B49D03C080F0957C38E14A407F0693FBDC9B03C0FF91ADA9F0E04A40B088C1F0F19C03C0F307B7F4D3E04A40C4DC06AC949B03C0B986BBF1C4E04A409D1E48C9E59A03C04CDC49B0CEE04A40D8806705179A03C05E696239C5E04A4034FDE79E509603C02AAD9229C8E04A402DD5BE85789203C0D89359D3ADE04A40F84B9ADA539003C0A3D0684696E04A40A04D012E0C9003C0432FDA8B79E04A40B44553CFA58E03C03863E14576E04A40BD28C7BC058F03C05B90227E66E04A401EFD48A8118903C02EB0FD6941E04A40D75EA9324B8903C0E4BEAA2B30E04A408A5B9C43428503C0BE39D5EEECDF4A405AD97A91D48303C0D8C133F2B8DF4A40331C0A02F78603C0189EB79894DF4A40C2C0940EB28603C04D9F72607EDF4A404232DA93338803C0C460FB9279DF4A4081FC69B2308903C0AFA3B6A963DF4A4091C68994A88703C01FDE93A222DF4A40053E6775D08803C03D34654B07DF4A402625F9D6A98703C03036B0ED01DF4A40ADF19F50D18603C0D3AC1AEEBDDE4A40F9ED93AF0A8503C0E2F7DB0298DE4A401A8FA9CBA18403C0072CE231DFDD4A40600DB35B478203C00A8FA039BCDD4A4080DAE80A578303C0B1132B41BDDD4A4086B38F11A18603C0A0A108AD65DD4A40AAF0F200288B03C0CA6AF60521DD4A40D523DF0B02A003C0F1DF027219DC4A4097ECEF4574A903C06284BAAAC3DB4A40EDC8EB1C39AA03C08A524B9AF7DB4A4088238DDA79AD03C0DB1A24783CDC4A40275A8340CBA503C01137B12F5BDC4A408FE47BC73BAA03C0B0C0BC04BDDC4A40760E78AF16A903C0362E1BF3C1DC4A405660F023B8A903C0235E3647CCDC4A40EC5F3BD556B203C06EF3D378ADDC4A40A704A438C7B903C009D77AE356DD4A409969ED9AB0BD03C0BF81338757DD4A402D54917FB5C003C0CF797CD743DD4A40935CCA4033C503C037A1A24F0EDD4A40492E0F7404C703C0073C5E9207DD4A40F1F653924BCD03C04DEFD07B12DD4A403A306983A6CD03C04C0942F002DD4A40328C52C978CF03C01B9F188906DD4A40AD5940272DCF03C0AC6B75FBFCDC4A4081F4416761D203C012168562F5DC4A403BB28D4B01D303C0CCD53F54E9DC4A4021AF91DE8FD303C05958572AF8DC4A4089249DE5A3D303C0FF48D2DCDEDC4A400ADA92ABB2D803C0B78E7725D8DC4A40F706B0A4DBD703C03F4E3447D1DC4A40ED72B9CA49DB03C0C315DC7EA2DC4A4082FAED2D1BDC03C0359984A58ADC4A40F6354FAF63DB03C0891405B786DC4A40D32886788CDC03C0C049D68077DC4A40FCDAA6D60BE203C03F3196A767DC4A40A24DF97073E503C04CC0DCB585DC4A40760FDE7C11E403C04A169E3599DC4A4084DD3AE551E603C05C3FDFA2A3DC4A402E963B4D7EEC03C0C00AE1E0F8DC4A406DF0054406EE03C050711160FEDC4A4053B2C7C694EF03C06A800C392FDD4A4049EDC40775F003C008570DA12BDD4A4069F3A2C1DDF103C0CB7BDE3149DD4A40DE9536793AF303C016E6DFC326DD4A4084F848E37BF403C03FD462ED2CDD4A40304ADB3A48F803C069A9E692E3DC4A40A9579AFFEFFB03C0603D5FB2C0DC4A40C9518F2441F703C04465501DA4DC4A409989A79F68FF03C0C98436021FDC4A40D4938EC0690304C017CB2E6856DC4A409AD8060AD40C04C0BD0A888FE5DB4A40A1ABC54B1C0A04C073D7AE3BDEDB4A409A19A78D020604C057F80A23E5DB4A40C7C45404D70804C0BB8EB878A2DB4A409D3DAB2E010904C0BB97229081DB4A40B8ADCAD4D90504C0C78A08135BDB4A4032324531271104C06F25B4AC4BDB4A404507A183233004C0E602D5AA3ADB4A403421726C053904C0F8CBFBAC44DB4A40A6766071D45004C0F472C83A7ADB4A40072FFA3EB55104C04622CB38B8DB4A403B3FFCED175104C0CE465A1BDBDB4A400B3D6DFDB65104C0C226D277EBDB4A4031279A69D65404C02D1A6B6403DC4A40AB8E43969E5304C01A31AC6520DC4A4095A1B273675404C01B59AD492BDC4A40FDAF93DA755604C072858A5D2FDC4A40B049B40FA55904C08A54482F54DC4A408884E6DA365B04C0BB3D71254BDC4A40505625BBE55E04C0D5C95ED451DC4A40538282B0D26004C064F2E6656ADC4A4070C408EE905E04C0A42C3C7DC9DC4A4094AE79D2EC6704C079832568FFDC4A401140E042157104C039F8935DD6DC4A4064C7F227367404C00C5874960CDD4A40FD83C385CA7404C0B6CE05011CDD4A4066640DEB7B7204C00265317650DD4A40621D2C44F27304C07869EA068FDD4A40645675CCC37304C00CC43DEABCDD4A407666F1A3EB7404C0B707BA7DC3DD4A40E44753AC0D7504C025D006C507DE4A40651B07EC917904C0A8C78DD619DE4A407A66CB9F5F7C04C0184245926FDE4A4069D0DBC82A7E04C0B7B7512572DE4A408C9F5E1B498204C0C8CD9A48A0DE4A4079CDB7BD8D8104C086659CDDB3DE4A401C4B5807208204C0C31C8489D1DE4A40F09EC1C0BF8004C093657EE9EADE4A4029FE316D488404C078BDB9E408DF4A40388C2EA66C8404C0D040AAD12FDF4A4000FF2A9E777E04C0102E9B8C53DF4A402CDECFFBEE7F04C0705347CB80DF4A40FE22F0FB287E04C0FA21AB769ADF4A40C74405301A7904C0B41F232FAFDF4A404A2D4D6F007704C0E42F562EE0DF4A4055DA7E03E57204C053B08C4EE0DF4A40A35238DCAE7004C03880D883FCDF4A400D70A4C4946F04C0DDFE5DD2F0DF4A4003C7C3687B6E04C07C74BE65F5DF4A40D8983155096E04C0D94CC2F90DE04A40E0B8B5EDBF6B04C0D012233126E04A40A6AD3690026C04C03D70FD1044E04A40CE61E572426A04C03A47FA0165E04A40D2479A16166A04C033BC03D4BCE04A40290893C0776804C0D4A30D62D9E04A406A371147E16504C06A66B576EAE04A4099B716FD0F6604C04020F46DF9E04A403E33C377CD6104C030211C891EE14A4026B18CDAFE5E04C01C41179D20E14A40D47A1D99AC5D04C0A7F2518833E14A40E3D703EAC95B04C0199D1E002CE14A40D1B11EE5215704C070F192703AE14A40D6EC18740C5304C0A4E5A4613AE14A40537CBD170A5104C09F801E3C45E14A404077F7C6235004C0D7EEF7B93AE14A40E99AE675D14E04C0DE02257D45E14A4038F89A4AAD4A04C0CD05D3C537E14A40EB465D636F4A04C0B368966D28E14A4055C85388824204C0A8D4F85D1EE14A40836F424B104104C021A7473337E14A40FFBE6C55363D04C0E721278244E14A40153CBB4BE33804C0BAF5795F34E14A40B5FDA337A73704C06220E69E3EE14A4054D1BF8E9B3404C00BDC6A113DE14A40BC3BF00F923104C042F4761B56E14A40E76BAE9F9D2F04C06D75EF1055E14A4067A528E6892A04C06F2ED6E18FE14A40E96745A7A12804C0A087963994E14A403AFA94EB492604C0F8C1FCF4C7E14A406F9EB477F11F04C0CC572532E2E14A4091F734422D1F04C00CB04FE0F3E14A40FCF6774AC41F04C0023DE299FFE14A40D622753C621E04C0365AC2FC12E24A405B666B60F41604C04AB747C922E24A40EEE8A249D01304C0550D31CE70E24A406030DEE3971104C00260F0C47CE24A404E7A03729B0904C035075E1586E24A4048EEFF33130404C0725B9D3D6BE24A408D8B4A9C03FD03C0EEE4AFDC62E24A4026EDC72A2FF603C073DADB2488E24A40E96EADE05EF503C0D3381F6895E24A408213B10201EE03C05F7D0ECCB1E24A409AB2F49DBFE403C01B8EB2EAEAE24A4015E1DEA9A7E103C00D699459F3E24A40488AC89DEEDF03C0C2812EEFEAE24A4003949BC1A5D903C09EF99BA50BE34A40A5600E7BBBD803C05176162812E34A40AA959A4BD9D203C0EE74B12DD5E34A401A9BB72CE7D003C0CCCEA562E6E34A406F75C43B13D103C036F33F5EF9E34A40EDCE9DD526CA03C0EA4175BAD3E34A4085B05D58FAC103C02D14D8BC11E44A4071786D5683BC03C0B4CFB89F07E44A40312A0665F9B903C09EC097B1F1E34A40', 2); +INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001202', 'Dover and Deal', '0103000020E610000001000000740100000993C41B9F4DF63F42A79668D6A04940E18BDA240B64F63F2E82F2E1859F49402497CD43F672F63F9F50131E649E4940892627263F79F63FE0A39225AE9D4940F0011C64817BF63F914045326E9C4940184C4F6BA177F63F3793C99EE69A494001E9CF50C77AF63F06E1F1B7099A4940C6D1CD6B887AF63F33B347FF96984940B46CA43DF87DF63F64AFEF08189849402C539210227DF63F74981AFECE974940DE08616A5F76F63F5547338424974940254EE37A7377F63F85D4583D0B974940D7B06CC9167AF63F7DACE7A40C97494091BDD816637BF63F4CB4AE7500974940E001E11B5579F63F10198AE37D964940AF467FBC297CF63F8371AEB050964940F6587D718079F63F549A004A46964940253F777B0F75F63F5EFA5F06E195494048A74209856EF63FB2FE3D568B954940CE98BBEA0F6FF63F4920529E60954940911B51AA7E69F63FDFD2CC92169549404933DDA5F759F63FECC4A32F68944940F5AB04639451F63F230614472294494023B051543053F63FC40F0FDC0494494004BC19AEA94DF63F9791164AF49349400768CDC3F640F63FBF45A0AAAC93494017E9931BFD38F63FBDB18E418F9349403D4B42EA2539F63F3EA68FB481934940478BFB232C27F63FAF831C812C9349406E51DB5DCA24F63F1CD1AA0814934940D45D82198026F63FA91F0AD5019349408536DA73EA21F63FA5652CDDA9924940295B086B3D07F63F800ED9DC05924940F75F110FFBF4F53FF4352BD0C49149406370D2102ED3F53F6FC84C7C669149400367E93A96C9F53F332CF06A5C914940AAFB3BCB96B7F53FE4DA04E42B914940551B8889A0B2F53FDF8C3C6B34914940D6C4DBF451A5F53F61B4006A189149407DE7F546089DF53FA08CB4F917914940B7BD81BD108FF53F75123C3E049149406318DA1CE286F53F6639A8C7DB904940243115678482F53F335D30D7DB9049402DD114D8657CF53F8E3D8CC4C49049407FF64A27207BF53FD0A1CE25B1904940DC158D454C7EF53F99733E15968F49406463DD47F67CF53FB5B22834998F4940B3429637C77BF53FFE0F47EC3D904940E2CABED1EF6DF53F641211FD3A90494084AC6207716BF53F1C91E18A40904940BD6635727C6AF53F1D9AB7B84F904940D244B6487B6DF53F9D9CEDED0B9049402A08C6F3A368F53F6E95BA9F4A90494057CA3DF1F562F53FDDCBDC4B439049401A59B7106768F53FAEC2F2EDFD8F4940889D575B7A63F53F826A06C23590494072A3A994A15FF53F3B9532AB2B90494089B61B591D64F53F077B88BAF18F4940EB110173395FF53FC1C6CDFD2A90494035178294095AF53F4BEFE7903390494076C70BDC1256F53FA0D5440729904940D0B0AEB03F60F53FB93EF883DF8F4940FA14F5974D58F53FB9315195069049400EE38002895FF53FC4805F81DC8F49405F8DCF545055F53F956364A70F904940D23FF9D3F756F53FDCC0985B019049406D1383E47355F53F24330FA9F58F4940181B230F6D43F53F234876BFC68F49401DF576C73A3DF53FE88848B30190494041153DE78F3DF53F52D2026B0D904940ACFC580E153AF53F80A54201159049406D7D7A878128F53F7AAA656AFD8F49409154AF557729F53F038D039CEE8F49401FA7676A6128F53FC1819F12FD8F494043DA84F80125F53F57A3A3C2F98F4940B933D13FBD1EF53FFC67039CEC8F494078926913971FF53F35B9EA03E48F4940236FCF23841EF53FAE65CF8AEC8F49401C5BF0A7BA18F53F80688E42DC8F494084AC12166910F53F7596ACEFBF8F494099DBAFEFBD04F53FC0E643B0588F49405887807C1521F53F97482DA0278F4940706A51726404F53FD37F6D79578F494028C6B3FA9F03F53F450C35174B8F49408B95E211FA0BF53FEC324005248F494056FA12D3E917F53F07EAF6340B8F49405402AE3A6529F53FFFBEFEDB308F4940EC5DB6CAF71EF53F8BBE3A990E8F4940691837F6FF27F53FD867FFA0C28E49400976D0EE6F25F53F91654ACCBA8E4940E6B9CC3BEE2AF53F33C8D1B3988E4940F95E9E0AB618F53F6660B8DAE68E4940FD1F6F31950FF53FB3BA9D78A98E4940F630ECE0940DF53F0C09642CB38E49407817E8411E10F53F58A11805C88E49405D3481F0C701F53F54CF51C1F98E4940898B2C24CDFFF43F797BA395138F4940D0D77F70DDFDF43F5EC2C5C4118F494003FB72990900F53F49853768178F49401C8AFC437300F53F8447DACB378F494083EF190B63FBF43F9720F50E3E8F4940248EB2CAEAFAF43FE05B1664478F4940AA406E53B8F9F43F3D0E40D0428F4940B45D5F9DAEF9F43F7E271CAA158F494016FD25FB3BF8F43FF89D4B1C168F4940E01AA9B339F6F43F026320F9F28E49409DA0B540D5FDF43FF708CD2DE88E4940AA7A012104FFF43FEC241CC4F38E494017A662BB92FEF43F1E599AE5E68E494022C5AE3AD801F53F67D87CCAE78E4940239A8CCF4609F53FC1DC9C5CCE8E49405810F0296A0AF53F972EE66DC88E4940058FC4B4AF08F53FCD0EA437C08E494032393073E90BF53FAFA544A2B68E4940AE53153C9204F53FDBF270C6BE8E4940BC9AB86EAA03F53F0465D01CB88E4940B6D455D9C809F53FC5F54043998E4940E1878F1EE60BF53FFD063DE1AC8E4940A424130A620AF53F1DB35BCC968E49407D9BBFD34214F53FEC57E8566E8E49408678D7A87E1BF53FF162F082578E4940597A4216372FF53F505E093A548E4940B0F3D6FC1B2FF53FB40811A7478E49407CB9169A063FF53FB34C0723418E4940D28BA1A94D17F53F21257804468E4940089B54941008F53FCC5492D4718E4940B8A9B4E23201F53F3F9565D9A48E4940FBDA1BD23BD6F43FF6C5E702838E49406B45E42C23C0F43FFE1ED49B5A8E494076AA780894BCF43F878CEFCF488E49406BFEBE5646BBF43FB7AC1376288E49404DF23E8E04B6F43F602587D1058E4940BA6E04D8D883F43F25C705AAD18D49405C1DCDC2E47FF43FC556DD22C38D494091216F76CA82F43F2F2F81389A8D494046EEE1127581F43F8150A9218A8D4940CD6D7DC35648F43FA5D9D7E5058D49403A9C44EDDF32F43FF93C5D9C058D4940CEC8D709AF2FF43FFC80F7A6238D49401D4DB8ED660CF43F996F09A7FC8C4940A3DFA6582606F43FE143F75CEE8C49406FFBF723CAFFF33F3F75676CC68C4940A3743A0928FBF33F65138EE6C98C4940C641743DD6F8F33FC899C1FEE08C494045C7C47266F4F33FEA3E2AE2EA8C4940EEA9081B3ED1F33FC61387CDE08C4940D95023AD1DA0F33F11D7E8D2BF8C4940D9A35516CC89F33F87C26C318E8C4940E14138870987F33FC2CD5554F48C4940FB7477ECDE84F33FB8E4A3DFF88C4940E70FD180F06CF33F24D0BE1DD68C49407333629D9669F33F391C0276C38C4940644B3EB6E159F33FD1F7A593B28C494056F4F0032458F33FBF554026AC8C49403029D1A7DF58F33FE55A6863998C494007F779DD724CF33FA34B403F868C4940B676DB421B45F33F2780A6C7708C4940DC752A27AD44F33F2813B952AB8C49401005325F143EF33F5B4C651BC08C49402C9D2BE1A827F33FD6A9A40A058D49400E5448976A1EF33F2529DD6A318D4940902C954B0326F33F858120EB608D4940C0F776E9211FF33FBCC83817878D494049799FC3F420F33F1E6728DCC78D494003999720261DF33F0E94CC8C2A8E49407F31AA87A41AF33F6397410A3A8E4940B93236920E21F33F7637B0F6988E49409AA241455E21F33F0415246FD58E49408B2C38F06A28F33FE75D4F7F1F8F49400A90D7933314F33F7DC75189408F49406D2FECF81406F33F1EDEF061438F49406F2906CC1001F33FAD438F3D378F4940630D68970701F33FAFE2E6D35B8F4940909599040AF9F23F3249F7298A8F4940A9807C89D4E4F23FE4EB2E05428F4940D6A22E08CDDDF23F45CDB5C7788F49407AB4B43564DDF23F76153E3DB88F49402AFBB2B31FD9F23F70A85F9CD58F4940F7C3BD7B45CCF23F269BE95AA68F4940861B981ED2C4F23F8A05D8CFAE8F494023C9544C74BEF23F8CFE9A3FF48F4940C53CF3458EC8F23FFA6697BC389049403027855A56C9F23F934CBB4A419049405EC8B9DF75C6F23FB24620DF47904940FCEDB9ECF0CDF23F35A4A77A8A9049402AD5A1F4ECCFF23FA4FA565E889049406A584161FDCEF23FAC27290774904940155D129256D0F23FE290E27E71904940AA7CF7CD1CD3F23F85E6D7B0AA904940679E99EC37D1F23FA6F3E73CB3904940A2D2CCFDBDD4F23FF38B375ACC904940F5036CDA34D4F23F6CE7657CE29049406F594246EAD8F23FC70E18861A914940B0CF3B0CE2DCF23FADDE6D3238914940BAE66F2C2AEAF23FAE302B16129149402502411FBCF3F23F2A5A598C40914940058C250889F9F23FC41911BE2B9149403E86B95C1704F33F79A168E741914940B254F699B80AF33F18BEE00A68914940EAB0BDC50207F33FD65FD04C79914940F1DC276BBC06F33F2EB065C0C2914940556CF7EBB81AF33F7BF87E2720924940F21C4DF02B20F33F9AB625044592494005B4E339522AF33F941BBC1264924940663D85942F2FF33F70F8C10F899249401E30B2237F3CF33F16A2FE1FA5924940A29F9ED9DF3FF33F699BAE9CD7924940C9FC0364B945F33F3FEF0F95FA924940FF471540ED53F33FCA9BF2D13B93494090F1C8718560F33FFC0B2796639349409B9CD55C9060F33F970CCE5B79934940420AEAD39D5CF33F39DDE218AC9349409D74BDF2A552F33FC411F32513944940F6CA0AACD03FF33F8441ECFFD9934940BA219A351E38F33F36023739E0934940014DB6A05A1BF33F83CB81DEBD93494000448473F00CF33FA517F7C9B69349405D30EC997401F33F20F498E1D3934940923AE86089EDF23FB4E2335D379449409EE0B48F15E9F23F5D686A59249449403F96323C76E6F23FF63FFE20FD9349407A9E2A3268DFF23F133D776F0F9449402129CE805CD5F23FE5D222D515944940394FE13296D7F23FAA8D2B1A62944940CF4797F739D3F23FA1D28101CB944940967CFFAAD1CFF23F9850A68DC49449408AC638325ECEF23F5F8FFEF0CA944940C95F5D77D3BEF23FCF4654A7909449400A0BCB98ADBCF23FEFD22554A194494001363DB954BBF23FBC2243A4E19449404ACCDC2128B3F23F0FC51ACFCD9449407F640EE234A8F23F062E78B88294494043D9A83334A7F23F98765CD149944940A9A7E99A5797F23FA8AFA6F139944940AB0BCC692E86F23FA9B3B84D69944940EBB1AAF12A81F23F6FFD5C7D7F9449400181ADB35E80F23F266C097AA49449403041276D5774F23FE7C6E79AD2944940836EFBA3B84CF23F2678A12838954940110CFDB3CF48F23F39BBCD94A59549409EC9BC23514EF23F3362DEE13596494079ECB939B35CF23F6FB631A0AD964940EAC1932DF765F23F100AA1F1B6974940A168ED969866F23F8FB4FD9FC19749403DB41D4A656CF23F48CACF25B69749403A0405F88F6EF23FA5E3B863179849402D8D5EDC5C70F23F53898E2618984940838A74824475F23F802945C86F9849405683EB695B86F23F5AC6CD04739849405ABCF4F1A38DF23F3FB918590C984940590A2A84C59EF23FA8FA2260B39749400EBF6FD9779DF23F733E1B5B9497494060C10CB3FAB7F23F54A7229C8B9749400F2A150928CAF23F137F908DDC974940D66D050679D7F23F9C7E3425FC9749406AA8AD0A37DFF23FAD14C8D2199849406CC0EFC7A0E3F23FAA4BC9073D984940049AE99CB7E4F23F813248A361984940A82CB77F4DEBF23F312298575C984940FDA89DB44EF1F23FDE071250459849401CE1C6882303F33F553812EACB9749402F61AEFCFE12F33FE7906CF687974940130E428FFD1CF33F05A3380B929749404695DCBBC920F33FD43A2A33A697494052439E83FA1BF33FBA4FF692B59749402A617937DF2BF33F47FD329EE397494095F86F9EC029F33FA9E15AE0F297494075C247A6D63FF33FC069704A7E984940B57EEF6EE54BF33FB57BD064B798494000ACA1977551F33F7449AB5FDC984940A3E6CFF31653F33F24321E1CFA98494016B11CCD2A80F33FC8676EEC0A9A4940F15C017FE587F33FBE50B76A499A49402354F6911B8EF33F3F5C0FEE8B9A4940DFC3B10B5883F33F41FD410CCB9A4940F639D4B20F57F33FF5968113449B49404C2C8515963EF33F8D87FFD9D39B4940E59EF89CE82CF33F30D64A310F9C4940D9857A7F8027F33F6112BD041D9C494045825B788717F33F5BE7FA972D9C494083F35930CD0BF33F8F20809B1A9C4940231C9BC001F8F23FAF7CD786E19B49404F20D8BDA6FFF23F28E535B4209C4940AFC2B9835906F33F271FD551959C49403B36F6FC2F12F33F89117579109D494092EAF841EC19F33FA75EEE87829D4940FD1AA212D91FF33FF263874AA89D4940979FEC6DB129F33F87B153F2519E4940E652F01FD12EF33F7204FB15869E4940640C030A0C37F33F16BB3C46B69E4940441BF3CD264DF33FB690238F859F49404327DFDF957EF33FF247BE1E1C9F4940E281DD4A1374F33F6BE5C5E9C29E4940B4087225F66DF33FB71DF58FAD9E4940B2F6B050F677F33F6495982E8D9E4940F9C1113D3481F33F345582D5579E49407B5A16611D85F33FCB6F73EA729E4940EC3028E3AE9AF33F598E8F88159E4940577AD355289DF33FA34ED912229E49405E69DAFE7EA6F33FBB52BCF7FB9D4940BFC8A80B31B2F33F8C7E72BFE19D4940AA4AF9CD89BCF33FB03864EC059E4940BEE62D0CCAC0F33F04022722F89D4940168F494B3CC5F33FCC0FF844139E4940385D0E1D84CEF33F206F9AE8269E494030032AED09D1F33F25786AC3189E49403C1948539BD4F33F2385A869259E4940EBA965759BD3F33F2E1158F12B9E494001590D0E53D7F33F3026988B3F9E4940F8C7CDE369D9F33F8381B085319E494081FF001148DDF33F05EC178B499E4940D3CEA1307DEEF33FADE879CE189E4940229CA89AFE0CF43F572395D9E69D49408894DAC8270FF43F1CED1AB3DB9D4940884858D7FC0EF43F2FB5E15EB99D494000BB4CF14215F43FC988FDA2439D49408A30FEA18125F43FDBBB2A032E9D494099E2315D222CF43F0CC8D673149D4940B86A84286A3AF43F6AA49F385B9D49404C70498EFC4DF43FF2341B6F8C9D4940C751503D595BF43F03AFDBD2D99D4940339B6DFE0D61F43F80352205DA9D4940459CF2867F6BF43F5068985C2C9E494075DC24C23A77F43F64B634C2A39E4940DEEA59D6A082F43FCBB77128DF9E4940486DF6D6747EF43FFBDCB133E99E49406A9DA3114788F43F2AF156A2269F4940701F70D73C8AF43F4EFFC6A6209F4940685F8CA3409DF43FBF8BACB7AB9F49407008C46FEC9AF43F67AF3485B29F49400B2D8BA6D89CF43FB4E4177002A049408F2D9B4777A3F43F7D8BD72949A04940D8B7F2AFB1B0F43F54EE6754B2A0494079013CB9CFB2F43F40F4375EE1A0494090028412E8D4F43F740178A7DAA04940E3A23E6F6AF7F43F633BDA9AF5A04940AA428BCA4A0EF53F84095E5DF0A04940BFB154A3CC2EF53F80C06358F8A049405DBEF1ACA842F53F3B50E3697AA14940D8925F9D8A4FF53F2EA2F12404A14940F0ADC456535FF53F20F9701A36A14940875A040FEC5DF53F9F5E59B380A149401D72B52C7F62F53F19F6172C94A14940131328D72066F53FF3827394BCA14940B5548AE17F6AF53F1E11CC6886A14940C15C4EFDA970F53F295D921669A14940EB024D2B7175F53F9184B5467CA1494006EBBE3FE287F53FC92598588FA14940C9205EEE528EF53F099D7C8D81A14940DB7BE729608CF53FBE7ADAA5B8A149404A5871EA7990F53FDEC1C2CCB8A149401C72BA966993F53FB04FC3F2A9A14940ED3B88415891F53F01B9346385A149401CA7D20C799AF53F8533E71596A14940BABD88D4FA9BF53F503A4AC5A2A14940F9DFF17888A8F53FEE026DF7B3A1494047D3289E9AA4F53FE0DCACADD8A149406347987030A7F53FDE2DA931E1A14940DA792952E4A5F53F8E1CFB99EBA149401A44E63C0BA7F53F657138D2F0A14940E6DF2F0BB5ADF53F4CEB8C2CEFA14940AD909C93E6BCF53F8FB74E4AC0A1494074A04D33DACFF53F57DE99986FA14940C6BB3EF339D9F53FF40ED67932A14940A0ACC7837EF9F53F00A01DC12CA049404FACBCC776FFF53F5AB2975B33A049406177E8B25A19F63FAB2EA8CA05A049400B831CA78927F63FC4DC638777A04940E7B00E96A92AF63F6B7CE40D7EA049403F6FB50DF62AF63F97E7067F90A049400F6571F94736F63FAFEB78959CA049400993C41B9F4DF63F42A79668D6A04940', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001204', 'Dudley', '0103000020E610000001000000C90000003C08E82DE3F800C0831D33B945474A40FA652CC592F100C02F18A522C2464A40C375E669A3F200C0732B5667BB464A401533C2EB4EF100C0C7336671A2464A4071E7ADE016F000C066475281AA464A40BBA2BFDFD0EA00C0E80967A63C464A40773A8EB88EEC00C03453B6EF34464A400B1259EA8AEA00C07653FFF60D464A4098410E5AC9E500C01749266523464A4014663E7949E400C029A3312307464A40E716983D58E500C05C3E4F99E5454A401830CC058AE000C00654646DF3454A40369B9C3AAFDD00C0F4AC5EFC9F454A40AA2A0B21B1DC00C0CE6F87C6A5454A4007874EFD0EDB00C06C15646B95454A404D0A215556D700C0028D8CF698454A40164A03BB3ED400C009F26A8E51454A40AEFD2A5CC5D600C0A39EDCD447454A4029FE1C1454D700C0D96787D753454A40788AAFD620D800C006EBA16951454A40F6A6B478D1D700C06CF454B843454A40A442F33D0DDA00C0AA2B2CCB3C454A4090D16D24ADD900C03ED4448E36454A4099A8B386E6D100C0EC88D24849454A4000B2FAD399CE00C0AF5671D539454A4007377ADBD4CD00C0DA92066129454A4059CACDDC15CB00C0280175EF35454A4049C4129418C700C02D85C2E432454A405558935C87C300C0AF8060763F454A408239E37939BD00C072D91AAAD2444A4081E0A65D69B900C03C7F885BBF444A408D8F4E2966B400C03351D0CBBB444A406E11EEA4B5B000C07DD8124E11444A402CB9854463A900C03FDD6DCB0C444A407D02BAC6ADA700C00425E2F724444A4068D8CA3F149F00C06CC207C718444A40DBF94D4611A300C0AB51B8B496434A4052AEB64B00A300C073BACA3515434A40C761758A339B00C09D11E34A15434A40B26163F74D9200C087C929E2CB424A40563AE7C8EC8A00C0C0E7A82170424A402C08E7DBC28300C0DB105238D8414A401FE9E521798100C002D18B92C1414A40505FCE1FCD7B00C0D581479DA1414A40E0F0167A437500C03697961C92414A40CD91B6C38B7500C039BB998E82414A40AE3E1C25D77600C0845CB83881414A40074BDCD5327700C0E49618C173414A40C3C090C7297600C0CFD5E0C16F414A40E9F8227E8D7800C0B63EFE9D48414A406B24B747D47A00C027B441EB33414A40F4E5EDB0B77D00C0840BAFC939414A406134D2D5EC7E00C00BA120AC1C414A4014BF1CC4E17F00C0C155C306FD404A40B8A2E2F8B87D00C0C567A019E8404A40533D181D357E00C045495AB3BB404A4016019EE0F17C00C0826D967BAC404A400D059F9BE57D00C061CBBAAE9B404A40C881403ABB7C00C0439CF7A592404A4028E2F71C947D00C0DA6D98F569404A40B97FBA6AC57C00C0F0E4653254404A407F2D1970F08000C0BB299E5F44404A40804DC46E218200C0947908281F404A40F1EBCE7A978000C08FED1A2CFA3F4A40C573B01B238300C0415F1623F23F4A408E53A4BD208500C051D05A53CD3F4A4012BD9475E38600C0EF03E3B3C03F4A40C80C26C2A28A00C032F56EE8C33F4A40B107E5716B8900C0AB588147A23F4A40D42F648AFF8C00C0C8638053893F4A40F919FE4A608C00C02D0481D5653F4A4025B9B168DD8E00C0CAD07F96483F4A406B2B3B30EE8F00C078EC6F36F53E4A40D805FB751E9500C00085A7E7323F4A4014A2524F289A00C01E2BE2DA583F4A40FFD4D82933A200C0C1D51885743F4A40A7EC7AF481A700C0251AB22A9B3F4A40B3311B823CA800C0EB91DC3A903F4A403C6C822F15AA00C07E35B72F933F4A40E3D4C48E4FAD00C0E73AB802AC3F4A40BC51742DE6AC00C0AA34C263BA3F4A40DC53ED3E3DB000C0843661F7EF3F4A4006848B75C3B100C0B7D6C903F63F4A40C1D96726A6B000C0B03A65E004404A4056DEFE3E53AF00C0A5E4743F52404A40C822223086B000C0635698655C404A40F844E15C28B600C0A6D27C6132404A404DEDF17A14BD00C0BB47F1880F404A40FD93651338C100C0810358A225404A401090924D8DC000C0F184E38E3A404A40D1F8BD4F9DC400C0A122DAE15C404A40D1D40EC09FC300C09071E9186C404A407B7B135DD3C300C04CEBE24FAB404A407D3B35C19CC500C05329F91DAF404A406CBB575671C900C06BE9D60CA5404A4037F0A6A58DD600C01252879A5A404A4086B30C5F11DE00C0C4796EF555404A4077AB7C5A11E600C0EB2C31443C404A4044C5946A53EA00C09B71A5631F404A4001BF243C96E900C086BE377D1F404A4037340E82C1E900C09E95EEF50D404A405CA40E5A5FE300C027B43CA5FA3F4A407D0B366D36E500C03B57AE2CD53F4A401BF081B045E600C063BD5114DA3F4A406D283EB611E800C03F949A3EAE3F4A40C11CFB41EBE700C02B1281E69E3F4A40DD5A19EFDDE500C07260C3199B3F4A405DC4C1BA3BE900C04C6AD7C04F3F4A406522400FA2F400C007CE694C903E4A4062BECD1E81FC00C0BBA3EA316A3E4A40B02C3027080101C0D411E1C63A3E4A40541B89632E0701C023727ED1A63D4A40BB051F4E5A1001C06A4E84B8AC3D4A40D0CA0E35511301C03CC701960C3E4A407E68BCFF831701C0D80A70D65A3E4A403151E8606C2701C0852AC170203F4A40A7674670A42801C097146DD13E3F4A408C8F4A59852801C0ABACB31D663F4A40C589825D652401C077AE1EE2C73F4A406A03049D012401C0DAC517F44A404A40F62294D05E2F01C0F4573CDB19404A409ABD2DE55B3A01C0BB7D84FF0F404A4086EC914CE73A01C0BD22878C24404A405E587BFE4B3C01C0699016AA22404A40ED7F9091A33C01C063B0281551404A40F0A58CE15E3E01C0D67D574F50404A405984015F334001C01729A74F8E404A40DB091C32084201C08443A7E38D404A40320D4884694201C0CF6DC7579B404A40232F4A5BD34401C0B2E23C9599404A408C022CB6934401C039E1F459BC404A409DF346F4B24501C06BA43FB0D4404A40A9C00DE1C14101C04A1953D6EA404A40107DCD721C4301C03499BB8C01414A402613F46CE54101C0D6856FAC06414A40E1302676BF3C01C0CE8C789011414A401F4DDAECF43401C075E83DA311414A4085FAC63B623101C08DE4F5F61D414A40EFE53F4F043301C08285E7842F414A40742C021BC82F01C00DA6E5AF82414A409EEB5FC5962D01C002BDA28279414A40C140380F772B01C0CAA77CF07C414A4014C3F10B482D01C058374296A6414A40E1F476B42B2D01C046D4AD9EB9414A40C1CB8185083001C05981815CCF414A402944D938B43001C0F5F54186E1414A407DBCBC8B2A2701C050A49E9B32424A400FEDF0178D2901C082BE9F0045424A40A2AF0261F82701C084EAE5EC82424A4023E4D8121B2801C076FACEB7BC424A404B1A2FBF262B01C00E7E14FFE6424A40560CDDC13B2A01C07A3BECD0E9424A40CFFD2DA4902A01C09F5710EE01434A40053070593C2B01C0319D7A89FD424A406E23290D382E01C063A1639017434A404B75AA3A4E2F01C0B82AC96E09434A405F35E70FE33201C0A3ACF7BC2C434A40333630E1B93701C0372348024A434A40BCB3F981423701C0C93F73EF59434A409E49C12BFB2C01C03FF41D5E99434A40AEDB82AF012B01C0019DDF0AAE434A402C333214152B01C07AA81B50C5434A401B78A4556D2701C0D78C44F7E5434A405E704DBD581901C0E3C321F842444A404688C186631901C0F218126051444A400C7E0D50E81801C0EA9CF50B5B444A408C1AE8F6941701C053F990C659444A40A408418D7E1701C02893C89F69444A40B49228C8AE1501C0AAAC493769444A4001A1B8CF581501C08530D72E76444A405F48B786E41701C055EE467E78444A403DD453F64D1501C02D7A13999C444A4077E20E03C31701C00CEB364BA1444A4040901602291701C0E0F2BA85CC444A409C67938CFE1501C012F6ECE7D2444A4004DA447F2A1801C01F9BFF34DE444A4007A87F105D1701C064D07E3828454A4046376E30FB1501C00DA4CCB236454A40DDD1BB36A11601C07558AE2157454A4056A09C7A9D1501C06B7F23668E454A4061CC62E82B1801C0A8B3BAB08E454A405E02BAA0811801C00EA89C4FC3454A4015AF8E3EDE1601C0FAD3E08ED3454A40216A73F0B21801C0E14B45ED05464A40059D949CF51C01C088516F8427464A40779C7E3EEF1A01C07B10C9BD3F464A40766C967FB51A01C08F06375654464A40827F1C3C8B1901C0EEC5415557464A40E58DC502411401C0668697569F464A40F3E71DC5961301C0E4D730519C464A40B5ADEAD6511101C030434D4DD7464A4059EE557D611101C00AC80AA8EB464A401A282A9DB40D01C0B5B3EF3AEF464A406222FBE4EC0601C0F6F48520E4464A40F1A6F47FD20601C035AA1E0E14474A400DE77BD8480601C0C922618824474A40E4D8038D860401C090B19A5830474A409D8389E1C30401C08E0D28D856474A406BD46C42F10601C01C4B76AD62474A400FFBC9EB510501C0CC6C415273474A403C08E82DE3F800C0831D33B945474A40', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001206', 'Dunstable and Leighton Buzzard', '0103000020E610000001000000EF000000AF6976231975DFBF045C7AE602F449406BFE4228D89EDFBFFEAB372DFAF349401A8B514FA3D9DFBFEC4B9A68B6F3494068B78F9C8530E0BF9FCC304547F349402E2F98E28908DFBFA166AD8217F24940C3A23015E2F8DEBFEA36BCD1B3F14940EF883AC60502DFBF09BCE36BABF14940B4BFBD8B3064DFBFD7C0CF38A5F1494027689782B59ADFBF31A7FA918BF14940530C8982F7B8DFBF4E8961F271F149408874555B7B9DDFBF619A5EB143F14940FD7D08CF1209E0BF9CBBF480E5F049407234406CC5DCDFBF3DFBC0A9ACF0494090D330570FE0DFBF4829F1ADA6F049406E5A48E738D0DFBFE7DBDDD28EF0494039AAD6994BB4DFBFE19280D26FF04940DC0A81D31FC9DFBFC3D4ED8E5EF049406D199088068EDFBF8F12974400F049401C2A35F851AEDFBF9242CAEFD9EF4940DB99483A7EC9DFBF096FB34202F0494036330FFA58EBDFBFDE76DBA9D9EF49402C9FB073FDF5DFBF011BBA4CE7EF4940BDFD37452E08E0BFBE830124BFEF49405552AA8B3D16E0BF126C05E4DCEF4940DF2AB60BF40EE0BFDB2AC565C9EF4940746BE28ABB25E0BFEC764FFC6FEF494049D89B103133E0BF1C827C3688EF49404198D6ED2E3DE0BF2B50A11073EF49402E5DF826F447E0BFA41E50EE72EF494082EC457EB26BE0BFB7EEEDD24EEF494019B9280D7176E0BFDA19466877EF4940D6FA4C960393E0BF67A6A9E5B6EF494014D96BF07BA7E0BF317419A8A3EF49409D17051E88B2E0BF3ED67E73B7EF49408306FA5BAC9FE0BFDAA3116DCEEF494015CDDAE61EB1E0BF0014EA392BF049407048C253CAD6E0BF3FE2C78EEAEF4940BBE2B9BAC0EAE0BF5FC4D8BE02F049407CAEF739CEF2E0BFF64466121CF0494011DC7DA477F1E0BF7ED9180627F04940689FDE67DBFAE0BFE81A69623EF04940441503AB46FCE0BF277589C436F049405CD60EB33828E1BF37747CEA32F04940A65A9DB8752FE1BFC49EA50A34F04940B97A8F525D31E1BFA3DB93543EF0494080F3E588B056E1BFCF76C6342CF04940794A17C4AD73E1BF0BED67F346F049406B9EB92ED178E1BF3B9637AB32F049400924A56C9C97E1BF4D89FC515EF04940D77A605C5492E1BF9E0ED1588DF049409398123C8799E1BF34F24C8890F049400F986342EA8FE1BF727E0B22FFF04940D889FE23849EE1BFE3F94C86FCF0494008C1441F0081E1BFAEFACFEF35F1494044C7B399906BE1BF6837B12275F149404E8050BDA5C6E1BF765F7EA9F0F149401ED0885C6CF3E1BF904639ACEEF1494052D8CC806914E2BFF84F858049F2494024911FAA351AE2BF546E420872F24940F5217FC91F27E2BFCC3B6FCB6AF249403324CD65BF31E2BFF04894CB83F24940EC450DC36D35E2BF389E8E849BF2494040CC64A9E227E2BF47E6AA40C3F2494088D77FD8693CE2BFA822918BE5F249405ABE5CE29E41E2BFA35AD100E5F249402C06F8F1B746E2BF05D8194522F349402D774674074FE2BF167972471CF34940B1D3F63EFE64E2BFC7B3FFD931F3494048BEC1B58F77E2BFE2B5DFB935F34940AD8D1C3C258BE2BF3FFB528C31F34940652917B25890E2BFF42A6FB928F3494019C072D8629AE2BF151BD0822EF34940784BAC20C3A3E2BF80ACAD3029F3494098A900EF32A5E2BF1B8F27261CF349406D191EB8FCC4E2BF188E8DEBEEF249400CBF9EA473D1E2BFF171F99BF0F24940DDFD1387C2E2E2BF78697749DDF249401E7E5037E6EFE2BF50633148E8F249400C2B880B17ECE2BF501CEF94D0F24940573D3667AA03E3BF3BDA7C08DDF249407280983F913AE3BFD6D9273AC7F24940495B807BC84AE3BF41F53E10AAF249407891C025B75EE3BFC7C0FE0A9BF24940991239F6598AE3BF386476D859F24940667A54F288B4E3BFE61F6EFAC2F14940A49F97F3E2E4E3BF3F7E4A8671F249401A4FB9478A0BE4BFD43E58B173F349408A7E7ABEE915E4BFC4D6F449A2F3494074808A087012E4BF352D1D8EABF3494068DC4DCB0825E4BFF1F3EF05F7F3494084E3BB0F1522E4BFD55236B9FEF349409D239F15783BE4BF829FE6D44BF44940ECEAC1747167E4BFF9587639C5F3494051B057AF24A8E4BF660DE6BC1CF34940F55C4342F1BFE4BF483F0B205EF34940E1BDAD2DF1D8E4BFD10D4CE056F34940430B3A64A0CEE4BF887AC90536F34940C1851BBD47EFE4BF445E1FFFE8F24940F5AF64D7810BE5BFD034D46889F2494012D8E160B217E5BFD55407E97CF24940B8638E5B0621E5BFE2652B9580F24940A5868A767D33E5BF8D4FCD77AFF249404EE2963F4A36E5BF02BB26BFA0F24940E3316452724AE5BF1DFF6CB5BDF249402A8AC99AC84EE5BFB035A865B8F24940FEAE73490253E5BFE306327EC5F24940AD8C746C255AE5BF2C4479C1C4F249401F5CBCF0E25FE5BF64009F33EBF249402B9EC855F866E5BF4EF7F0D8ECF24940CD7EFBB3807AE5BFD620322D33F349403496770EA783E5BFC340A9EB38F34940758F167DF187E5BF1CA893624DF3494038F58DB6CB85E5BF212427B45FF3494002E35821E28BE5BF06F4FB806FF34940480E9AA5008FE5BF0E0843E465F34940A4FD6B552C9BE5BFDCA4087E62F34940D51552FFA2B5E5BF93BF471865F34940D461B59D89D8E5BF57CC3D593CF349407EA6609C0912E6BFE7FB580C39F34940263CBBCD0E1AE6BFE84FDD4C40F3494038AED2B27028E6BF8D3B98BE36F34940109B649F0239E6BF7B16AA124BF349401404013CB743E6BFD4ED741C6FF34940709BDB671948E6BF2527AEF6B8F349400A86D0701178E6BFEE4ADC035EF44940B854EBD33E5AE6BF252566FDE1F44940DA4F13D05A44E6BF5226B0CF63F5494018162598F129E6BFD48F8775D5F54940172B8DE5DE16E6BFA082D19174F649409D2307FE7720E6BFE62713BCA9F64940782A9FCEE70AE6BF8137B056EEF64940652EFF6EC9DAE5BF32FA10C0C9F74940FBD2E98245D0E5BFC5ACB308B2F7494032E80B70F8C3E5BFCE76D065C4F749405D80D82EFBC4E5BFDAE9751ADDF749400DD951A887CFE5BFDD3A2ED903F84940ED93E3DFDECCE5BFE50A54C112F8494038B1B85672D0E5BFAA9E06671CF84940114B604B33CBE5BF8154F2EA24F84940FC475DF894D3E5BF218CAB814BF84940F966E3433BD5E5BFF1E3CD577DF84940BC1F0667E2DAE5BF4C08719D8DF84940B0E079C44ED5E5BFC179CE72ACF84940C0D3F76C40B7E5BFB8E688FCE1F84940804A9B62B498E5BF2905F2C8F7F849408AF8DBC9AE91E5BF10FDB64FE2F84940203481604390E5BFB84782D0BAF849407903CF68AF74E5BF9257348DC8F849408DE00A0E9074E5BF3DE582B6E6F84940FA3930F84181E5BF6F9620E8F9F84940E512D741BD86E5BF252EF08D15F94940B6482ED75582E5BF9F2B475D2BF94940E02714E5E975E5BFBD4241FC3EF94940F4D8538F366EE5BF12FCB3DD6DF94940AC311F1C297BE5BF897DDE1191F94940BAE60275717CE5BFF7AC9D09B2F949402CB54691DE89E5BFFEDC8462E2F94940B917AB30FF85E5BF5DAD8159F7F94940C02D24647387E5BF491D37CD2CFA4940637E98F0A581E5BFCD1B82445BFA49405EEF6DA6F97EE5BFA254C34F65FA4940474BC63FAA5CE5BF75D37C502CFA49400415BABCF94AE5BF576A9DC73DFA4940C139C6A5694DE5BF61F9906450FA49401E5AF7C5BF40E5BF955364506FFA49408C9485E91D30E5BF0EEA0F634FFA49402C1AC5FD3521E5BFC69F055299FA494053C83CA18519E5BF89FE27C8DCFA4940A58FEAC008F8E4BF0B83334CF4FA49406A134C0C9CF9E4BFEA6D7A490AFB4940E0D9463592EAE4BF0A70080161FB49400049233BA7EAE4BF3C449FB09FFB49406A6FC5A680E6E4BF0C175C8CB4FB4940F23206F78CE9E4BF59F57529CBFB4940FC24AA1355E4E4BF13B7E43FDFFB4940915FEE9CFED6E4BFA50DC974EFFB49406DFE7130910FE4BF3DAC8EA842FA494013B9375024C2E3BF0126B8CE8AF94940BC15980D7588E3BFC197463511F949406C9484986083E3BF34B8698604F94940C13EEC496085E3BF49ACBBB8FBF84940D6C60E5A1D46E3BF9277BE5365F84940B9020339100DE3BF858075E8EFF74940CD9B54F2E4E5E2BF537CE5E48CF74940829FB29FAEDBE2BF3C78C7249FF749407E02F892CED4E2BF8788204D88F7494063EA5C5B97B3E2BF37023C63CDF749405B38C85347B1E2BF3360C1BCB6F749409E47464433BBE2BF09708E2176F749403AD44A1F26B2E2BF135D580271F74940951DA48312AFE2BF60018D537DF74940D703F64947A8E2BF673F891A76F74940E09C232E71A3E2BF0BE1D70C88F74940401A8DFA4D95E2BFD0E97C4390F74940314F7ECA568CE2BF2AD581E883F74940BF773D8DF288E2BF10E2B22A8DF749404D4CDE23F481E2BF9BD379028CF74940C4CCE24AB57EE2BF4B06E790ACF7494008E1E943AD7BE2BF0B00023CA9F74940F264D0E58862E2BFED1080A1EAF74940DC1B2E13DA56E2BFF44CCE4DF8F749400E873A5C4145E2BFEBF3D383AEF84940DE0263559443E2BF788920A2A7F84940913D27C94419E2BFE714B05ECBF849409BE69B277917E2BFAFFFC281C3F849400715BF7E12FEE1BFE7CDD2D8DEF8494028AD148FE3F9E1BF5AF18246CEF849404D4DF39E70C8E1BF6FFA0BE2DAF8494021159CBF2F9FE1BF952CB9ADCBF84940D5BF1E54F986E1BF1ECAC77C96F849409ADA334F7D8BE1BFD103335F89F8494083EF8C57487DE1BF310EB3EED4F74940F81E41F0216EE1BFDDB61FBDBAF74940B9CAFB45C35BE1BF41E0A2A2BEF749401CA75F04532EE1BFC0A202D6E3F749401EAF7D02D3FBE0BF08E617D6F0F74940BD33DDB697E9E0BF3BE3E2ADE9F74940140C61A9FED4E0BF0A26C5CDF3F7494002F2EACB5FC2E0BFE98EDA06EEF74940EFD92D0AC7A8E0BF1C186F84F9F749403C78E68AD899E0BFECA2291E0AF8494015EABAEF8694E0BF2345D5A9DAF74940F8ACB4B78695E0BF3A33726DB7F7494078FBF932F18FE0BFB04F24E9BBF74940CDC5FF59E675E0BF2FD8C708AAF74940B65D2760AC7AE0BF0451C8397FF749401B57125BA574E0BF9C842E0665F74940A657B7FF4365E0BF52C7FE004FF7494008DC4E5ACB62E0BFC621902541F74940691FB48D0462E0BFC9AA3ADBD3F6494030F88456C46EE0BFC97B1C299AF6494034D3A0AC8271E0BFFCFBCF3B33F64940C7C739DE1079E0BF1522879B2EF6494095B34613143CE0BFEFCB0C74F5F549400A38B0B7CC33E0BFD7E9FA43E1F54940997A5465D415E0BF0E91FDDDC2F54940424DB71F33BDDFBFBA18FACE6AF5494094EFDDCD839ADFBF52715ADED7F44940AF6976231975DFBF045C7AE602F44940', 2); +INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001225', 'Enfield North', '0103000020E6100000010000006E0000003E9E1D9CD16888BF7645150127D749400B16DD8486A986BFF909D7BC26D7494066F96F27AD2485BFC56225F99BD64940B54E7AB31C5F87BF1A24C06AEBD5494060E656DBAD9F85BF67737F48A6D549409DB55EFBBAF986BF6482981C74D5494063C703DD9A8387BF7FF2B3A2F5D44940536D0575C04582BF7770B38389D44940FBF60845562788BF88B6A4D094D34940ABB17B6794D387BF2EA00E432BD349402768AC579B1B89BFBD1C369FB7D249402FA092D393418BBF13A21386E1D1494068D2CE0404458EBF1C9313A880D149401B8FC9945AC992BFE6A042BAA1D14940BBEC127C7C5593BF24D1C8BB93D149403F6216C75A869BBF1D432777EFD149404A82D2F7F2CA9CBF734D6AA5EED1494078F4A6906BC99DBF428D1703E3D149408D58D937CD7B9EBF6E8932F4C8D14940479104B275769EBFFEFA40E896D14940C082A90675F39FBFE508873C97D1494007F038A36056A8BFAD0723D3F4D149409D557F518784AABF4D0543E104D2494062D8EEC426ABAABF97C545F619D24940F7866D0A8436AABFE6AB820769D24940320CEFD11C02ACBFC0056A297FD24940751102C491F4AEBF859B92798DD249403A5CDF491B0FAFBFA838712751D249403A4D6EF46B64AFBF2D1AE8A126D24940FF91F2E223E5B0BF8D4FD4589DD1494060EA279FFCA8B3BFCF11ADB33BD349408CF74A66011FB4BF2ECAED20DED249403FC96F676963B4BF4FD8E5DED9D249403CB710359F7CB4BF81F5788125D34940F3506320E2B5B4BF6603ABD623D34940A187A2ED22BBB4BFF71B453235D34940F18282CE7EF1B4BFEF09148333D349403748816530F7B4BF98300C3C48D34940844E35B4019FB5BF7002488148D349406EE5DAE199EAB5BFA027A3915ED349403C7693DD5A3FB6BFB025137267D34940B67140285D3CB6BF62DF06927DD34940E752DFE2E85CB6BF9DAA73A77ED34940DD7556EDC464B6BF96EC227D73D34940009BC7396EB7B6BF4C08C18179D3494011A8E7A662B4B6BF8F9D7D6387D349402BCA0745D4DEB6BFB42558B18BD3494099CA4EA3CAEBB6BF90A45A607ED3494079E091F48D3EB7BF2EEBBFF16DD34940A8230A1AE257B7BFD678FF3571D3494010C562F0E657B8BFF1A49804C8D24940784D8CBCDF7CB8BFA6BBEF71CCD24940BE354FEDE2E6B8BFF67A1004A4D2494042DE1E491A6DB9BFC8B6D13DEED24940D440804F6DC8BABF4712FC5AB6D2494001FCBB7B6D01BCBFCACC718E9ED249408E191878BFE6BBBF85C60393CED24940B546CE3DB681BABF7EC2CCDDF9D2494004C786C4CF10BBBF0B01276D4ED349405617133FF2CFBBBF700FB65249D34940D04F43BAC8BFBBBFCF165CA861D349407809947B3AD0BCBF38EE3E1C37D34940534EC60C5B0FBDBF5D9291F1B0D3494051DD0B9EAB02C0BFED50547864D349407163CE267028C0BFC78ADFDA63D44940E08B67EB3FFABEBFA6BB244E87D44940F1F75E581D09BFBF9144F1D2E0D449401D111A539A04C0BF1B1F60E0DBD4494004D241761E08C0BF73E9AB1AEED44940DF5B824AA439C0BF659FF2FDE7D449407202983FE548C0BFA3917BC458D549408297F57906E8C0BF2808AFF35BD5494012C895F136C0C1BFC6B5F80992D54940B54E4B7A802DC2BF493090C574D54940A3C22ABB7BA1C2BFF2386E1A66D549408C961EC05883C3BF4D2D3B775DD54940C9B6A4E0C755C3BF8DE87C0807D649406D870E0C7503C3BFA3251CED0CD64940E89EA427300EC3BF85AB56B529D749402416BBABBCC2C3BF19CD38783ED7494058010D38B1EDC4BF6BB06652CCD74940BFEB9F86D1ECC4BFC527FE5714D84940F4A1E925A94DC4BFD8F1BFA4FCD749403A22BAD49191C3BF419C2446C8D749403816CA0F931EC3BFB7CB72D2BED74940EDA8649D0381C2BFF3F9E64DDAD749409765FBFFBCF2C1BF3BCD121C07D84940ABCEFF6D1277C1BFF0C032C321D84940D745374225FCC0BFAA775EA82DD849401A1B62817E2FBFBFC69392E321D84940F46D36059136BEBF7234929434D8494071D771477619BCBF45ABEE6987D849400EC9347A5714BBBFD75E90618FD849409E0A7F63CE30B9BFB1D6CDC56FD84940B212371FBE46B8BF975E542055D849409A6DA776C179B5BF44B3F4144FD84940F5E3BF95A136B4BFB8ED09062CD84940E1610EAEB530B1BF9DC8B6218FD74940B4B3586074ADAFBFC8FB3AE56AD74940638DB8CCA460AEBF945FA36966D74940DE6D0CA6E816ABBFADBF4DD078D74940DDF6D416F73EA9BFCFCBA44D95D74940FBEB7A85772CA8BFB9FE9CD07BD7494086E15F3DFFA6A6BF20AAB0C87AD74940C2F2C8758E56A6BFAB4128BD77D74940CE3D57101D5CA6BFF3D893B76FD749406F960EDE1B88A1BFFF777E9D5AD749407AF912A18C5E9FBF99F8199E5DD749405CC3B3A0043F94BF091FECF82FD749403E9E1D9CD16888BF7645150127D74940', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001231', 'Exeter', '0106000020E610000002000000010300000001000000040000005A53181008FE0BC09A6AF406205949407E97854047FF0BC0C9785C5F3A5949404EDAF2C997FC0BC02F1D83982B5949405A53181008FE0BC09A6AF40620594940010300000001000000C10000007218EA9B6FFC0BC0B55B66D92C5949402053D7E3F2000CC0A7A542044B594940463A0EE449020CC0416F1F6B6259494030BB3B5CA3020CC0709584F38D594940BE37D3B709090CC0CEBCCFBAF15949401357E119A5090CC06B79902CEB594940A328BEE77E060CC01C23779AC6594940380DAB6C91080CC07D5C7007C3594940A8CA224E050A0CC01F0ECD30B2594940E6D5799E170B0CC0556A7789B959494049280836BF0B0CC01B565B48B6594940EC4DF0172A0E0CC0EEF8A90DBF5949406C304CDFB20E0CC004DAB43EC25949404A252A0BDA0D0CC0305E3053D8594940CB69A484FB0E0CC0C5C26C05C2594940EC4DF0172A0E0CC0EEF8A90DBF59494087ABD4D8430C0CC01BA003B5B359494049280836BF0B0CC01B565B48B65949408834D5230B0A0CC0E112381AB05949408F579BD374080CC040AFFFFCC159494071A9C16A9E060CC0B29F958FC3594940BB08F5299E040CC08E008EDB97594940E03550E6A4050CC06CE9B4D7A15949402603FEE461050CC0FBF8136594594940D7D6C28FBF0E0CC064228F3E8D59494067DC5FE7E4120CC00224C474A0594940C8E26B786D150CC095A3F300C659494082C05BEDBE160CC04ED7D18FBE594940DE34EEDCCD130CC0D0712C569B5949401604A335AE100CC09A517F4E8859494056D5DAF206040CC0F1201C258659494046E382F969030CC0052C0FCD5E594940D1DB46534F010CC04C2160293359494007B0E8CD06050CC07CE8F86848594940B9A85C5A48010CC064F6B1973259494050553FD7A4FF0BC0AAB2B6F81259494082BDA66C69010CC03759167F0459494025A945C320020CC0321DE4900D5949407264385474010CC0E7D97722045949408138294E61040CC0E01AD802E0584940115BFA10A0030CC0B8BDE8A2DF5849400EA9B4C0AD060CC024B1BD3CA1584940A8AA80CD7B070CC018FD3EE1A7584940D9AE0412140B0CC0C4D246538058494041600E7EE30D0CC0A04CA67D76584940ACD6BB7543160CC0F1C174F39A584940C5212D9459240CC0C8C7BF5EA3584940BF542AA075260CC05675EE51B5584940096C7824CD2A0CC058EE88B4A4584940DCF0C81ABB2E0CC0A4379B5CA6584940D444AF6362330CC0FA121FD7C958494066095888F3360CC096221543005949402BE5D3E42F3C0CC07348433DAE584940BEB2E96709450CC02AA64D51AB584940C44AD9601D4E0CC0F9913791C158494043F663460F500CC022CA3889B1584940071727DDAB580CC02984396693594940D6FB45777E5F0CC01C01122BF159494003470E8A0B690CC03DE6359E305A4940E4DD4D59FB7A0CC081DFA328635A49409F122E4AA8800CC06B6F994A7D5A4940D65353BA00810CC0244375CBC15A494072B2710558850CC0A98D66C2F45A49401A46BB04DD840CC09994989DFD5A4940D50D814F94870CC0FD43512C095B49404E7BA140FF880CC03D54CD346A5B49403C488D0CB28C0CC056F8A55E725B4940F06C0103A78C0CC0E3751CCE885B49409E3B60BB41890CC02B8CDD7D905B49401E11203CA2870CC0E7B0947F875B49405C225629A7860CC085E9BA7E925B4940200CA4094A860CC084BB9D20BD5B4940C553B54F5B840CC0E8636596E25B494027929DB071840CC0B90EEF1D275C49409165475161850CC0ACF2F8282C5C4940ED063DDDFF850CC0A6E8676E725C4940202AA5C955870CC03D1B00AC8E5C4940ED4C67269F860CC0E276B629A55C494025E71B6691880CC057659E1CBB5C4940969D85A1F58A0CC00AEC9BF9F25C4940825C41831B8C0CC0A3145D2CF55C4940B38789EAA38F0CC0AF10F41F295D49409C782705FB880CC0F4D82FC11C5D4940D36ED681A58A0CC0804C1B3F3A5D494026FD536B9C870CC0E986F1CD535D494028579D569C860CC0B0516EF9575D4940C9263D2570810CC0F73EEE96465D4940F0BCB2F3B37E0CC0260F0E91645D49408B3D50647F7C0CC0F7508002685D4940D5180322107C0CC0B664898A725D4940EFF67E0A6A7E0CC0F2F76CEBB15D494040D6F1F074800CC014E2534CBB5D494004FA758D1E810CC089AE3B94DE5D4940197D3CB7D5820CC0351291B9E95D4940F1CDE48D07840CC0B08D2FF5265E4940DFB15E8FFA890CC02AACDFA8615E4940488FD2ED30870CC0B3FD70F1AB5E4940AA381A6084840CC0F1D456B4B75E494034BA93D47D880CC0C239077ECD5E4940B394430B5D8D0CC017803C21D75E494066DB9641AC8E0CC0BCF0D8465C5F494083B713FA508A0CC05D25E3996D5F49400EFB0E1C8A840CC0782F016C695F494021FF123CD27A0CC02CD15912525F4940DE9F4B0C8B720CC00D8BAAC9565F49405EDB4AA0766C0CC0CC9832546B5F4940DADA4B518C6A0CC05D104A73605F4940EFF114ADC2690CC0F6BE2DCFB25F4940B57988851A670CC010C20182D85F49405018F2D880640CC030396858C55F494055E7D565BE620CC01DE267A1A75F4940487391F832610CC093A5F66ADE5F49402067A4013F5A0CC0C080560B1E604940704BABA03A560CC0E1E329C63A6049400BDD9412884C0CC0DBE74E8D666049401F5A106F46470CC04BE20E14C56049406B344917783D0CC036BA2B2BE46049407C2573BA5F3B0CC089809413D560494052B7DD405E3B0CC0202684CFB5604940DC5F3D4FC4360CC0482C094FA9604940285CD5312A350CC04B3588B1B3604940AAC7960CF5350CC03DDE29E7FA604940373EF92EFB340CC0DE157B1E10614940DEE5539E10340CC01755D23912614940266B73BD49310CC0670002F0FA604940EA2B5C86102E0CC0691423D7F76049405766FC2FB61F0CC0CB1FC91A2D614940AEF116ACFF1D0CC01A1C9CC738614940EB0270C8871C0CC0592CA68361614940B43006B5161A0CC0ACF4265C77614940213F037D26190CC0CD5A9AB46B61494042AD04FB310F0CC06A99E28E5B6149409852A7F60C0E0CC0599442B21B6149404BAA9928C00A0CC07EA4C048EB604940082E210E6F090CC0AF101845F0604940E4DBE33364040CC02A15DF19A4604940178A23729CFD0BC0F98141908C604940FE60ABC7A7F90BC00C2CF44A906049408CF2ABFE95F10BC015E0384684604940C6EBAAD8E2EA0BC0644E4E5D60604940E13B1CB802E30BC0A86208BF1A604940CED24783E5E40BC0C026C746FE5F49405AE7C2BAC5E40BC066020EDCDF5F4940E10235898AE10BC0A62FFE2FCE5F49402DE42F3D3ADF0BC015536968995F4940CD61707B3CDD0BC038F757F69A5F494025A6F35CE2DB0BC037B12928725F49400A4A8B54F9DC0BC01DB27849715F494020C1F5D44EDD0BC0B72F3D6A3F5F49407707B8E2BDDF0BC02AED4E893E5F4940C33C93FFE6DF0BC0626923CA1B5F4940BCBC629A94DE0BC09BAA455E155F49404B4FE86E24E30BC04904E5F2DC5E4940C15A97A3CDE20BC0DD6F1D52C45E49408F1A38D575E30BC0E331CBE9BE5E4940FD8601B3A1E40BC020415E70CE5E494057D80E012BF30BC0F6104DC4AF5E4940EB0FB8E0B6EF0BC0A6D349CF205E4940EB0D1A35ADEE0BC01381CECFB65D49400996413F6DE00BC05D0312BAD95D49407AB36ACD98D90BC04F8F91A4F65D4940555FDD5713DA0BC0EB1FFC378B5D494083CDACE336DD0BC0CA8850B83C5D49404194FCF1B4DC0BC01F5F62F7EE5C4940DA945D2F3AEA0BC0D0126A102A5D4940A619E6D797ED0BC076CE28CEF65C4940BCD22023FCF00BC028DE32B5DD5C4940B1AC574CBCEF0BC05464E24AD15C4940A9EA5BE458EF0BC027B0F5B2B55C4940950CF0CB55EE0BC03716ED5CBD5C494097A1865C24ED0BC089ACFA9FA85C49407F83A5C005EC0BC0FFE6D7CCAB5C49400D3C58312DEC0BC0026414FA6F5C494038354BCAD6EC0BC0A6C4B1D0715C49401C6CDB9145ED0BC0D2524AA65A5C49404407A14A1AEE0BC067F23CC05C5C4940E543ED8A44EF0BC046973FDC3B5C4940A3D02EAABAEE0BC0381CEA5F295C49407609B831D9F00BC074550DDD1F5C494010F1B5BD83F30BC0EDCE0D36DB5B494023120835C2F30BC0413F74E0A35B494093BC74D188F80BC0C1D42A88525B4940417C7FB763F70BC07E1DA70F435B4940CD2BB58D1AFD0BC0775BE68F055B4940F4C39A09F8F60BC0ABBA2448E45A494004788A80EBEB0BC033A8EB541D5B494037A4BB6C99EA0BC0C5364D7C1E5B494077B24595B8E50BC0310FC527005B4940289391EC85E00BC01B938F9BF75A4940B7FAC73AB9DF0BC02D208376FF5A4940C37E56B381E40BC06823AAB13D5A49405D2AA8AA7CEE0BC0FB9EF687DE5949407218EA9B6FFC0BC0B55B66D92C594940', 2); INSERT INTO public.area (code, name, geography, area_set_id) VALUES ('E14001248', 'Gloucester', '0103000020E610000001000000840000006066ADBFB79401C0469109F793EF4940A3E365D7C29101C0A6D68BF51CEF49401A9FF058329201C0313C3621CAEE494031581AEABE8901C0E893A19CA5EE4940296DD287B08101C07584932B70EE494085507B2F066B01C05D9665628DED494027454A2FED7201C0694F637B33ED494011EECF9EB17E01C013BE335147EC49408267D5315F8501C08EDAED9EE3EB4940D4A74382DB9801C0FD6CE68F32EB49409A260E61809A01C0C702967533EB494055458A54559A01C03A38018E22EB494038CDCC4E049E01C0BD895F8505EB4940A6663969B5AC01C07C366D2A22EA49406A7B6AAD79B301C093E893F7D9E94940F3E3702B72C901C074887C141CE949405776D36072CE01C06EB671B525E94940D1B20AF04ECC01C09F70CAEE4EE949408EC837856ECF01C015D4888059E94940739A6ECD38CD01C0ABE4D4FA8BE949402791A74889C901C009758041A6E94940BA6A1BAC52CC01C0A805FC3AB7E9494057864ACCEFC901C0B1575BABC0E949404B9591ACF5CC01C06FEC2B03CDE94940E4BD135CE5D101C0B9F5781A06EA4940850814DCF3D801C0583A4CC0C9E94940ED769116A4E701C04BBC8612A3E949407C11C76C02EC01C01ACB4F27ADE94940AF1F5C1648EF01C0B1886F04C0E94940F49B8291C2F301C009CDC80BC0E949401843AC4EE6F501C09405D0FDB6E94940391254624AF801C0DA5B0A2FBCE94940E6B91E40E8FB01C0AED5796EA2E949406A26AB8FA8FE01C061F7F6EBA0E94940EBC825324D0402C03004B0F74BE9494097D8668BE10502C0565A38CE50E949403E5CEA72B50A02C0894E553D13E949403039F56B940D02C0677F15BB25E94940BA8E1DD9531A02C04C0569CB5EE749404404D25C9B2502C0CFFD0EC0A7E74940B4BF5DAFA92602C04042CBD5ABE74940A54B13E13A2702C0EEDFC62A9BE74940F3FDDA40142A02C0E9C0DCF5A3E7494054D8259C7D2902C02430CD4DB3E749401910EEA0812D02C0B722439ED0E749405D1ECE6C1E2D02C092058BB1E4E74940E5AFF93B053802C06A9895C610E84940A37D6123FE3602C0D900F3A618E849409B7A81AC173B02C054FA795C20E8494078C76C8D304302C00FC66C0643E849400290BE884E4002C0A9A3DDBC6DE84940870D2AE3C24902C0D4B17D9EC9E849406955EF9EF95002C0691EC561F5E84940E7A916F9045102C09408FE6919E94940B2F229D90F5202C07ADCC5D92EE94940F61B987A5B5B02C055CC4B0E6BE94940E3D55056146002C0EF6D643E7AE94940F267795A026202C0CF6B40A38BE94940AB5369E9866402C091C8E5988DE9494028F547A8A66802C0C71A8F6ED3E9494063EC84740B6902C0F1161DF024EA4940CD1CEF79AA6802C0C6D8202137EA494005D1C71D786602C05BAFD43644EA4940BD7309E52B6902C0387F25B177EA4940E3A16209706602C077B8E27987EA49404864CA215B6302C0839B7E5688EA49409FEC5EF8845F02C0B6DBC8257BEA49406CEB92C9545C02C00C0503A97CEA494031A556B2FE5402C0727DBFE7A5EA4940E1316AD7F05102C083C999CBDCEA49402D3D2171C45102C07908567D49EB49401A155ECB875002C092AE464B66EB49401F43DA95894702C04492355BAEEB4940A0C052B9ED3D02C02FAC93B9E1EB49402AE38C3A933E02C08260A243E5EB4940B11764BB363C02C016B670B6F9EB494024274051A63A02C004FA010B1FEC494074B298A8873C02C0E40C3DA686EC49405D608928B44C02C014164D92D6ED49408F71E113664D02C01AF26EEAF1ED49402ECF7ADBC34C02C012B72EDC07EE4940AC99091B7C4802C00FE6BB9A23EE4940E4D04138473902C0F9CFEF0741EE49402F4E3AAD3B3302C00F3EA8F25AEE4940C50DCFE63E2E02C014DBB6909AEE49406E5E83D4342B02C067E0B4EAF1EE49402C355327E52402C0C910DC6823EF4940F8B9A332932002C0AD871BCB1FEF4940777A314F081E02C0D070DA9811EF4940E6F87D692F1A02C0DA1DAC6DB4EE4940302C494BCF1602C0A69D79FE83EE494005B9F6CF801502C000A19AA04FEE4940D3B4A350121402C0F47FD3DC65EE49405F5AF7CC2A1902C051F9EA39B2EE4940CCC235B9521C02C0262F964804EF494062ABA4CC341E02C007C113601DEF49401AF02B23B12302C051B7F94B2FEF4940DF70AB79B22702C0C5A398A123EF49407F335EC9A22802C0A7A44BD07FEF4940CC10F452662402C0DAD58B12CFEF4940DE16A323F02302C0C4A072CD05F049404C054741462002C0FA4F99B409F049407CDBC288BA1F02C0292C0C4E16F04940F1D4C2D34F1502C06F81866012F04940E9BEF147321102C048E8923417F04940A89049651E1102C0B2B316F51EF04940D9C6D43D490802C09FDABAA938F04940793496D3E50002C018D118DD70F0494038A6A76AD9F901C012311B9DBDF04940BB67A2B4D5F901C096278D92C7F0494018AD365D6EF901C07ED9AB75C1F04940C31E809876F301C09626CA4EE6F04940FBFD8A47B0E901C02DD332FFFFF049400622E1597FE701C06311CD800EF14940D9EF40BC30EA01C017963A40D0F04940B4E8BDCAF9EC01C0F0C8AA4C1FF0494088447DDFBFE301C0D4C26E7D13F04940BDB0BD5F0DDB01C0E7340501F3EF4940397D64D4E5D301C0E2ABFA24C4EF4940483892D3F4CD01C03803D5C088EF4940AEC1E61A57CC01C061B3F8DD69EF4940AE973D919BD101C02F0D122E38EF4940533285D78BCF01C0045621762EEF4940F11BAF7DE3D001C09F14294FDCEE49406857C59008D401C05889E72594EE4940DDAA635D59D801C0F0CF5A9C53EE4940A1A1DB2174D001C0FFC43C1E30EE4940E3EF34BB7FC801C0B3FB7C9DF8ED4940044A61017ABF01C08026957B65EE49403021DAE697B401C0D05F4960C6EE4940B6FB14D9D1A901C0E58684CA16EF49406066ADBFB79401C0469109F793EF4940', 2); diff --git a/tests/resources/stats.csv b/tests/resources/stats.csv index d5e59afc..b6fdf05f 100644 --- a/tests/resources/stats.csv +++ b/tests/resources/stats.csv @@ -1,5 +1,7 @@ -Code,Name,Electorate -E14001070,Ashton-under-Lyne,"71,002" -E14001071,Aylesbury,"79 169" -E14001088,Bexhill and Battle,72198 -W07000089,Cardiff East,72876 \ No newline at end of file +Code,Name,Electorate,Lab %,Con %,Segment,Segment name +E14001071,Aylesbury,"79 169",11,15,1,Normal +E14001088,Bexhill and Battle,72199,12,3,2,Weird +E14001092,Birmingham Edgbaston,"71,002",22,13,, +E14001202,Dover and Deal,,10,5,1,Normal +E14001225,Enfield,72481,13,25,3,Unusual +W07000089,Cardiff East,72876,14,7,4,Strange \ No newline at end of file diff --git a/tests/setup.ts b/tests/setup.ts index f74f69be..ff5c07d7 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -16,6 +16,9 @@ export async function setup() { const client = await pool.connect(); try { await client.query(sampleAreasSql, []); + await client.query( + "REFRESH MATERIALIZED VIEW CONCURRENTLY public.area_search", + ); } finally { client.release(); } From 0845a5f28c67a7a13bdc7adad2259ebd6f7d3be5 Mon Sep 17 00:00:00 2001 From: joaquimds Date: Thu, 26 Feb 2026 19:12:45 +0100 Subject: [PATCH 5/6] Update src/app/map/[id]/components/Legend.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/app/map/[id]/components/Legend.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/app/map/[id]/components/Legend.tsx b/src/app/map/[id]/components/Legend.tsx index 6f1dfb0d..3f75e179 100644 --- a/src/app/map/[id]/components/Legend.tsx +++ b/src/app/map/[id]/components/Legend.tsx @@ -309,9 +309,14 @@ export default function Legend() { if (hasValueLabels) { values = Object.keys(valueLabels) - .map(Number) - .filter((v) => v >= colorScheme.minValue && v <= colorScheme.maxValue) - .toSorted(); + .map((key) => Number(key)) + .filter( + (v) => + Number.isFinite(v) && + v >= colorScheme.minValue && + v <= colorScheme.maxValue, + ) + .toSorted((a, b) => a - b); numTicks = values.length; denom = Math.max(numTicks - 1, 1); } From da79fe881c7e6e8722eb158f88ea752145339a09 Mon Sep 17 00:00:00 2001 From: Joaquim d'Souza Date: Thu, 26 Feb 2026 19:15:44 +0100 Subject: [PATCH 6/6] fix: safer handling of non-numeric value labels in legend --- src/app/map/[id]/components/Legend.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/map/[id]/components/Legend.tsx b/src/app/map/[id]/components/Legend.tsx index 3f75e179..34372bf8 100644 --- a/src/app/map/[id]/components/Legend.tsx +++ b/src/app/map/[id]/components/Legend.tsx @@ -308,7 +308,7 @@ export default function Legend() { const hasValueLabels = Object.keys(valueLabels).length > 0; if (hasValueLabels) { - values = Object.keys(valueLabels) + const numericKeys = Object.keys(valueLabels) .map((key) => Number(key)) .filter( (v) => @@ -317,8 +317,11 @@ export default function Legend() { v <= colorScheme.maxValue, ) .toSorted((a, b) => a - b); - numTicks = values.length; - denom = Math.max(numTicks - 1, 1); + if (numericKeys.length) { + values = numericKeys; + numTicks = values.length; + denom = Math.max(numTicks - 1, 1); + } } return (