Skip to content

Commit

Permalink
Merge 28a5b58 into 9571669
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Jan 15, 2021
2 parents 9571669 + 28a5b58 commit 8a06772
Show file tree
Hide file tree
Showing 26 changed files with 5,519 additions and 89,138 deletions.
4 changes: 2 additions & 2 deletions spotlight-client/src/ErrorMessage/ErrorMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ import { FallbackProps } from "react-error-boundary";

const ErrorMessage: React.FC<FallbackProps> = ({ error }) => {
return (
<article>
<div>
<h1>An error has occurred.</h1>
<p>{error?.message}</p>
</article>
</div>
);
};

Expand Down
35 changes: 35 additions & 0 deletions spotlight-client/src/MetricVizMapper/MetricVizMapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import React from "react";
import Metric from "../contentModels/Metric";
import HistoricalPopulationBreakdownMetric from "../contentModels/HistoricalPopulationBreakdownMetric";
import VizHistoricalPopulationBreakdown from "../VizHistoricalPopulationBreakdown";
import { MetricRecord } from "../contentModels/types";

type MetricVizMapperProps = {
metric: Metric<MetricRecord>;
};

const MetricVizMapper: React.FC<MetricVizMapperProps> = ({ metric }) => {
if (metric instanceof HistoricalPopulationBreakdownMetric) {
return <VizHistoricalPopulationBreakdown metric={metric} />;
}
return <h3>Placeholder for {metric.name}</h3>;
};

export default MetricVizMapper;
18 changes: 18 additions & 0 deletions spotlight-client/src/MetricVizMapper/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

export { default } from "./MetricVizMapper";
9 changes: 5 additions & 4 deletions spotlight-client/src/SystemNarrativePage/Section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import React from "react";
import Sticker from "react-stickyfill";
import styled from "styled-components/macro";
import { NAV_BAR_HEIGHT } from "../constants";
import Metric from "../contentModels/Metric";
import { SystemNarrativeSection } from "../contentModels/SystemNarrative";
import { AnyMetric } from "../contentModels/types";
import { MetricRecord } from "../contentModels/types";
import MetricVizMapper from "../MetricVizMapper";
import { colors, typefaces } from "../UiLibrary";
import { X_PADDING } from "./constants";

