Skip to content

Commit

Permalink
fix: OperatorSelector inconsistencies, operator filter bugs (#522)
Browse files Browse the repository at this point in the history
  • Loading branch information
Haswell-s authored Mar 9, 2024
1 parent ed0ff1f commit 024a34b
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 100 deletions.
14 changes: 9 additions & 5 deletions src/api/agencyList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ export interface Agency {
agency_name: string // example - "אלקטרה אפיקים"
}

let json: Agency[]
let agencyList: Agency[]

/**
* Fetch agency data from MOT api
* @returns Agency data array, might contain DUPLICATE agencies with different `date` values
*/
export default async function getAgencyList(): Promise<Agency[]> {
if (!json) {
if (!agencyList) {
const response = await fetch(`${BASE_PATH}/gtfs_agencies/list`)
json = await response.json()
const data = (await response.json()) as Awaited<Agency[]>
agencyList = data.filter(Boolean) // filter empty entries
}

return json
return agencyList
}

export function useAgencyList() {
Expand Down
47 changes: 15 additions & 32 deletions src/model/operator.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
import getAgencyList, { Agency } from 'src/api/agencyList'
import getAgencyList from 'src/api/agencyList'

export type Operator = {
name: string
id: string
}

export const getRelevantOperators = async (onlyRelevantOperators: boolean) => {
export const MAJOR_OPERATORS = ['3', '5', '15', '18', '25', '34'] // ['אלקטרה אפיקים', 'דן', 'מטרופולין', 'קווים', 'אגד', 'תנופה']

/**
* Get operators list, based on agencies fetched from MOT api
* @param filter Operator ID list
* @returns List of operators
*/
export async function getOperators(filter?: string[]): Promise<Operator[]> {
const agencyList = await getAgencyList()
if (!onlyRelevantOperators) {
const allOperators = agencyList.map((agency) => ({
name: agency.agency_name,
id: agency.operator_ref.toString(),
}))
return allOperators
}
const relevant = [
'אגד',
'אגד תעבורה',
'אלקטרה אפיקים',
'דן',
'מטרופולין',
'נתיב אקספרס',
'סופרבוס',
'קווים',
'תנופה',
]
const agencyMap: Map<string, Agency> = new Map()
agencyList.forEach((obj) => {
if (obj) {
agencyMap.set(obj.agency_name, obj)
}
})
const allOperators: Operator[] = agencyList.map((agency) => ({
name: agency.agency_name,
id: agency.operator_ref.toString(),
}))
const res = allOperators.filter((op, i, a) => a.findIndex((op2) => op2.id === op.id) === i) // Filter duplicates

const res = relevant.reduce((acc: Operator[], name: string): Operator[] => {
return agencyMap.has(name)
? [...acc, { name, id: (agencyMap.get(name) as Agency).operator_ref.toString() }]
: acc
}, [])
return res
return !filter ? res : res.filter((operator) => filter.includes(operator.id))
}
8 changes: 2 additions & 6 deletions src/pages/DataResearch/DataResearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { PageContainer } from '../components/PageContainer'
import { getColorName } from '../dashboard/AllLineschart/OperatorHbarChart/OperatorHbarChart'
import './DataResearch.scss'
import OperatorSelector, { FilterOperatorOptions } from '../components/OperatorSelector'
import OperatorSelector from '../components/OperatorSelector'

const now = moment()
const unique: (value: string, index: number, self: string[]) => boolean = (value, index, self) =>
Expand Down Expand Up @@ -126,11 +126,7 @@ function StackedResearchInputs({
customLabel={t('end')}
/>
</Grid>
<OperatorSelector
operatorId={operatorId}
setOperatorId={setOperatorId}
filter={FilterOperatorOptions.ALL}
/>
<OperatorSelector operatorId={operatorId} setOperatorId={setOperatorId} />
</Grid>
<label>
<input
Expand Down
15 changes: 4 additions & 11 deletions src/pages/components/OperatorSelector.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from '@storybook/react'
import '../../shared/shared.css'
import OperatorSelector, { FilterOperatorOptions } from './OperatorSelector'
import OperatorSelector from './OperatorSelector'
import { useState } from 'react'
import { MAJOR_OPERATORS } from 'src/model/operator'

const OperatorSelectorStory = () => {
const [operatorId, setOperatorId] = useState<string>()
Expand All @@ -12,17 +13,9 @@ const OperatorSelectorStory = () => {
<OperatorSelector
operatorId={operatorId}
setOperatorId={setOperatorId}
filter={FilterOperatorOptions.MAJOR}></OperatorSelector>
filter={MAJOR_OPERATORS}></OperatorSelector>
<h1>בחירת כל המפעילים</h1>
<OperatorSelector
operatorId={operatorId}
setOperatorId={setOperatorId}
filter={FilterOperatorOptions.ALL}></OperatorSelector>
<h1>בחירת המפעילים הרלוונטיים</h1>
<OperatorSelector
operatorId={operatorId}
setOperatorId={() => ''}
filter={FilterOperatorOptions.RELEVANT}></OperatorSelector>
<OperatorSelector operatorId={operatorId} setOperatorId={setOperatorId}></OperatorSelector>
</div>
)
}
Expand Down
38 changes: 9 additions & 29 deletions src/pages/components/OperatorSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Operator, getRelevantOperators } from 'src/model/operator'
import { Operator, getOperators } from 'src/model/operator'
import { Autocomplete, TextField } from '@mui/material'

export enum FilterOperatorOptions {
ALL,
RELEVANT,
MAJOR,
}

type OperatorSelectorProps = {
operatorId?: string
setOperatorId: (operatorId: string) => void
filter?: FilterOperatorOptions
filter?: string[]
}

const OperatorSelector = ({
export default function OperatorSelector({
operatorId,
setOperatorId,
filter = FilterOperatorOptions.RELEVANT,
}: OperatorSelectorProps) => {
filter,
}: OperatorSelectorProps) {
const { t } = useTranslation()
const [operators, setOperators] = useState<Operator[]>([])

useEffect(() => {
const majorOperatorsIds = ['3', '5', '15', '18', '25', '34']
getRelevantOperators(filter != FilterOperatorOptions.ALL)
.then((list) =>
filter === FilterOperatorOptions.ALL
? [...list, { id: '', name: t('operatorSelectorOptions.all') }]
: list,
)
.then((resultObj) =>
setOperators(
filter == FilterOperatorOptions.MAJOR && resultObj
? resultObj.filter((item) => majorOperatorsIds.includes(item.id))
: resultObj,
),
)
}, [filter == FilterOperatorOptions.MAJOR])
getOperators(filter).then((o) => setOperators(o))
}, [filter])

const valueFinned = operators.find((operator) => operator.id === operatorId)
const value = valueFinned ? valueFinned : null
Expand All @@ -47,13 +29,11 @@ const OperatorSelector = ({
disablePortal
style={{ width: '100%' }}
value={value}
onChange={(e, value) => setOperatorId(value ? value.id : '')}
onChange={(_, value) => setOperatorId(value ? value.id : '')}
id="operator-select"
options={operators}
renderInput={(params) => <TextField {...params} label={t('choose_operator')} />}
getOptionLabel={(option) => option.name}
/>
)
}

export default OperatorSelector
8 changes: 2 additions & 6 deletions src/pages/dashboard/DashboardPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { Alert, Typography } from 'antd'
import Grid from '@mui/material/Unstable_Grid2' // Grid version 2

// Components
import OperatorSelector, { FilterOperatorOptions } from 'src/pages/components/OperatorSelector'
import OperatorSelector from 'src/pages/components/OperatorSelector'
import DayTimeChart from './ArrivalByTimeChart/DayTimeChart'
import AllLinesChart from './AllLineschart/AllLinesChart'
import WorstLinesChart from './WorstLinesChart/WorstLinesChart'
Expand Down Expand Up @@ -63,11 +63,7 @@ const DashboardPage = () => {
</Grid>

<Grid lg={6} xs={12}>
<OperatorSelector
operatorId={operatorId}
setOperatorId={setOperatorId}
filter={FilterOperatorOptions.MAJOR}
/>
<OperatorSelector operatorId={operatorId} setOperatorId={setOperatorId} />
</Grid>
</Grid>
<Grid container spacing={2} alignItems="flex-start">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { getColorName } from '../../AllLineschart/OperatorHbarChart/OperatorHbar
function LinesHbarChart({
lines,
complement = false, // complement the chart (100% - actual) instead of actual
operators_whitelist = [],
}: {
lines: {
id: string
Expand All @@ -15,7 +14,8 @@ function LinesHbarChart({
actual: number
}[]
complement?: boolean
operators_whitelist?: Array<string>
operators_whitelist?: string[]
defaultOperators?: string[]
}) {
const percents = lines
.map((o) => (o.actual / o.total) * 100)
Expand All @@ -35,9 +35,6 @@ function LinesHbarChart({
entries={rows
.sort((a, b) => a.actual / a.total - b.actual / b.total)
.filter((line) => line.total > 10)
.filter(
(line) => !operators_whitelist.length || operators_whitelist.includes(line.operator_name),
)
.filter((line) => line.actual / line.total > 0.4)
.slice(0, 200)}
complement={complement}
Expand Down
12 changes: 6 additions & 6 deletions src/pages/dashboard/WorstLinesChart/WorstLinesChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,33 @@ import { Skeleton } from 'antd'
import { GroupByRes, useGroupBy } from 'src/api/groupByService'
import LinesHbarChart from './LineHbarChart/LinesHbarChart'
import { useTranslation } from 'react-i18next'
import { FC } from 'react'
import { Moment } from 'moment/moment'
import Widget from 'src/shared/Widget'
import { MAJOR_OPERATORS } from 'src/model/operator'

interface WorstLinesChartProps {
startDate: Moment
endDate: Moment
operatorId: string
}

export const WorstLinesChart: FC<WorstLinesChartProps> = ({ startDate, endDate, operatorId }) => {
export const WorstLinesChart = ({ startDate, endDate, operatorId }: WorstLinesChartProps) => {
const [groupByLineData, lineDataLoading] = useGroupBy({
dateTo: endDate,
dateFrom: startDate,
groupBy: 'operator_ref,line_ref',
})

const { t } = useTranslation()

const convertToWorstLineChartCompatibleStruct = (arr: GroupByRes[], operatorId: string) => {
if (!arr || !arr.length) return []
return arr
.filter(
(row: GroupByRes) => row.operator_ref?.agency_id === operatorId || !Number(operatorId),
(row) =>
operatorId || (row.operator_ref && MAJOR_OPERATORS.includes(row.operator_ref.agency_id)),
)
.map((item: GroupByRes) => ({
.filter((row) => row.operator_ref?.agency_id === operatorId || !Number(operatorId))
.map((item) => ({
id: `${item.line_ref}|${item.operator_ref?.agency_id}` || 'Unknown',
operator_name: item.operator_ref?.agency_name || 'Unknown',
short_name: JSON.parse(item.route_short_name)[0],
Expand All @@ -45,7 +46,6 @@ export const WorstLinesChart: FC<WorstLinesChartProps> = ({ startDate, endDate,
) : (
<LinesHbarChart
lines={convertToWorstLineChartCompatibleStruct(groupByLineData, operatorId)}
operators_whitelist={['אלקטרה אפיקים', 'דן', 'מטרופולין', 'קווים', 'אגד', 'תנופה']}
/>
)}
</Widget>
Expand Down

0 comments on commit 024a34b

Please sign in to comment.