Skip to content

Commit

Permalink
Merge pull request #72 from tsdataclinic/api-route-filter
Browse files Browse the repository at this point in the history
re-enable route filtering client side, add requisite API endpoint
  • Loading branch information
indraneel authored Feb 27, 2024
2 parents 1c8e839 + 4155b40 commit b9d8df1
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 19 deletions.
35 changes: 34 additions & 1 deletion app/backend/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from enum import Enum
from fastapi import FastAPI, HTTPException, Response
from fastapi import FastAPI, HTTPException, Response, Query
from fastapi.middleware.cors import CORSMiddleware
from psycopg2 import pool
from typing import Union, List
from dotenv import load_dotenv
import os

Expand Down Expand Up @@ -108,6 +109,36 @@ async def get_route_summary(city: str, route: str):
output["count"] = sum(output["flood_risk_category"])
return output

@app.get("/stops-on-route")
async def stops_on_route(cities: Union[List[str], None] = Query(default=None), routes: Union[List[str], None] = Query(default=None)):
connection = app.state.db_pool.getconn()
cursor = connection.cursor()

stops_query = f"""
SELECT
stop_id,
routes_serviced
FROM public.stop_features
WHERE
'{routes[0]}'::text = ANY(string_to_array(routes_serviced, ','))
AND
'{cities[0]}' = city
"""

for i in range(1, len(cities)):
stops_query += f"""
OR ('{routes[i]}'::text = ANY(string_to_array(routes_serviced, ',')) AND '{cities[i]}' = city)
"""

cursor.execute(stops_query)
routes_result = cursor.fetchall()
stops_result = [row[0] for row in routes_result]


cursor.close()
app.state.db_pool.putconn(connection)
return stops_result

@app.get("/available-routes")
async def get_all_available_routes():
connection = app.state.db_pool.getconn()
Expand Down Expand Up @@ -168,6 +199,8 @@ async def get_all_cities():
GROUP BY city
"""

SELECT
city,
cursor.execute(city_query)
cities = [{
'city': r[0],
Expand Down
8 changes: 5 additions & 3 deletions app/src/components/ContextPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ function ContextPane({
</div>
<hr />
<div className="text-lg flex justify-between">
<b>Transit Lines</b>
{/* <button onClick={() => setSelectedRoutes([])}>Reset</button> */}
<b>Filter Transit Lines</b>
<button onClick={() => setSelectedRoutes([])}>Reset</button>
</div>
{selectedRoutes.length > 0 ? (
<div className="grid grid-cols-4 px-3 gap-1">
Expand Down Expand Up @@ -199,7 +199,9 @@ function ContextPane({
}
setSelectedRoutes(newRoutes);
}}
disabled={true}
disabled={selectedRoutes.length > 1 && !selectedRoutes
.map(r => r.routeServiced)
.includes(route_serviced)}
checked={selectedRoutes
.map(r => r.routeServiced)
.includes(route_serviced)}
Expand Down
22 changes: 7 additions & 15 deletions app/src/components/MainPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as Fathom from "fathom-client";
import { Cities } from '../libs/cities';
import { useAvailableCities } from '../hooks/useAvailableCities';
import { useAvailableRoutes } from '../hooks/useAvailableRoutes';
import { useRemoteRouteFilter } from '../hooks/useRemoteRouteFilter';
import usePrevious from '../hooks/usePrevious';
import { useRemoteRouteSummary } from '../hooks/useRemoteRouteSummary';
import Modal from './ui/Modal';
Expand Down Expand Up @@ -169,6 +170,7 @@ export default function MainPage(): JSX.Element {

const [layers, setLayers] = useState(AVAILABLE_LAYERS);
let remoteLayers = useRemoteLayers(layers, selectedCity);
let {data: remoteSelectedRoutes, status: remoteRouteFilterStatus } = useRemoteRouteFilter(selectedRoutes);
let {data: routeSummaryNew, status} = useRemoteRouteSummary(detailedRoutes.city, detailedRoutes.routeServiced);

let sourceLayerConfigs = useSourceLayerConfigs(
Expand Down Expand Up @@ -196,23 +198,13 @@ export default function MainPage(): JSX.Element {
]
]),

...(selectedRoutes.length > 0
...(selectedRoutes.length > 0 && remoteRouteFilterStatus === 'success'
? [
[
'any',
...selectedRoutes.map(selectedRoute => [
'all',
['==', ['get', 'city'], selectedRoute.city],
['==', ['get', 'route_type'], selectedRoute.routeType],
// ['in'] expression doesn't work properly in a comma-separated list, so used 'index-of' instead
[
'in',
selectedRoute.routeServiced,
['get', 'routes_serviced'],
],
// ['>=', ['index-of', selectedRoute.routeServiced, ['get', 'routes_serviced']], 0],
]),
],
"in",
["get", "stop_id"],
["literal", remoteSelectedRoutes]
]
]
: []),
],
Expand Down
30 changes: 30 additions & 0 deletions app/src/hooks/useRemoteRouteFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useQuery } from "@tanstack/react-query";
import { SelectedRoute } from "../components/MainPage";

const BACKEND_URI = process.env.REACT_APP_PROD_BACKEND_URI ?? process.env.REACT_APP_DEV_BACKEND_URI;

export const useRemoteRouteFilter = (selectedRoutes: Array<SelectedRoute>) : { data: string[] , status: string} => {
const shouldEnableQuery = selectedRoutes.length > 0;
const result = useQuery({
queryKey: [`routeFilter-${JSON.stringify(selectedRoutes)}`],
queryFn: async () => {
let stopsOnRouteURL = new URL(`${BACKEND_URI}/stops-on-route`);

const params = new URLSearchParams();
selectedRoutes.forEach((route) => {
params.append('cities', route.city);
params.append('routes', route.routeServiced);
});

stopsOnRouteURL.search = params.toString();

const response = await fetch(stopsOnRouteURL);
const data = await response.json();
return data;
},
staleTime: 1000 * 60 * 60, // one hour,
enabled: shouldEnableQuery,
});
return { data: result.data , status: result.status};
};

0 comments on commit b9d8df1

Please sign in to comment.