-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Aggregate and display unknown values (#370)
- Loading branch information
1 parent
aa40cef
commit 3c41224
Showing
34 changed files
with
973 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// 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 { render, screen } from "@testing-library/react"; | ||
import React from "react"; | ||
import VizNotes from "./VizNotes"; | ||
|
||
test("all notes", () => { | ||
render( | ||
<VizNotes | ||
smallData | ||
unknowns={{ gender: 1, ageBucket: 0, raceOrEthnicity: 0 }} | ||
/> | ||
); | ||
|
||
const items = screen.getAllByRole("listitem"); | ||
expect(items[0]).toHaveTextContent("counts are especially low"); | ||
expect(items[1]).toHaveTextContent( | ||
"age, gender, or race/ethnicity is not reported" | ||
); | ||
}); | ||
|
||
test("small data only", () => { | ||
render(<VizNotes smallData />); | ||
|
||
expect(screen.getByRole("listitem")).toHaveTextContent( | ||
"counts are especially low" | ||
); | ||
}); | ||
|
||
test("unknowns only", () => { | ||
render( | ||
<VizNotes unknowns={{ gender: 1, ageBucket: 0, raceOrEthnicity: 0 }} /> | ||
); | ||
|
||
expect(screen.getByRole("listitem")).toHaveTextContent( | ||
"age, gender, or race/ethnicity is not reported" | ||
); | ||
}); | ||
|
||
test("format single unknowns", () => { | ||
render( | ||
<VizNotes unknowns={{ gender: 1, ageBucket: 4, raceOrEthnicity: 0 }} /> | ||
); | ||
|
||
expect(screen.getByRole("listitem")).toHaveTextContent( | ||
"age group (4), gender (1)." | ||
); | ||
}); | ||
|
||
test("format unknowns by date", () => { | ||
render( | ||
<VizNotes | ||
unknowns={[ | ||
{ | ||
date: new Date(2021, 0), | ||
unknowns: { gender: 1, ageBucket: 0, raceOrEthnicity: 1 }, | ||
}, | ||
{ | ||
date: new Date(2021, 1), | ||
unknowns: { gender: 2, ageBucket: 0, raceOrEthnicity: 0 }, | ||
}, | ||
]} | ||
/> | ||
); | ||
|
||
expect(screen.getByRole("listitem")).toHaveTextContent( | ||
"gender (1), race or ethnicity (1) for Jan 1 2021; gender (2) for Feb 1 2021." | ||
); | ||
}); | ||
|
||
test("format unknowns by cohort", () => { | ||
render( | ||
<VizNotes | ||
unknowns={[ | ||
{ | ||
cohort: 2012, | ||
unknowns: { gender: 1, ageBucket: 2, raceOrEthnicity: 3 }, | ||
}, | ||
]} | ||
/> | ||
); | ||
|
||
expect(screen.getByRole("listitem")).toHaveTextContent( | ||
"age group (2), gender (1), race or ethnicity (3) for the 2012 cohort." | ||
); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// 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 { format } from "date-fns"; | ||
import React from "react"; | ||
import { ValuesType } from "utility-types"; | ||
import { UnknownCounts, Unknowns } from "../contentModels/types"; | ||
import { getDemographicViewLabel } from "../demographics"; | ||
import { DemographicFieldKeyList } from "../metricsApi"; | ||
import Notes from "../Notes"; | ||
import { formatAsNumber } from "../utils"; | ||
|
||
function formatUnknownCounts(unknowns: UnknownCounts) { | ||
const parts: string[] = []; | ||
|
||
DemographicFieldKeyList.forEach((key) => { | ||
const value = unknowns[key]; | ||
if (!value) return; | ||
|
||
parts.push( | ||
`${getDemographicViewLabel(key).toLowerCase()} (${formatAsNumber(value)})` | ||
); | ||
}); | ||
|
||
return parts.join(", "); | ||
} | ||
|
||
function formatUnknowns(unknowns: Unknowns) { | ||
if (Array.isArray(unknowns)) { | ||
// Typescript freaks out over unions of array types, | ||
// but this assertion is functionally identical | ||
return (unknowns as ValuesType<typeof unknowns>[]) | ||
.map((entry) => { | ||
const formattedCounts = formatUnknownCounts(entry.unknowns); | ||
let label: string; | ||
|
||
if ("date" in entry) { | ||
label = format(entry.date, "MMM d y"); | ||
} else { | ||
label = `the ${entry.cohort} cohort`; | ||
} | ||
|
||
return `${formattedCounts} for ${label}`; | ||
}) | ||
.join("; "); | ||
} | ||
return formatUnknownCounts(unknowns); | ||
} | ||
|
||
type VizNotesProps = { | ||
smallData?: boolean; | ||
unknowns?: Unknowns; | ||
}; | ||
|
||
const VizNotes: React.FC<VizNotesProps> = ({ smallData, unknowns }) => { | ||
return ( | ||
<Notes> | ||
{smallData && ( | ||
<> | ||
Please always take note of the number of people associated with each | ||
proportion presented here; in cases where the counts are especially | ||
low, the proportion may not be statistically significant and therefore | ||
not indicative of long-term trends. | ||
</> | ||
)} | ||
{unknowns && ( | ||
<> | ||
This data includes some individuals for whom age, gender, or | ||
race/ethnicity is not reported. These individuals count toward the | ||
total but are excluded from demographic breakdown views. Unknown | ||
values comprise: {formatUnknowns(unknowns)}. | ||
</> | ||
)} | ||
</Notes> | ||
); | ||
}; | ||
|
||
export default VizNotes; |
Oops, something went wrong.