Expand Down Expand Up @@ -60,15 +62,14 @@ const SectionBody = styled.div`

const VizContainer = styled.div`
margin-left: ${rem(176)};
height: 125vh;
flex: 1 1 auto;
padding: ${rem(240)} 0;
`;

const SectionViz: React.FC<{ metric: AnyMetric }> = ({ metric }) => {
const SectionViz: React.FC<{ metric: Metric<MetricRecord> }> = ({ metric }) => {
return (
<VizContainer>
<h3>Placeholder for {metric.name}</h3>
<MetricVizMapper metric={metric} />
</VizContainer>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import React from "react";
import { HistoricalPopulationBreakdownRecord } from "../metricsApi";

type VizHistoricalPopulationBreakdownProps = {
data: HistoricalPopulationBreakdownRecord[];
};

const VizHistoricalPopulationBreakdown: React.FC<VizHistoricalPopulationBreakdownProps> = ({
data,
}) => {
// Just a proof-of-concept on data handling for now
return <div>{data.length} records found</div>;
};

export default VizHistoricalPopulationBreakdown;
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import { observer } from "mobx-react-lite";
import React from "react";
import { withErrorBoundary } from "react-error-boundary";
import HistoricalPopulationBreakdownMetric from "../contentModels/HistoricalPopulationBreakdownMetric";
import ErrorMessage from "../ErrorMessage";
import VizHistoricalPopulationBreakdown from "./VizHistoricalPopulationBreakdown";

type VizHistoricalPopulationBreakdownContainerProps = {
metric: HistoricalPopulationBreakdownMetric;
};

const VizHistoricalPopulationBreakdownContainer: React.FC<VizHistoricalPopulationBreakdownContainerProps> = ({
metric,
}) => {
if (metric.records)
return <VizHistoricalPopulationBreakdown data={metric.records} />;

if (metric.error) throw metric.error;

return <div>loading...</div>;
};

export default withErrorBoundary(
observer(VizHistoricalPopulationBreakdownContainer),
{ FallbackComponent: ErrorMessage }
);
18 changes: 18 additions & 0 deletions spotlight-client/src/VizHistoricalPopulationBreakdown/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

export { default } from "./VizHistoricalPopulationBreakdownContainer";
21 changes: 6 additions & 15 deletions spotlight-client/src/contentModels/Collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
// =============================================================================

import { assertNever } from "assert-never";
import {
CollectionTypeId,
MetricTypeId,
MetricTypeIdList,
} from "../contentApi/types";
import { CollectionTypeId, MetricTypeId } from "../contentApi/types";
import { MetricMapping } from "./types";

type InitOptions = {
Expand Down Expand Up @@ -56,16 +52,11 @@ function getCollectionMetricMapping({
allMetrics: MetricMapping;
idsToInclude: MetricTypeId[];
}): MetricMapping {
const filteredMetricMapping = { ...allMetrics };

// filter out all but the included keys
MetricTypeIdList.forEach((metricId) => {
if (!idsToInclude.includes(metricId)) {
delete filteredMetricMapping[metricId];
}
});

return filteredMetricMapping;
return new Map(
Array.from(allMetrics).filter(([metricId]) =>
idsToInclude.includes(metricId)
)
);
}

/**
Expand Down
36 changes: 36 additions & 0 deletions spotlight-client/src/contentModels/DemographicsByCategoryMetric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import {
DemographicsByCategoryRecord,
recordIsTotalByDimension,
} from "../metricsApi";
import Metric from "./Metric";

export default class DemographicsByCategoryMetric extends Metric<
DemographicsByCategoryRecord
> {
get records(): DemographicsByCategoryRecord[] | undefined {
let recordsToReturn = this.getOrFetchRecords();
if (!recordsToReturn) return undefined;

recordsToReturn = recordsToReturn.filter(
recordIsTotalByDimension(this.demographicView)
);
return recordsToReturn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2021 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import {
HistoricalPopulationBreakdownRecord,
recordIsTotalByDimension,
} from "../metricsApi";
import Metric from "./Metric";

export default class HistoricalPopulationBreakdownMetric extends Metric<
HistoricalPopulationBreakdownRecord
> {
get records(): HistoricalPopulationBreakdownRecord[] | undefined {
let recordsToReturn = this.getOrFetchRecords();
if (!recordsToReturn) return undefined;

recordsToReturn = recordsToReturn.filter(
recordIsTotalByDimension(this.demographicView)
);
return recordsToReturn;
}
}
50 changes: 44 additions & 6 deletions spotlight-client/src/contentModels/Metric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@
// =============================================================================

import fetchMock from "jest-fetch-mock";
import { when } from "mobx";
import { runInAction, when } from "mobx";
import { fromPromise } from "mobx-utils";
import { createMetricMapping } from "./Metric";
import retrieveContent from "../contentApi/retrieveContent";
import { MetricTypeId, MetricTypeIdList } from "../contentApi/types";
import { reactImmediately } from "../testUtils";
import createMetricMapping from "./createMetricMapping";

const testTenantId = "US_ND";
const testMetadataMapping = retrieveContent({ tenantId: testTenantId }).metrics;
Expand All @@ -34,7 +35,7 @@ const getTestMapping = () =>
let testMetricMapping: ReturnType<typeof createMetricMapping>;

function getTestMetric(testMetricId: MetricTypeId) {
const metric = testMetricMapping[testMetricId];
const metric = testMetricMapping.get(testMetricId);

if (!metric) {
throw new Error("expected instance of Metric");
Expand Down Expand Up @@ -139,11 +140,48 @@ test("fetch error state", async () => {
status: 500,
});

await expect(metric.fetch()).rejects.toThrow(
"Metrics API responded with status 500. Error message: test error message"
);
await metric.fetch();

reactImmediately(() => {
expect(metric.error?.message).toBe(
"Metrics API responded with status 500. Error message: test error message"
);
});

expect.hasAssertions();

fetchMock.resetMocks();
// return the mock to its default inactive state
fetchMock.dontMock();
});

test("demographic filter", async () => {
testMetricMapping = getTestMapping();
// one of several metric types that supports this type of filter
// (and will result in a relatively small snapshot!)
const metric = getTestMetric("ProbationRevocationsAggregate");

metric.fetch();

await when(() => metric.records !== undefined);

runInAction(() => {
metric.demographicView = "gender";
});

reactImmediately(() => expect(metric.records).toMatchSnapshot());

runInAction(() => {
metric.demographicView = "race";
});

reactImmediately(() => expect(metric.records).toMatchSnapshot());

runInAction(() => {
metric.demographicView = "age";
});

reactImmediately(() => expect(metric.records).toMatchSnapshot());

expect.hasAssertions();
});

0 comments on commit 8a06772

Please sign in to comment.