-
Notifications
You must be signed in to change notification settings - Fork 2
Add Data Status Component #96
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9b1f30c
db84c33
9310c95
51b3354
062d7f2
f7d173c
660208c
bc13cd0
b02c290
419d4cb
fe25968
b6c28be
295af24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| ATOK.Elev.Inst.30Minutes.0.Decodes-Raw | ||
| KEYS.Elev.Inst.30Minutes.0.Ccp-Rev | ||
| KEYS.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| : OOLO.Elev.Inst.30Minutes.0.Ccp-Rev | ||
| : OOLO.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| : DENI.Elev.Inst.1Hour.0.Ccp-Rev | ||
| : DENI.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| : JOHN.Elev.Inst.1Hour.0.Ccp-Rev | ||
| : JOHN.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| : INDX.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : FLOR.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : JOPL.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : UNGE.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : BART.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : RALS.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : DUND.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : FRIT.Precip-Cuml.Inst.1Hour.0.Ccp-Rev | ||
| : AMES.Stage.Inst.1Hour.0.Ccp-Rev | ||
| : CLDY.Stage.Inst.1Hour.0.Ccp-Rev | ||
| ALTU.Precip-Inc.Total.15Minutes.15Minutes.Ccp-Rev | ||
| CLRK.Precip-Inc.Total.15Minutes.15Minutes.Ccp-Rev | ||
| : FSUP.Precip-Inc.Total.30Minutes.30Minutes.Ccp-Rev | ||
| : PENS.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| : THRA.Precip-Inc.Total.30Minutes.30Minutes.Ccp-Rev | ||
| : WOO2.Precip-Inc.Total.30Minutes.30Minutes.Ccp-Rev | ||
| : ALTA.Flow.Inst.15Minutes.0.Ccp-Rev | ||
| : KANS.Flow.Inst.15Minutes.0.Ccp-Rev | ||
| : PECK.Flow.Inst.15Minutes.0.Ccp-Rev | ||
| : STIL.Flow.Inst.15Minutes.0.Ccp-Rev | ||
| : WETU.Flow.Inst.30Minutes.0.Ccp-Rev | ||
| : ARBU.Stor.Inst.30Minutes.0.Ccp-Rev | ||
| : DENI.Stor.Inst.30Minutes.0.Ccp-Rev | ||
| : MARI.Stor.Inst.1Hour.0.Ccp-Rev | ||
| : TENK.Stor.Inst.1Hour.0.Ccp-Rev | ||
| : WDMA.Stor.Inst.1Hour.0.Ccp-Rev |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also should probably throw in some quick docs for useDataStatusFile. Could see them either just being an addendum to the DataStatus page or on their own. Either way works. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { Badge, Text } from "@usace/groundwork"; | ||
| import ParamsTable from "../../components/params-table"; | ||
| import DocsPage from "../_docs-wrapper"; | ||
| import Divider from "../../components/divider"; | ||
| import { Code } from "../../components/code"; | ||
| // import { DataStatus } from "@usace-watermanagement/groundwork-water" | ||
| import DataStatus from "../../../../../lib/components/data/summary/DataStatus"; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think we need to import from @usace-watermanagement/groundwork-water rather than local |
||
| import { useDataStatusFile } from "@usace-watermanagement/groundwork-water"; | ||
|
|
||
| const returnParams = [ | ||
| { | ||
| name: "office", | ||
| type: "string", | ||
| required: true, | ||
| desc: "The office code for the data status", | ||
| }, | ||
| { | ||
| name: "tsids", | ||
| type: "array", | ||
| desc: "An array of TimeSeries Identifiers to fetch data status for from CDA", | ||
| }, | ||
| { | ||
| name: "pageSize", | ||
| type: "number", | ||
| desc: "The maximum number of entries to fetch in one date range", | ||
| }, | ||
| { | ||
| name: "cdaUrl", | ||
| type: "string", | ||
| desc: "The URL to the CDA service to fetch TimeSeries from. Defaults to: https://cwms-data.usace.army.mil/cwms-data", | ||
| }, | ||
| { | ||
| name: "linkPath", | ||
| type: "string", | ||
| desc: "A url path to a project or some other page. I.e. /{office}/projects/ would end up pointing to /{office}/projects/{name}", | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this doesn't want a trailing slash, so |
||
| }, | ||
| { | ||
| name: "lookBackHours", | ||
| type: "number", | ||
| desc: "Number of hours from current time to look back for data status", | ||
| }, | ||
| { | ||
| name: "dateFormat", | ||
| type: "string", | ||
| desc: ( | ||
| <span> | ||
| A{" "} | ||
| <a | ||
| href="https://day.js.org/docs/en/display/format" | ||
| target="_blank" | ||
| rel="noreferrer" | ||
| className="underline" | ||
| > | ||
| day.js | ||
| </a>{" "} | ||
| format string for the date display in the table | ||
| </span> | ||
| ), | ||
| }, | ||
| { | ||
| name: "showBadges", | ||
| type: "boolean", | ||
| desc: "A flag to determine if the badge color legend should be shown", | ||
| }, | ||
| { | ||
| name: "title", | ||
| type: "string", | ||
| desc: "The title of the Data Status component within UsaceBox", | ||
| }, | ||
| ]; | ||
|
|
||
| function DataStatusPage() { | ||
| const { data: fileTsids = [], error: fileError, isPending: filePending } = useDataStatusFile({ | ||
| fileUrl: "/data/summary/swt.datastatus", | ||
| }) | ||
| return ( | ||
| <DocsPage middleText="Data Status Overview"> | ||
| <div> | ||
| <Text className="w-3/4 mb-2"> | ||
| The Data Status component uses quality flags and values from | ||
| timeseries to return a quick overview of data in the last number of | ||
| hours that you specify. It is not meant to be a replacement of the | ||
| DataStatusSummary CWMS application, but certainly can function as a | ||
| substitute! | ||
| </Text> | ||
| </div> | ||
| {!filePending && fileError ? <div>Failed to load TSID data status file! {fileError?.message}</div> : <DataStatus | ||
| office="SWT" | ||
| tsids={["KEYS.Elev.Inst.1Hour.0.Ccp-Rev", ...fileTsids]} | ||
| dataStatusUrl={"/data/summary/swt.datastatus"} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dataStatusUrl here can be removed as well |
||
| />} | ||
| <Divider text="Code Example:" className="mt-8" /> | ||
| <div className="mt-8"> | ||
| <Code className="mt-8" language="jsx"> | ||
| {`import dayjs from "dayjs"; | ||
| import { useState } from "react"; | ||
| import { useDataStatusFile } from "@usace-watermanagement/groundwork-water"; | ||
|
|
||
| /* swt.datastatus Contents : | ||
| : This is a comment | ||
| KEYS.Elev.Inst.1Hour.0.Ccp-Rev | ||
| ALTU.Precip-Inc.Total.15Minutes.15Minutes.Ccp-Rev | ||
| CLRK.Precip-Inc.Total.15Minutes.15Minutes.Ccp-Rev | ||
| : KEYS.Precip-Inc.Total.1Hour.1Hour.Ccp-Rev | ||
| */ | ||
|
|
||
| default export function Example() { | ||
| /* This is optional if you want to load tsids from a file */ | ||
| const { data: fileTsids, error: fileError, isPending: filePending } = useDataStatusFile({ | ||
| fileUrl: "/data/summary/swt.datastatus", | ||
| }) | ||
| if (filePending) { | ||
| return <Skeleton className="w-full" /> | ||
| } | ||
| if (fileError) { | ||
| return <div>Failed to load TSID data status file! {fileError?.message}</div> | ||
| } | ||
| /* You could use a timeseries group to load tsids as well! */ | ||
| // Or use a static array below : | ||
| const tsids = ["KEYS.Elev.Inst.1Hour.0.Ccp-Rev"] | ||
| return <DataStatus | ||
| office="SWT" | ||
| tsids={[...tsids, ...fileTsids]} | ||
| dataStatusUrl={"/data/summary/swt.datastatus"} | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can remove the dataStatusUrl line |
||
| /> | ||
| } | ||
| `} | ||
| </Code> | ||
| </div> | ||
| <div className="font-bold text-lg pt-6"> | ||
| Data Status Parameters | ||
| <Code | ||
| enableCopy={false} | ||
| className="p-2" | ||
| language="jsx" | ||
| >{`<DataStatus ... />`}</Code> | ||
| </div> | ||
| <Divider text="API Reference" className="mt-8" /> | ||
| <ParamsTable paramsList={returnParams} showReq={true} /> | ||
| <Badge color="yellow" className="my-2"> | ||
| You must specify either dataStatusUrl or tsids or the table will have no | ||
| values! | ||
| </Badge> | ||
|
Comment on lines
+140
to
+143
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can remove dataStatusUrl here |
||
| </DocsPage> | ||
| ); | ||
| } | ||
|
|
||
| export { DataStatusPage }; | ||
| export default DataStatusPage; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { useQuery, UseQueryOptions } from "@tanstack/react-query"; | ||
|
|
||
|
|
||
| interface useDataStatusFileParams { | ||
| fileUrl: string; | ||
| queryOptions?: Partial<UseQueryOptions<string[]>>; | ||
| } | ||
|
|
||
| const useDataStatusFile = ({ | ||
| fileUrl, | ||
| queryOptions, | ||
| }: useDataStatusFileParams) => { | ||
|
|
||
| return useQuery({ | ||
| queryKey: ["dataStatus", fileUrl], | ||
| queryFn: async () => { | ||
| return fetch(fileUrl) | ||
| .then((response) => response.text()) | ||
| .then((text) => { | ||
| // Split by new lines and filter out lines starting with ":" (commented) | ||
| return text.split("\n").filter((line) => !line.startsWith(":")); | ||
| }); | ||
| }, | ||
| refetchOnWindowFocus: false, | ||
| ...queryOptions, | ||
| }); | ||
|
|
||
| }; | ||
|
|
||
| export { useDataStatusFile }; | ||
| export default useDataStatusFile; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
|
|
||
| import { | ||
| UsaceBox, | ||
| Badge, | ||
| Text, | ||
| Table, | ||
| TableBody, | ||
| TableRow, | ||
| TableHead, | ||
| TableCell, | ||
| } from "@usace/groundwork"; | ||
|
|
||
| import "../../../css/alert.css"; | ||
| import StatusRow from "./components/StatusRow"; | ||
|
|
||
| function DataStatus({ | ||
| office, | ||
| pageSize, | ||
| cdaUrl, | ||
| linkPath, | ||
| tsids = [], | ||
| lookBackHours = 24, | ||
| dateFormat = "DD MMM HH:mm", | ||
| showBadges = true, | ||
| title = "Data Status", | ||
| }) { | ||
| // fetch the data status file from URL and parse it new line delimited | ||
|
|
||
|
|
||
| if (!tsids) { | ||
| console.error( | ||
| "Error: No data status URL or tsids provided to component <DataStatus />" | ||
| ); | ||
| return ( | ||
| <Badge color="red">Error: No data status URL or tsids provided</Badge> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <UsaceBox title={title}> | ||
| {showBadges && ( | ||
| <> | ||
| <Text key="info">Data Quality Flags are shown as: </Text> | ||
| <Badge key="missing" className="gww-ms-2 alert-missing"> | ||
| Missing | ||
| </Badge> | ||
| <Badge key="questionable" className="gww-ms-2 alert-questionable"> | ||
| Questionable | ||
| </Badge> | ||
| <Badge key="unknown" className="gww-ms-2 alert-unknown"> | ||
| Unknown or Undefined | ||
| </Badge> | ||
| <Badge key="okay" className="gww-ms-2 alert-okay"> | ||
| Passed Screening and/or Validated | ||
| </Badge> | ||
| </> | ||
| )} | ||
| <Table key="table"> | ||
| {/* Build list of dates for column headers */} | ||
| <TableHead key="table-header"> | ||
| <TableRow key="header-row"> | ||
| <TableCell key="gage-header">Gage</TableCell> | ||
| <TableCell key="quality-header">Quality Info</TableCell> | ||
| <TableCell key="latest-header" title={dateFormat}> | ||
| Latest Date-time | ||
| </TableCell> | ||
| </TableRow> | ||
| </TableHead> | ||
| <TableBody> | ||
| {tsids.map((name, idx) => { | ||
| if (name) { | ||
| return ( | ||
| <StatusRow | ||
| key={`status-row-${idx}-${name}`} | ||
| office={office} | ||
| pageSize={pageSize} | ||
| cdaUrl={cdaUrl} | ||
| linkPath={linkPath} | ||
| name={name} | ||
| idx={idx} | ||
| lookBackHours={lookBackHours} | ||
| dateFormat={dateFormat} | ||
| /> | ||
| ); | ||
| } | ||
| return null; | ||
| })} | ||
| </TableBody> | ||
| </Table> | ||
| </UsaceBox> | ||
| ); | ||
| } | ||
|
|
||
| export default DataStatus; | ||
| export { DataStatus }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should probably add this page to the nav-links