Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/public/data/summary/swt.datastatus
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
2 changes: 2 additions & 0 deletions docs/src/bundles/route-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import DataHooks from "../pages/docs/hooks";
import HelpPage from "../pages/docs/help";
import CdaUrlProviderDocs from "../pages/docs/utilities/cda-url-provider";
import UtilitiesDocs from "../pages/docs/utilities";
import DataStatus from "../pages/docs/summary/data-status";

export default createRouteBundle(
{
Expand All @@ -40,6 +41,7 @@ export default createRouteBundle(
"/docs/plots/cwms-plot": CWMSPlotDocs,
"/docs/maps": Maps,
"/docs/tables": Tables,
"/docs/summary/data-status": DataStatus,
"/docs/utilities": UtilitiesDocs,
"/docs/utilities/cda-url-provider": CdaUrlProviderDocs,
"/docs/react-query": ReactQuery,
Expand Down
149 changes: 149 additions & 0 deletions docs/src/pages/docs/summary/data-status.jsx
Copy link
Collaborator

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

Copy link
Collaborator

Choose a reason for hiding this comment

The 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";
Copy link
Collaborator

Choose a reason for hiding this comment

The 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}",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this doesn't want a trailing slash, so /{office}/projects is probably a better example. Also, maybe use {location} instead of {name} (just to use consistent CWMS terminology).

},
{
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"}
Copy link
Collaborator

Choose a reason for hiding this comment

The 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"}
Copy link
Collaborator

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove dataStatusUrl here

</DocsPage>
);
}

export { DataStatusPage };
export default DataStatusPage;
31 changes: 31 additions & 0 deletions lib/components/data/hooks/useDataStatusFile.ts
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;
95 changes: 95 additions & 0 deletions lib/components/data/summary/DataStatus.jsx
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 };
Loading