Skip to content

Commit

Permalink
feat: add equipment type filter
Browse files Browse the repository at this point in the history
Closes #42
  • Loading branch information
stdavis committed Nov 18, 2024
1 parent 755b31d commit 8d3c6e2
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 45 deletions.
21 changes: 18 additions & 3 deletions src/components/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import config from '../config';
import { useFilter } from './contexts/FilterProvider';
import { useSelection } from './contexts/SelectionProvider';
import DateRange from './filters/DateRange';
import Domain from './filters/Domain';
import Location from './filters/Location';
import Purpose from './filters/Purpose';
import SpeciesLength from './filters/SpeciesLength';
import { useMap } from './hooks';
import { getStationQuery } from './queryHelpers';
Expand Down Expand Up @@ -83,7 +83,7 @@ export default function Filter(): JSX.Element {

if (Object.keys(filter).length > 0) {
const newQuery = getStationQuery(Object.values(filter));
console.log('new query:', newQuery);
console.log('new station query:', newQuery);
stationsLayer.current.definitionExpression = newQuery;
} else {
stationsLayer.current.definitionExpression = emptyDefinition;
Expand Down Expand Up @@ -139,7 +139,22 @@ export default function Filter(): JSX.Element {
<>
<h2 className="text-xl font-bold">Map filters</h2>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<Purpose />
<Domain
label="Purpose"
filterKey="purpose"
featureServiceUrl={config.urls.events}
fieldName={config.fieldNames.SURVEY_PURPOSE}
tableName={config.tableNames.events}
/>
</div>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<Domain
label="Equipment Type"
filterKey="equipmentType"
featureServiceUrl={config.urls.equipment}
fieldName={config.fieldNames.TYPE}
tableName={config.tableNames.equipment}
/>
</div>
<div className="flex flex-col gap-4 rounded border border-zinc-200 p-3 dark:border-zinc-700">
<DateRange />
Expand Down
25 changes: 14 additions & 11 deletions src/components/ResultsGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { getGridQuery, removeIrrelevantWhiteSpace } from './queryHelpers';
import { Cell, Column, Row, Table, TableHeader } from './Table';
import { getEventIdsForDownload, getResultOidsFromStationIds, getStationIdsFromResultRows } from './utils';

const STATION_NAME = 'STATION_NAME';
export type Result = Record<string, string | number | null>;
async function getData(where: string, currentUser: User): Promise<Result[]> {
if (where === '') {
Expand All @@ -30,30 +29,33 @@ async function getData(where: string, currentUser: User): Promise<Result[]> {
st.${config.fieldNames.WaterName} as ${config.fieldNames.WaterName}_Stream,
st.${config.fieldNames.DWR_WaterID} as ${config.fieldNames.DWR_WaterID}_Stream,
st.${config.fieldNames.ReachCode} as ${config.fieldNames.ReachCode}_Stream,
s.${config.fieldNames.NAME} as ${STATION_NAME},
s.${config.fieldNames.NAME} as ${config.fieldNames.STATION_NAME},
s.${config.fieldNames.STATION_ID},
SPECIES = STUFF((SELECT DISTINCT ', ' + f.${config.fieldNames.SPECIES_CODE}
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.Fish_evw as f
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.fish} as f
WHERE se.${config.fieldNames.EVENT_ID} = f.${config.fieldNames.EVENT_ID}
FOR XML PATH ('')),
1, 1, ''),
TYPES = STUFF((SELECT DISTINCT ', ' + eq.TYPE
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.Equipment_evw as eq
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.equipment} as eq
WHERE se.${config.fieldNames.EVENT_ID} = eq.${config.fieldNames.EVENT_ID}
FOR XML PATH ('')),
1, 1, '')
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.SamplingEvents_evw as se
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.events} as se
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.Fish_evw as f
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.fish} as f
ON se.${config.fieldNames.EVENT_ID} = f.${config.fieldNames.EVENT_ID}
INNER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.Stations_evw as s
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.equipment} as eq
ON se.${config.fieldNames.EVENT_ID} = eq.${config.fieldNames.EVENT_ID}
INNER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.stations} as s
ON s.${config.fieldNames.STATION_ID} = se.${config.fieldNames.STATION_ID}
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.UDWRLakes_evw as l
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.lakes} as l
ON l.${config.fieldNames.Permanent_Identifier} = s.${config.fieldNames.WATER_ID}
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.UDWRStreams_evw as st
LEFT OUTER JOIN ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.streams} as st
ON st.${config.fieldNames.Permanent_Identifier} = s.${config.fieldNames.WATER_ID}
WHERE ${where}
Expand Down Expand Up @@ -110,6 +112,7 @@ export default function ResultsGrid() {

const { currentUser } = useFirebaseAuth();
const gridQuery = getGridQuery(Object.values(filter));
console.log('new grid query:', gridQuery);
const { data, isPending, error } = useQuery({
queryKey: ['grid', gridQuery],
queryFn: () => {
Expand Down Expand Up @@ -204,7 +207,7 @@ export default function ResultsGrid() {
<Column id={`${config.fieldNames.ReachCode}_Lake`} minWidth={200}>
Lake Reach Code
</Column>
<Column id={STATION_NAME} minWidth={180}>
<Column id={config.fieldNames.STATION_NAME} minWidth={180}>
Station Name
</Column>
<Column id={config.fieldNames.SPECIES} minWidth={180}>
Expand All @@ -228,7 +231,7 @@ export default function ResultsGrid() {
<Cell>{row[`${config.fieldNames.WaterName}_Lake`]}</Cell>
<Cell>{row[`${config.fieldNames.DWR_WaterID}_Lake`]}</Cell>
<Cell>{row[`${config.fieldNames.ReachCode}_Lake`]}</Cell>
<Cell>{row[STATION_NAME]}</Cell>
<Cell>{row[config.fieldNames.STATION_NAME]}</Cell>
<Cell>{row[config.fieldNames.SPECIES]}</Cell>
<Cell>{row[config.fieldNames.TYPES]}</Cell>
<Cell>{row[config.fieldNames.EVENT_ID]}</Cell>
Expand Down
2 changes: 1 addition & 1 deletion src/components/contexts/FilterProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export type QueryInfo = {
table: string;
};
export type FilterState = Partial<Record<FilterKeys, QueryInfo>>;
type FilterKeys = 'purpose' | 'date' | 'speciesLength' | 'location';
export type FilterKeys = 'purpose' | 'date' | 'speciesLength' | 'location' | 'equipmentType';
type Action =
| {
type: 'UPDATE_TABLE';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,53 @@
import { Button, Checkbox, CheckboxGroup, Spinner } from '@ugrc/utah-design-system';
import { useEffect, useState } from 'react';
import config from '../../config';
import { useFilter } from '../contexts/FilterProvider';
import { FilterKeys, useFilter } from '../contexts/FilterProvider';
import { useDomainValues } from './utilities';

export default function Purpose(): JSX.Element {
const { data, isPending, error } = useDomainValues(config.urls.events, config.fieldNames.SURVEY_PURPOSE);
type DomainProps = {
featureServiceUrl: string;
fieldName: string;
filterKey: FilterKeys;
label: string;
tableName: string;
};
export default function Domain({
featureServiceUrl,
fieldName,
filterKey,
label,
tableName,
}: DomainProps): JSX.Element {
const { data, isPending, error } = useDomainValues(featureServiceUrl, fieldName);
const { filterDispatch } = useFilter();
const [selectedValues, setSelectedValues] = useState<string[]>([]);

useEffect(() => {
if (selectedValues.length > 0) {
filterDispatch({
type: 'UPDATE_TABLE',
filterKey: 'purpose',
filterKey,
value: {
where: `${config.fieldNames.SURVEY_PURPOSE} IN ('${selectedValues.join("','")}')`,
table: config.tableNames.events,
where: `${fieldName} IN ('${selectedValues.join("','")}')`,
table: tableName,
},
});
} else {
filterDispatch({ type: 'CLEAR_TABLE', filterKey: 'purpose' });
filterDispatch({ type: 'CLEAR_TABLE', filterKey });
}
}, [selectedValues, filterDispatch]);
}, [selectedValues, filterDispatch, filterKey, fieldName, tableName]);

if (error) {
return (
<div className="text-sm text-rose-600 forced-colors:text-[Mark]">
There was an error retrieving the purpose values
There was an error retrieving the values for this filter
</div>
);
}

return (
<>
<div>
<h3 className="text-lg font-semibold">Purpose</h3>
<h3 className="text-lg font-semibold">{label}</h3>
{isPending ? (
<Spinner />
) : (
Expand Down
27 changes: 20 additions & 7 deletions src/components/queryHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,27 @@ describe('getStationQuery', () => {
where: '2 = 2',
table: config.tableNames.events,
},
{
where: '3 = 3',
table: config.tableNames.equipment,
},
];
const expected = removeIrrelevantWhiteSpace(`(${config.fieldNames.STATION_ID} IN (
SELECT ${config.fieldNames.STATION_ID} FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.events}
WHERE ${config.fieldNames.EVENT_ID} IN (
SELECT ${config.fieldNames.EVENT_ID} FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.fish}
WHERE 1 = 1
) AND 2 = 2
))`);
const expected = removeIrrelevantWhiteSpace(
`(${config.fieldNames.STATION_ID} IN (
SELECT ${config.fieldNames.STATION_ID}
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.events}
WHERE ${config.fieldNames.EVENT_ID} IN (
SELECT ${config.fieldNames.EVENT_ID}
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.fish}
WHERE 1 = 1
) AND 2 = 2
AND ${config.fieldNames.EVENT_ID} IN (
SELECT ${config.fieldNames.EVENT_ID}
FROM ${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.equipment}
WHERE 3 = 3
)
))`,
);

expect(getStationQuery(input)).toBe(expected);
});
Expand Down
12 changes: 6 additions & 6 deletions src/components/queryHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ import { QueryInfo } from './contexts/FilterProvider';
export function getStationQuery(queryInfos: QueryInfo[]): string {
// Returns a query that selects stations given some related table queries
const tables: Record<string, string[]> = {};
const extraJoinTables = [config.tableNames.fish, config.tableNames.equipment];

// organize where clauses by table
queryInfos.forEach((info) => {
// make sure that we don't mutate this object and mess up the grid query
info = Object.assign({}, info);

if (info.table === config.tableNames.fish) {
// fish table requires an additional join
info.table = config.tableNames.events;
if (extraJoinTables.includes(info.table)) {
// these tables require an additional join
info.where = `${config.fieldNames.EVENT_ID} IN (
SELECT ${config.fieldNames.EVENT_ID} FROM
${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${config.tableNames.fish}
${config.databaseSecrets.databaseName}.${config.databaseSecrets.user}.${info.table}
WHERE ${info.where})`;
info.table = config.tableNames.events;
}

if (tables[info.table]) {
Expand Down Expand Up @@ -48,8 +49,7 @@ export function getStationQuery(queryInfos: QueryInfo[]): string {
}

export function getGridQuery(queryInfos: QueryInfo[]): string {
// Returns a query that selects rows in the grid

// Returns a query that is used as the where clause for the results grid query
const parts = queryInfos
.filter((info) => info.where)
.reduce((previous: string[], current) => {
Expand Down
16 changes: 10 additions & 6 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const config = {
NAME: 'NAME',
STREAM_TYPE: 'STREAM_TYPE',
WATER_ID: 'WATER_ID',
STATION_NAME: 'STATION_NAME', // dynamic field created via SQL query

// SamplingEvents
EVENT_ID: 'EVENT_ID',
Expand All @@ -34,12 +35,13 @@ const config = {
OBSERVERS: 'OBSERVERS',

// Fish
SPECIES: 'SPECIES', // dynamic field created via SQL query in AGSStores
SPECIES: 'SPECIES', // dynamic field created via SQL query
SPECIES_CODE: 'SPECIES_CODE',
LENGTH: 'LENGTH',

// Equipment
TYPES: 'TYPES', // dynamic field created via SQL query in AGSStores
TYPES: 'TYPES', // dynamic field created via SQL query
TYPE: 'TYPE',

// Streams/Lakes
WaterName: 'WaterName',
Expand All @@ -50,19 +52,21 @@ const config = {
},

tableNames: {
equipment: 'Equipment_evw',
events: 'SamplingEvents_evw',
stations: 'Stations_evw',
fish: 'Fish_evw',
streams: 'UDWRStreams',
lakes: 'UDWRLakes',
lakes: 'UDWRLakes_evw',
stations: 'Stations_evw',
streams: 'UDWRStreams_evw',
},

urls: {
functionsUrl,
featureService,
stations: `${featureService}/0`,
equipment: `${featureService}/7`,
events: `${featureService}/1`,
fish: `${featureService}/2`,
stations: `${featureService}/0`,
landownership:
'https://gis.trustlands.utah.gov/hosting/rest/services/Hosted/Land_Ownership_WM_VectorTile/VectorTileServer',
streams: `${referenceMapService}/0`,
Expand Down

0 comments on commit 8d3c6e2

Please sign in to comment.