From 96de3cfae212818a3fbd60a43b0f4ce65384eebb Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Thu, 19 Jan 2023 16:54:34 -0800 Subject: [PATCH 1/9] Add daily registrations report (LG-8679) - Shows users and fully registered users --- .gitignore | 1 + src/components/daily-registrations-report.tsx | 117 ++++++++++++++++++ src/components/report-filter-controls.tsx | 35 ++++++ src/contexts/report-filter-context.tsx | 2 + src/models/daily-registrations-report-data.ts | 91 ++++++++++++++ src/routes/index.tsx | 4 + src/routes/report-route.tsx | 9 ++ typings/@observablehq/plot/index.d.ts | 1 + 8 files changed, 260 insertions(+) create mode 100644 src/components/daily-registrations-report.tsx create mode 100644 src/models/daily-registrations-report-data.ts diff --git a/.gitignore b/.gitignore index 3d841da..fe9e8da 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules _site *.local data +local diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx new file mode 100644 index 0000000..c70100d --- /dev/null +++ b/src/components/daily-registrations-report.tsx @@ -0,0 +1,117 @@ +import { VNode } from "preact"; +import { useQuery } from "preact-fetching"; +import { useRef, useState, useContext } from "preact/hooks"; +import * as Plot from "@observablehq/plot"; +import { ReportFilterContext } from "../contexts/report-filter-context"; +import useResizeListener from "../hooks/resize-listener"; +import { + DataType, + loadData, + ProcessedResult, + ProcessedRenderableData, + toRenderableData, +} from "../models/daily-registrations-report-data"; +import PlotComponent from "./plot"; +import { formatSIDropTrailingZeroes, formatWithCommas, yearMonthDayFormat } from "../formats"; +import Table, { TableData } from "./table"; + +function plot({ + data, + width, +}: { + data: ProcessedRenderableData[]; + start: Date; + finish: Date; + width?: number; +}): HTMLElement { + return Plot.plot({ + color: { + legend: true, + type: "ordinal", + tickFormat: (type: DataType): string => { + switch (type) { + case DataType.TOTAL_USERS: + case DataType.TOTAL_USERS_CUMULATIVE: + return "Total Users"; + case DataType.FULLY_REGISTERED_USERS: + case DataType.FULLY_REGISTERED_USERS_CUMULATIVE: + return "Full Registered Users"; + default: + throw new Error(`Unknown type ${type}`); + } + }, + }, + marks: [ + Plot.ruleY([0]), + Plot.line(data, { + x: "date", + y: "value", + z: "type", + stroke: "type", + }), + ], + width, + y: { + tickFormat: formatSIDropTrailingZeroes, + }, + }); +} + +/** + * Assumes that results is pre-sorted, pre-filtered + */ +function tabulate(results: ProcessedResult[]): TableData { + return { + header: ["", ...results.map(({ date }) => yearMonthDayFormat(date))], + body: [ + ["New Users", ...results.map(({ totalUsers }) => totalUsers)], + [ + "New Fully Registered Users", + ...results.map(({ fullyRegisteredUsers }) => fullyRegisteredUsers), + ], + ["Cumulative Users", ...results.map(({ totalUsersCumulative }) => totalUsersCumulative)], + [ + "Cumulative Fully Registered Users", + ...results.map(({ fullyRegisteredUsersCumulative }) => fullyRegisteredUsersCumulative), + ], + ], + }; +} + +function DailyRegistrationsReport(): VNode { + const ref = useRef(null as HTMLDivElement | null); + const [width, setWidth] = useState(undefined as number | undefined); + useResizeListener(() => setWidth(ref.current?.offsetWidth)); + const { start, finish, env, cumulative } = useContext(ReportFilterContext); + + const { data } = useQuery(`daily-registrations-${finish.valueOf()}`, () => loadData(finish, env)); + + const filteredData = toRenderableData(data || []).filter(({ type }) => { + switch (type) { + case DataType.TOTAL_USERS: + case DataType.FULLY_REGISTERED_USERS: + return !cumulative; + case DataType.TOTAL_USERS_CUMULATIVE: + case DataType.FULLY_REGISTERED_USERS_CUMULATIVE: + return !!cumulative; + default: + throw new Error(`Unknown data type ${type}`); + } + }); + + const windowedData = (data || []).filter(({ date }) => start < date && date < finish); + + return ( +
+ {data && ( + plot({ data: filteredData, start, finish, width })} + inputs={[data, start.valueOf(), finish.valueOf(), width, cumulative]} + /> + )} + {data && } + + ); +} + +export default DailyRegistrationsReport; diff --git a/src/components/report-filter-controls.tsx b/src/components/report-filter-controls.tsx index b6adfa5..5e7db44 100644 --- a/src/components/report-filter-controls.tsx +++ b/src/components/report-filter-controls.tsx @@ -23,6 +23,7 @@ enum Control { AGENCY = "agency", BY_AGENCY = "by_agency", TIME_BUCKET = "time_bucket", + CUMULATIVE = "cumulative", } interface ReportFilterControlsProps { @@ -41,6 +42,7 @@ function ReportFilterControls({ controls }: ReportFilterControlsProps): VNode { byAgency, extra, timeBucket, + cumulative, setParameters, } = useContext(ReportFilterContext); const { agencies } = useContext(AgenciesContext); @@ -68,6 +70,7 @@ function ReportFilterControls({ controls }: ReportFilterControlsProps): VNode { } return ( +
@@ -301,6 +304,37 @@ function ReportFilterControls({ controls }: ReportFilterControlsProps): VNode {
)} + {controls?.includes(Control.CUMULATIVE) && ( +
+ Cumulative +
+ + +
+
+ + +
+
+ )}
@@ -316,6 +350,7 @@ function ReportFilterControls({ controls }: ReportFilterControlsProps): VNode { {env !== DEFAULT_ENV && } {extra && } +
); } diff --git a/src/contexts/report-filter-context.tsx b/src/contexts/report-filter-context.tsx index 1c990e6..764bb5b 100644 --- a/src/contexts/report-filter-context.tsx +++ b/src/contexts/report-filter-context.tsx @@ -39,6 +39,7 @@ interface ReportFilterContextValues { byAgency: boolean; extra: boolean; timeBucket?: TimeBucket + cumulative?: boolean setParameters: (params: Record) => void; } @@ -69,6 +70,7 @@ const ReportFilterContext = createContext({ byAgency: false, extra: false, timeBucket: DEFAULT_TIME_BUCKET, + cumulative: true, } as ReportFilterContextValues); type ReportFilterContextProviderProps = Omit; diff --git a/src/models/daily-registrations-report-data.ts b/src/models/daily-registrations-report-data.ts new file mode 100644 index 0000000..1c0a385 --- /dev/null +++ b/src/models/daily-registrations-report-data.ts @@ -0,0 +1,91 @@ +import { ascending } from "d3-array"; +import { path as reportPath } from "./api-path"; + +interface Result { + /** + * ISO8601 date-only string (YYYY-MM-DD) + */ + date: string; + total_users: number; + fully_registered_users: number; +} + +interface ProcessedResult { + date: Date; + totalUsers: number; + totalUsersCumulative: number; + fullyRegisteredUsers: number; + fullyRegisteredUsersCumulative: number; +} + +enum DataType { + TOTAL_USERS, + TOTAL_USERS_CUMULATIVE, + FULLY_REGISTERED_USERS, + FULLY_REGISTERED_USERS_CUMULATIVE, +} + +interface ProcessedRenderableData { + date: Date; + value: number; + type: DataType; +} + +interface DailyRegistrationsReportData { + results: Result[]; + + /** + * ISO8601 string + */ + finish: string; +} + +function process({ results }: DailyRegistrationsReportData): ProcessedResult[] { + let totalUsersCumulative = 0; + let fullyRegisteredUsersCumulative = 0; + + return results + .sort(({ date: dateA }, { date: dateB }) => ascending(dateA, dateB)) + .map(({ date, total_users: totalUsers, fully_registered_users: fullyRegisteredUsers }) => { + totalUsersCumulative += totalUsers; + fullyRegisteredUsersCumulative += fullyRegisteredUsers; + + return { + date: new Date(date), + totalUsers, + totalUsersCumulative, + fullyRegisteredUsers, + fullyRegisteredUsersCumulative, + }; + }); +} + +function toRenderableData(results: ProcessedResult[]): ProcessedRenderableData[] { + return results.flatMap( + ({ + date, + totalUsers, + totalUsersCumulative, + fullyRegisteredUsers, + fullyRegisteredUsersCumulative, + }) => [ + { date, value: totalUsers, type: DataType.TOTAL_USERS }, + { date, value: totalUsersCumulative, type: DataType.TOTAL_USERS_CUMULATIVE }, + { date, value: fullyRegisteredUsers, type: DataType.FULLY_REGISTERED_USERS }, + { + date, + value: fullyRegisteredUsersCumulative, + type: DataType.FULLY_REGISTERED_USERS_CUMULATIVE, + }, + ] + ); +} + +function loadData(date: Date, env: string, fetch = window.fetch): Promise { + const path = reportPath({ reportName: "daily-registrations-report", date, env }); + return fetch(path) + .then((response) => (response.status === 200 ? response.json() : { results: [] })) + .then((report) => process(report)); +} + +export { ProcessedResult, ProcessedRenderableData, DataType, loadData, toRenderableData }; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 3b17d9c..7a489f4 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -3,6 +3,7 @@ import { Control } from "../components/report-filter-controls"; import DailyAuthsReport from "../components/daily-auths-report"; import DailyDropffsReport from "../components/daily-dropoffs-report"; import ProofingOverTimeReport from "../components/proofing-over-time-report"; +import DailyRegistrationsReport from "../components/daily-registrations-report"; import { Router } from "../router"; import HomeRoute from "./home-route"; import createReportRoute, { ReportRoute } from "./report-route"; @@ -27,6 +28,9 @@ const reportRoutes: ReportRoutes = { defaultScale: Scale.PERCENT, }), "/": HomeRoute, + "/daily-registrations-report/": createReportRoute(DailyRegistrationsReport, { + controls: [Control.CUMULATIVE], + }), }; export function Routes(): VNode { diff --git a/src/routes/report-route.tsx b/src/routes/report-route.tsx index c544ef3..18df94a 100644 --- a/src/routes/report-route.tsx +++ b/src/routes/report-route.tsx @@ -44,6 +44,12 @@ interface ReportRouteProps { * Whether or not to show extra controls */ extra?: string; + + /** + * When "on" the report should show cumulative data + * When "off" it should show daily data + */ + cumulative?: "on" | "off"; } function createReportRoute( @@ -70,6 +76,7 @@ function createReportRoute( byAgency: byAgencyParam, extra: extraParam, timeBucket, + cumulative: cumulativeParam, }: ReportRouteProps): VNode => { const endOfPreviousWeek = utcDay.offset(utcWeek.floor(new Date()), -1); const startOfPreviousWeek = utcWeek.offset( @@ -85,6 +92,7 @@ function createReportRoute( const scale = scaleParam || defaultScale || DEFAULT_SCALE; const extra = extraParam === "true"; const byAgency = byAgencyParam ? byAgencyParam === "on" : extra; + const cumulative = cumulativeParam ? cumulativeParam === "on" : true; const reportControls = controls || []; if (extra) { @@ -108,6 +116,7 @@ function createReportRoute( byAgency={byAgency} extra={extra} timeBucket={timeBucket} + cumulative={cumulative} > diff --git a/typings/@observablehq/plot/index.d.ts b/typings/@observablehq/plot/index.d.ts index ea02529..9ea907f 100644 --- a/typings/@observablehq/plot/index.d.ts +++ b/typings/@observablehq/plot/index.d.ts @@ -5,6 +5,7 @@ declare module "@observablehq/plot" { export function binX(a: any, b: any): any; export function binY(a: any, b: any): any; export function ruleY(a: any, b?: any): any; + export function line(a: any, b: any): any; export function lineY(a: any, b: any): any; export function text(a: any, b?: any): any; } From ae2bcff17d051c30631152bdfa3483437a17e84e Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Fri, 20 Jan 2023 15:17:50 -0800 Subject: [PATCH 2/9] window --- src/components/daily-registrations-report.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx index c70100d..68fa36d 100644 --- a/src/components/daily-registrations-report.tsx +++ b/src/components/daily-registrations-report.tsx @@ -99,7 +99,7 @@ function DailyRegistrationsReport(): VNode { } }); - const windowedData = (data || []).filter(({ date }) => start < date && date < finish); + const windowedData = (data || []).filter(({ date }) => start <= date && date <= finish); return (
From 3cfcbbf961722788b7c4500c16f0f1b3b5a14d9c Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Fri, 20 Jan 2023 15:50:02 -0800 Subject: [PATCH 3/9] Add tests --- .../daily-registrations-report.test.tsx | 39 +++++++++ src/components/daily-registrations-report.tsx | 1 + .../daily-registrations-report-data.test.ts | 79 +++++++++++++++++++ src/models/daily-registrations-report-data.ts | 18 ++--- 4 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 src/components/daily-registrations-report.test.tsx create mode 100644 src/models/daily-registrations-report-data.test.ts diff --git a/src/components/daily-registrations-report.test.tsx b/src/components/daily-registrations-report.test.tsx new file mode 100644 index 0000000..b974e06 --- /dev/null +++ b/src/components/daily-registrations-report.test.tsx @@ -0,0 +1,39 @@ +import { expect } from "chai"; +import { yearMonthDayParse } from "../formats"; +import { ProcessedResult } from "../models/daily-registrations-report-data"; +import { tabulate } from "./daily-registrations-report"; + +describe("DailyRegistrationsReport", () => { + describe("#tabulate", () => { + it("renders a table", () => { + const results: ProcessedResult[] = [ + { + date: yearMonthDayParse("2020-01-01"), + totalUsers: 5, + fullyRegisteredUsers: 1, + totalUsersCumulative: 10, + fullyRegisteredUsersCumulative: 2, + }, + { + date: yearMonthDayParse("2020-01-02"), + totalUsers: 6, + fullyRegisteredUsers: 2, + totalUsersCumulative: 17, + fullyRegisteredUsersCumulative: 4, + }, + ]; + + const table = tabulate(results); + + expect(table).to.deep.equal({ + header: ["", "2020-01-01", "2020-01-02"], + body: [ + ["New Users", 5, 6], + ["New Fully Registered Users", 1, 2], + ["Cumulative Users", 10, 17], + ["Cumulative Fully Registered Users", 2, 4], + ], + }); + }); + }); +}); diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx index 68fa36d..51587c4 100644 --- a/src/components/daily-registrations-report.tsx +++ b/src/components/daily-registrations-report.tsx @@ -115,3 +115,4 @@ function DailyRegistrationsReport(): VNode { } export default DailyRegistrationsReport; +export { tabulate }; diff --git a/src/models/daily-registrations-report-data.test.ts b/src/models/daily-registrations-report-data.test.ts new file mode 100644 index 0000000..b191437 --- /dev/null +++ b/src/models/daily-registrations-report-data.test.ts @@ -0,0 +1,79 @@ +import { expect } from "chai"; +import fetchMock from "fetch-mock"; +import { yearMonthDayParse } from "../formats"; +import { DataType, loadData, toRenderableData } from "./daily-registrations-report-data"; + +describe("DailyRegistrationsReportData", () => { + describe("#loadData", () => { + const fetch = fetchMock + .sandbox() + .get("/local/daily-registrations-report/2021/2021-01-02.daily-registrations-report.json", { + finish: "2020-01-03", + results: [ + { date: "2020-01-01", total_users: 2, fully_registered_users: 1 }, + { date: "2020-01-02", total_users: 20, fully_registered_users: 10 }, + ], + }); + + it("loads data and processes it", () => { + return loadData(yearMonthDayParse("2021-01-02"), "local", fetch as typeof window.fetch).then( + (processed) => { + expect(processed).to.have.lengthOf(2); + expect(processed).to.deep.equal([ + { + date: yearMonthDayParse("2020-01-01"), + totalUsers: 2, + fullyRegisteredUsers: 1, + totalUsersCumulative: 2, + fullyRegisteredUsersCumulative: 1, + }, + { + date: yearMonthDayParse("2020-01-02"), + totalUsers: 20, + fullyRegisteredUsers: 10, + totalUsersCumulative: 22, + fullyRegisteredUsersCumulative: 11, + }, + ]); + } + ); + }); + }); + + describe("toRenderableData", () => { + it("breaks into elements with value and type", () => { + const renderable = toRenderableData([ + { + date: yearMonthDayParse("2020-01-01"), + totalUsers: 1, + fullyRegisteredUsers: 2, + totalUsersCumulative: 3, + fullyRegisteredUsersCumulative: 4, + }, + ]); + + expect(renderable).to.have.deep.members([ + { + date: yearMonthDayParse("2020-01-01"), + type: DataType.TOTAL_USERS, + value: 1, + }, + { + date: yearMonthDayParse("2020-01-01"), + type: DataType.FULLY_REGISTERED_USERS, + value: 2, + }, + { + date: yearMonthDayParse("2020-01-01"), + type: DataType.TOTAL_USERS_CUMULATIVE, + value: 3, + }, + { + date: yearMonthDayParse("2020-01-01"), + type: DataType.FULLY_REGISTERED_USERS_CUMULATIVE, + value: 4, + }, + ]); + }); + }); +}); diff --git a/src/models/daily-registrations-report-data.ts b/src/models/daily-registrations-report-data.ts index 1c0a385..ac7a642 100644 --- a/src/models/daily-registrations-report-data.ts +++ b/src/models/daily-registrations-report-data.ts @@ -69,15 +69,15 @@ function toRenderableData(results: ProcessedResult[]): ProcessedRenderableData[] fullyRegisteredUsers, fullyRegisteredUsersCumulative, }) => [ - { date, value: totalUsers, type: DataType.TOTAL_USERS }, - { date, value: totalUsersCumulative, type: DataType.TOTAL_USERS_CUMULATIVE }, - { date, value: fullyRegisteredUsers, type: DataType.FULLY_REGISTERED_USERS }, - { - date, - value: fullyRegisteredUsersCumulative, - type: DataType.FULLY_REGISTERED_USERS_CUMULATIVE, - }, - ] + { date, value: totalUsers, type: DataType.TOTAL_USERS }, + { date, value: totalUsersCumulative, type: DataType.TOTAL_USERS_CUMULATIVE }, + { date, value: fullyRegisteredUsers, type: DataType.FULLY_REGISTERED_USERS }, + { + date, + value: fullyRegisteredUsersCumulative, + type: DataType.FULLY_REGISTERED_USERS_CUMULATIVE, + }, + ] ); } From 78e878813f8b3831d1648a9609c5ca45d185ea1c Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Fri, 20 Jan 2023 16:20:49 -0800 Subject: [PATCH 4/9] add back route from rebase --- src/routes/all.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/all.ts b/src/routes/all.ts index 917e991..dcd9367 100644 --- a/src/routes/all.ts +++ b/src/routes/all.ts @@ -7,6 +7,7 @@ const ALL_ROUTES = { "/daily-auths-report/": "Daily Auths Report", "/daily-dropoffs-report/": "Daily Dropoffs Report", "/proofing-over-time/": "Proofing Over Time Report", + "/daily-registrations-report/": "Daily Registrations Report", }; export default ALL_ROUTES; From e6bdbdf04a96d99893237c2ef276501d8679dfa8 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Fri, 20 Jan 2023 16:23:36 -0800 Subject: [PATCH 5/9] lintfix --- .../daily-registrations-report-data.test.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/models/daily-registrations-report-data.test.ts b/src/models/daily-registrations-report-data.test.ts index b191437..8581504 100644 --- a/src/models/daily-registrations-report-data.test.ts +++ b/src/models/daily-registrations-report-data.test.ts @@ -5,17 +5,17 @@ import { DataType, loadData, toRenderableData } from "./daily-registrations-repo describe("DailyRegistrationsReportData", () => { describe("#loadData", () => { - const fetch = fetchMock - .sandbox() - .get("/local/daily-registrations-report/2021/2021-01-02.daily-registrations-report.json", { - finish: "2020-01-03", - results: [ - { date: "2020-01-01", total_users: 2, fully_registered_users: 1 }, - { date: "2020-01-02", total_users: 20, fully_registered_users: 10 }, - ], - }); - it("loads data and processes it", () => { + const fetch = fetchMock + .sandbox() + .get("/local/daily-registrations-report/2021/2021-01-02.daily-registrations-report.json", { + finish: "2020-01-03", + results: [ + { date: "2020-01-01", total_users: 2, fully_registered_users: 1 }, + { date: "2020-01-02", total_users: 20, fully_registered_users: 10 }, + ], + }); + return loadData(yearMonthDayParse("2021-01-02"), "local", fetch as typeof window.fetch).then( (processed) => { expect(processed).to.have.lengthOf(2); From ffc096807308471c30cb9f6497e03bc8a9bff98e Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Mon, 23 Jan 2023 07:19:41 -0800 Subject: [PATCH 6/9] Clean up start/finish and a few other variables --- src/components/daily-registrations-report.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx index 51587c4..9190402 100644 --- a/src/components/daily-registrations-report.tsx +++ b/src/components/daily-registrations-report.tsx @@ -20,8 +20,6 @@ function plot({ width, }: { data: ProcessedRenderableData[]; - start: Date; - finish: Date; width?: number; }): HTMLElement { return Plot.plot({ @@ -86,7 +84,7 @@ function DailyRegistrationsReport(): VNode { const { data } = useQuery(`daily-registrations-${finish.valueOf()}`, () => loadData(finish, env)); - const filteredData = toRenderableData(data || []).filter(({ type }) => { + const filteredData = data && toRenderableData(data).filter(({ type }) => { switch (type) { case DataType.TOTAL_USERS: case DataType.FULLY_REGISTERED_USERS: @@ -99,17 +97,17 @@ function DailyRegistrationsReport(): VNode { } }); - const windowedData = (data || []).filter(({ date }) => start <= date && date <= finish); + const windowedData = data && data.filter(({ date }) => start <= date && date <= finish); return (
- {data && ( + {filteredData && ( plot({ data: filteredData, start, finish, width })} - inputs={[data, start.valueOf(), finish.valueOf(), width, cumulative]} + plotter={() => plot({ data: filteredData, width })} + inputs={[data, width, cumulative]} /> )} - {data &&
} + {windowedData &&
} ); } From cccefc39a89bbe41058e8981cedbeaf720f95629 Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Mon, 23 Jan 2023 07:21:48 -0800 Subject: [PATCH 7/9] Remove extra
(view this change with whitespace hidden) --- src/components/report-filter-controls.tsx | 544 +++++++++++----------- 1 file changed, 271 insertions(+), 273 deletions(-) diff --git a/src/components/report-filter-controls.tsx b/src/components/report-filter-controls.tsx index 5e7db44..ca3aa19 100644 --- a/src/components/report-filter-controls.tsx +++ b/src/components/report-filter-controls.tsx @@ -70,287 +70,285 @@ function ReportFilterControls({ controls }: ReportFilterControlsProps): VNode { } return ( -
-
-
-
-
-
- Time Range -
-
- -
-
- -
-
-
-
- -
-
- -
-
-
- {controls?.includes(Control.TIME_BUCKET) && ( -
- Time Bucket -
- - -
-
- - -
-
- )} - {(controls?.includes(Control.AGENCY) || agency) && ( -
- - Agency - - -
- )} -
-
- {controls?.includes(Control.IAL) && ( -
- Identity -
- - -
-
- - -
-
- )} - {controls?.includes(Control.FUNNEL_MODE) && ( -
- Funnel Mode -
- - - - The funnel starts at the welcome step - -
-
+ +
+
+
+
+ Time Range +
+
+
-
- )} - {controls?.includes(Control.SCALE) && ( -
- Scale -
- - -
-
- - -
-
- )} - {controls?.includes(Control.BY_AGENCY) && ( -
- Break out by Agency -
- - -
-
- - -
-
- )} - {controls?.includes(Control.CUMULATIVE) && ( -
- Cumulative -
- - -
-
+ +
+
+
-
- )} -
-
-
-
-
+
+
+ +
+
+ +
+
+
+ {controls?.includes(Control.TIME_BUCKET) && ( +
+ Time Bucket +
+ + +
+
+ + +
+
+ )} + {(controls?.includes(Control.AGENCY) || agency) && ( +
+ + Agency + + +
+ )} +
+
+ {controls?.includes(Control.IAL) && ( +
+ Identity +
+ + +
+
+ + +
+
+ )} + {controls?.includes(Control.FUNNEL_MODE) && ( +
+ Funnel Mode +
+ + + + The funnel starts at the welcome step + +
+
+ + + + The funnel starts at the image submit step + +
+
+ )} + {controls?.includes(Control.SCALE) && ( +
+ Scale +
+ + +
+
+ + +
+
+ )} + {controls?.includes(Control.BY_AGENCY) && ( +
+ Break out by Agency +
+ + +
+
+ + +
+
+ )} + {controls?.includes(Control.CUMULATIVE) && ( +
+ Cumulative +
+ + +
+
+ + +
+
+ )} +
+
+ - {env !== DEFAULT_ENV && } - {extra && } - -
+
+ {env !== DEFAULT_ENV && } + {extra && } + ); } From b0861983d35f8fd7b9f23d193a7867c77e739ece Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Mon, 23 Jan 2023 10:46:13 -0800 Subject: [PATCH 8/9] Add "How we measured this" section --- src/components/daily-registrations-report.tsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx index 9190402..77f43a7 100644 --- a/src/components/daily-registrations-report.tsx +++ b/src/components/daily-registrations-report.tsx @@ -14,6 +14,8 @@ import { import PlotComponent from "./plot"; import { formatSIDropTrailingZeroes, formatWithCommas, yearMonthDayFormat } from "../formats"; import Table, { TableData } from "./table"; +import Accordion from "./accordion"; +import Markdown from "preact-markdown"; function plot({ data, @@ -101,6 +103,17 @@ function DailyRegistrationsReport(): VNode { return (
+ + + {filteredData && ( plot({ data: filteredData, width })} From a2747cf5d7efc140b67a593402fcf93622f9dbee Mon Sep 17 00:00:00 2001 From: Zach Margolis Date: Mon, 23 Jan 2023 10:52:43 -0800 Subject: [PATCH 9/9] linty lints --- src/components/daily-registrations-report.tsx | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/components/daily-registrations-report.tsx b/src/components/daily-registrations-report.tsx index 77f43a7..524e4c6 100644 --- a/src/components/daily-registrations-report.tsx +++ b/src/components/daily-registrations-report.tsx @@ -2,6 +2,7 @@ import { VNode } from "preact"; import { useQuery } from "preact-fetching"; import { useRef, useState, useContext } from "preact/hooks"; import * as Plot from "@observablehq/plot"; +import Markdown from "preact-markdown"; import { ReportFilterContext } from "../contexts/report-filter-context"; import useResizeListener from "../hooks/resize-listener"; import { @@ -15,15 +16,8 @@ import PlotComponent from "./plot"; import { formatSIDropTrailingZeroes, formatWithCommas, yearMonthDayFormat } from "../formats"; import Table, { TableData } from "./table"; import Accordion from "./accordion"; -import Markdown from "preact-markdown"; -function plot({ - data, - width, -}: { - data: ProcessedRenderableData[]; - width?: number; -}): HTMLElement { +function plot({ data, width }: { data: ProcessedRenderableData[]; width?: number }): HTMLElement { return Plot.plot({ color: { legend: true, @@ -86,18 +80,20 @@ function DailyRegistrationsReport(): VNode { const { data } = useQuery(`daily-registrations-${finish.valueOf()}`, () => loadData(finish, env)); - const filteredData = data && toRenderableData(data).filter(({ type }) => { - switch (type) { - case DataType.TOTAL_USERS: - case DataType.FULLY_REGISTERED_USERS: - return !cumulative; - case DataType.TOTAL_USERS_CUMULATIVE: - case DataType.FULLY_REGISTERED_USERS_CUMULATIVE: - return !!cumulative; - default: - throw new Error(`Unknown data type ${type}`); - } - }); + const filteredData = + data && + toRenderableData(data).filter(({ type }) => { + switch (type) { + case DataType.TOTAL_USERS: + case DataType.FULLY_REGISTERED_USERS: + return !cumulative; + case DataType.TOTAL_USERS_CUMULATIVE: + case DataType.FULLY_REGISTERED_USERS_CUMULATIVE: + return !!cumulative; + default: + throw new Error(`Unknown data type ${type}`); + } + }); const windowedData = data && data.filter(({ date }) => start <= date && date <= finish);