UNPKG

@seasketch/geoprocessing

Version:

Geoprocessing and reporting framework for SeaSketch 2.0

640 lines (604 loc) • 13.9 kB
import { flattenByGroupAllClass, flattenByGroupSketchAllClass, flattenBySketchAllClass, nestMetrics, createMetric, packMetrics, unpackMetrics, isMetric, isMetricArray, rekeyMetrics, MetricProperties, } from "./helpers.js"; import { NullSketch, NullSketchCollection, Metric } from "../types/index.js"; import { hasOwnProperty, toPercentMetric, toSketchPropertiesArray, } from "../../client-core.js"; import deepEqual from "fast-deep-equal"; const metricName = "metric1"; const CLASSES = [ { classId: "class1", display: "class1", }, { classId: "class2", display: "class2", }, ]; const PRECALC_TOTALS: Metric[] = [ createMetric({ metricId: metricName, value: 100, }), createMetric({ classId: "class1", metricId: metricName, value: 100, }), createMetric({ classId: "class2", metricId: metricName, value: 100, }), ]; const collectionId = "CCCC"; const sketchAId = "AAAA"; const sketchBId = "BBBB"; const sketchDId = "DDDD"; const sketches: NullSketch[] = [ { type: "Feature", properties: { id: sketchAId, name: "SketchA", updatedAt: "2021-11-20T00:00:34.365Z", createdAt: "2021-10-28T13:49:22.785Z", sketchClassId: "AAAA", isCollection: false, userAttributes: [], }, id: "1", geometry: null, }, { type: "Feature", properties: { id: sketchBId, name: "SketchB", updatedAt: "2021-11-19T23:59:16.315Z", createdAt: "2021-11-19T23:34:00.777Z", sketchClassId: "BBBB", isCollection: false, userAttributes: [], }, id: "2", geometry: null, }, { type: "Feature", properties: { id: sketchDId, name: "SketchD", updatedAt: "2021-11-19T23:59:16.315Z", createdAt: "2021-11-19T23:34:00.777Z", sketchClassId: "DDDD", isCollection: false, userAttributes: [], }, id: "4", geometry: null, }, ]; const collection: NullSketchCollection = { type: "FeatureCollection", properties: { id: collectionId, name: "Collection 1", updatedAt: "2021-11-20T00:00:34.269Z", createdAt: "2021-11-19T23:34:12.889Z", sketchClassId: "615b65a2aac8c8285d50d9f3", isCollection: true, userAttributes: [], }, features: sketches, }; const metrics: Metric[] = [ createMetric({ metricId: metricName, sketchId: sketchAId, value: 10, classId: "class1", }), createMetric({ metricId: metricName, sketchId: sketchBId, value: 20, classId: "class1", }), createMetric({ metricId: metricName, sketchId: sketchDId, value: 40, classId: "class1", }), createMetric({ metricId: metricName, sketchId: collectionId, value: 30, classId: "class1", }), createMetric({ metricId: metricName, sketchId: sketchAId, value: 40, classId: "class2", }), createMetric({ metricId: metricName, sketchId: sketchBId, value: 50, classId: "class2", }), createMetric({ metricId: metricName, sketchId: sketchDId, value: 60, classId: "class2", }), createMetric({ metricId: metricName, sketchId: collectionId, value: 90, classId: "class2", }), ]; const groupMetrics: Metric[] = [ // collection createMetric({ groupId: "group1", classId: "class1", metricId: metricName, sketchId: collectionId, value: 25, }), createMetric({ groupId: "group2", classId: "class1", metricId: metricName, sketchId: collectionId, value: 30, }), createMetric({ groupId: "group3", classId: "class1", metricId: metricName, sketchId: collectionId, value: 35, }), createMetric({ groupId: "group1", classId: "class2", metricId: metricName, sketchId: collectionId, value: 25, }), createMetric({ groupId: "group2", classId: "class2", metricId: metricName, sketchId: collectionId, value: 30, }), createMetric({ groupId: "group3", classId: "class2", metricId: metricName, sketchId: collectionId, value: 35, }), // sketch class 1 createMetric({ groupId: "group1", classId: "class1", metricId: metricName, sketchId: sketchAId, value: 15, }), createMetric({ groupId: "group1", classId: "class1", metricId: metricName, sketchId: sketchBId, value: 20, }), createMetric({ groupId: "group2", classId: "class1", metricId: metricName, sketchId: sketchDId, value: 30, }), // sketch class 2 createMetric({ groupId: "group1", classId: "class2", metricId: metricName, sketchId: sketchAId, value: 15, }), createMetric({ groupId: "group1", classId: "class2", metricId: metricName, sketchId: sketchBId, value: 20, }), createMetric({ groupId: "group2", classId: "class2", metricId: metricName, sketchId: sketchDId, value: 30, }), // sketch class 3 createMetric({ groupId: "group3", classId: "class2", metricId: metricName, sketchId: sketchAId, value: 20, }), createMetric({ groupId: "group3", classId: "class2", metricId: metricName, sketchId: sketchDId, value: 15, }), ]; describe("Metric checks", () => { test("isMetric", async () => { const trueMetric = metrics[0]; trueMetric.extra = { big: "fish" }; expect(isMetric(trueMetric)).toBe(true); }); test("isMetric false", async () => { const falseMetric = { value: 15 }; expect(isMetric(falseMetric)).toBe(false); const falseMetric2 = createMetric({ value: 15 }); //@ts-expect-error not allowed to assign undefined but we will force it falseMetric2.groupId = undefined; expect(isMetric(falseMetric2)).toBe(false); }); test("isMetricArray", async () => { expect(isMetricArray(metrics)).toBe(true); }); }); test("rekeyMetrics", async () => { const metrics = [createMetric({ value: 10 })]; const rekeyed = rekeyMetrics(metrics); expect(rekeyed.length).toBe(1); const keys = Object.keys(rekeyed[0]); expect(keys.length).toBeLessThanOrEqual(MetricProperties.length); // Add test of correct key order }); describe("MetricPack", () => { test("Can pack and unpack metrics", async () => { const metrics: Metric[] = [ createMetric({ metricId: metricName, sketchId: sketchAId, value: 10, classId: "class1", }), createMetric({ metricId: metricName, sketchId: sketchBId, value: 20, classId: "class1", }), ]; const packed = packMetrics(metrics); expect(hasOwnProperty(packed, "dimensions")).toBe(true); expect(hasOwnProperty(packed, "data")).toBe(true); expect(packed.dimensions).toHaveLength(6); expect(packed.data).toHaveLength(2); expect(packed.data[0]).toHaveLength(6); const unpacked = unpackMetrics(packed); expect(unpacked).toHaveLength(metrics.length); }); test("Can pack and unpack metrics with extra", async () => { const metrics = [ createMetric({ value: 15, extra: { big: "fish" }, }), ]; const packed = packMetrics(metrics); expect(hasOwnProperty(packed, "dimensions")).toBe(true); expect(hasOwnProperty(packed, "data")).toBe(true); expect(packed.dimensions).toHaveLength(7); expect(packed.data).toHaveLength(1); expect(packed.data[0]).toHaveLength(7); const unpacked = unpackMetrics(packed); expect(unpacked).toHaveLength(metrics.length); expect(unpacked[0].value).toEqual(15); expect(unpacked[0]?.extra?.big).toEqual("fish"); }); }); test("MetricPack", async () => { const metrics: Metric[] = [ { metricId: "ousPeopleCount", sketchId: "16624", classId: "saomiguel", groupId: null, geographyId: null, value: 102, }, ]; const packed = packMetrics(metrics); expect(hasOwnProperty(packed, "dimensions")).toBe(true); expect(hasOwnProperty(packed, "data")).toBe(true); expect(packed.dimensions).toHaveLength(6); expect(packed.data).toHaveLength(1); expect(packed.data[0]).toHaveLength(6); const unpacked = unpackMetrics(packed); // console.log("unpacked", JSON.stringify(unpacked, null, 2)); expect(deepEqual(metrics, unpacked)).toBe(true); }); describe("flattenSketchAllClass", () => { test("flattenSketchAllClass - basic", async () => { const answer = [ { class1: 10, class2: 40, sketchId: sketchAId, sketchName: "SketchA", }, { class1: 20, class2: 50, sketchId: sketchBId, sketchName: "SketchB", }, { class1: 40, class2: 60, sketchId: "DDDD", sketchName: "SketchD", }, ]; const rows = flattenBySketchAllClass( metrics, CLASSES, toSketchPropertiesArray(sketches), ); expect(rows).toEqual(answer); }); test("toPercentMetric - basic", async () => { const percMetrics = toPercentMetric( [ createMetric({ groupId: "group1", classId: "nearshore", metricId: metricName, sketchId: collectionId, value: 25, }), ], [ createMetric({ groupId: "group1", classId: "nearshore", metricId: metricName, sketchId: collectionId, value: 100, }), ], { debug: false, }, ); for (const m of percMetrics) { expect(m.value).toEqual(0.25); expect(m.classId).toEqual("nearshore"); } }); test("toPercentMetric - zero denominator value produces NaN", async () => { const percMetrics = toPercentMetric( [ createMetric({ groupId: "group1", classId: "nearshore", metricId: metricName, sketchId: collectionId, value: 25, }), ], [ createMetric({ groupId: "group1", classId: "nearshore", metricId: metricName, sketchId: collectionId, value: 0, }), ], { debug: false, }, ); for (const m of percMetrics) { expect(m.value).toEqual(Number.NaN); expect(m.classId).toEqual("nearshore"); } }); test("toPercentMetric - set new percent metric ID", async () => { const percMetricName = `${metricName}Perc`; const percMetrics = toPercentMetric(groupMetrics, PRECALC_TOTALS, { metricIdOverride: percMetricName, }); for (const m of percMetrics) expect(m.metricId).toEqual(percMetricName); }); test("toPercentMetric - set alternative idProperty", async () => { const percMetrics = toPercentMetric( [ createMetric({ groupId: "group1", geographyId: "nearshore", metricId: metricName, sketchId: collectionId, value: 25, }), ], [ createMetric({ groupId: "group1", geographyId: "nearshore", metricId: metricName, sketchId: collectionId, value: 100, }), ], { idProperty: "geographyId", }, ); for (const m of percMetrics) { expect(m.value).toEqual(0.25); expect(m.geographyId).toEqual("nearshore"); } }); test("flattenByGroup - single class", async () => { const rows = flattenByGroupAllClass( collection, groupMetrics, PRECALC_TOTALS, ); const answer = [ { groupId: "group1", percValue: 0.5, value: 50, class1: 0.25, numSketches: 2, class2: 0.25, }, { groupId: "group2", percValue: 0.6, value: 60, class1: 0.3, numSketches: 1, class2: 0.3, }, { groupId: "group3", percValue: 0.7, value: 70, class1: 0.35, numSketches: 2, class2: 0.35, }, ]; expect(rows).toEqual(answer); }); }); test("flattenByGroupSketch", async () => { const rows = flattenByGroupSketchAllClass( sketches, groupMetrics, PRECALC_TOTALS, ); const answer = [ { groupId: "group1", sketchId: "AAAA", value: 30, percValue: 0.3, class1: 0.15, class2: 0.15, }, { groupId: "group1", sketchId: "BBBB", value: 40, percValue: 0.4, class1: 0.2, class2: 0.2, }, { groupId: "group2", sketchId: "DDDD", value: 60, percValue: 0.6, class1: 0.3, class2: 0.3, }, { groupId: "group3", sketchId: "AAAA", value: 20, percValue: 0.2, class2: 0.2, }, { groupId: "group3", sketchId: "DDDD", value: 15, percValue: 0.15, class2: 0.15, }, ]; expect(rows).toEqual(answer); }); test("nestMetrics", async () => { const result = nestMetrics( groupMetrics.filter((m) => m.sketchId === "AAAA"), ["sketchId", "classId", "groupId"], ); const answer = { AAAA: { class1: { group1: [ { groupId: "group1", classId: "class1", metricId: "metric1", sketchId: "AAAA", geographyId: null, value: 15, }, ], }, class2: { group1: [ { groupId: "group1", classId: "class2", metricId: "metric1", sketchId: "AAAA", geographyId: null, value: 15, }, ], group3: [ { groupId: "group3", classId: "class2", metricId: "metric1", sketchId: "AAAA", geographyId: null, value: 20, }, ], }, }, }; expect(result).toEqual(answer); });