Skip to content

Commit

Permalink
Merge pull request #812 from oasisprotocol/mz/charts
Browse files Browse the repository at this point in the history
Fix charts by syncing with the latest tx_volume stats API changes
  • Loading branch information
buberdds authored Aug 24, 2023
2 parents a8020f9 + 66f4dd0 commit c3fc6b7
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 68 deletions.
1 change: 1 addition & 0 deletions .changelog/812.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix charts by syncing with the latest tx_volume stats API changes
18 changes: 7 additions & 11 deletions src/app/pages/ParatimeDashboardPage/ActiveAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import startOfMonth from 'date-fns/startOfMonth'
import { SnapshotCard } from '../../components/Snapshots/SnapshotCard'
import { SnapshotCardDurationLabel } from '../../components/Snapshots/SnapshotCardDurationLabel'
import { BarChart } from '../../components/charts/BarChart'
import {
useGetLayerStatsActiveAccounts,
GetLayerStatsActiveAccountsWindowStepSeconds,
type ActiveAccounts as Windows,
} from '../../../oasis-nexus/api'
import { useGetLayerStatsActiveAccounts, type ActiveAccounts as Windows } from '../../../oasis-nexus/api'
import {
ChartDuration,
chartUseQueryStaleTimeMs,
dailyLimitWithoutBuffer,
durationToQueryParams,
filterHourlyActiveAccounts,
sumBucketsByStartDuration,
sumWindowsByStartDuration,
} from '../../utils/chart-utils'
import { SearchScope } from '../../../types/searchScope'

Expand All @@ -25,7 +21,7 @@ export const getActiveAccountsWindows = (duration: ChartDuration, windows: Windo
case ChartDuration.TODAY:
return filterHourlyActiveAccounts(windows)
case ChartDuration.ALL_TIME:
return sumBucketsByStartDuration(windows, 'active_accounts', 'window_end', startOfMonth)
return sumWindowsByStartDuration(windows, 'active_accounts', 'window_end', startOfMonth)
default:
return windows
}
Expand Down Expand Up @@ -70,10 +66,10 @@ const getLabels = (t: TFunction): Record<ChartDuration, string> => ({
export const ActiveAccounts: FC<ActiveAccountsProps> = ({ scope, chartDuration }) => {
const { t } = useTranslation()
const labels = getLabels(t)
const { limit, bucket_size_seconds } = {
const { limit, window_step_seconds } = {
...durationToQueryParams[chartDuration],
// By default we fetch data with additional buckets buffer, but it does not apply to active accounts.
// Active accounts daily buckets are overlapping, so we cannot sum buckets like in other daily charts.
// By default we fetch data with additional buffer, but it does not apply to active accounts.
// Active accounts daily windows are overlapping, so we cannot sum windows like in other daily charts.
limit:
chartDuration === ChartDuration.TODAY
? dailyLimitWithoutBuffer
Expand All @@ -84,7 +80,7 @@ export const ActiveAccounts: FC<ActiveAccountsProps> = ({ scope, chartDuration }
scope.layer,
{
limit,
window_step_seconds: bucket_size_seconds as GetLayerStatsActiveAccountsWindowStepSeconds,
window_step_seconds,
},
{
query: {
Expand Down
8 changes: 4 additions & 4 deletions src/app/pages/ParatimeDashboardPage/TotalTransactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export const TotalTransactions: FC<{ scope: SearchScope }> = ({ scope }) => {
},
})

const buckets = dailyVolumeQuery.data?.data.buckets
? cumulativeSum(dailyVolumeQuery.data?.data.buckets.slice().reverse(), 'tx_volume')
const windows = dailyVolumeQuery.data?.data.windows
? cumulativeSum(dailyVolumeQuery.data?.data.windows.slice().reverse(), 'tx_volume')
: undefined

return (
Expand All @@ -40,13 +40,13 @@ export const TotalTransactions: FC<{ scope: SearchScope }> = ({ scope }) => {
title={t('totalTransactions.header')}
/>
<CardContent sx={{ height: 450 }}>
{buckets && (
{windows && (
<LineChart
tooltipActiveDotRadius={9}
cartesianGrid
strokeWidth={3}
dataKey="tx_volume"
data={buckets}
data={windows}
margin={{ bottom: 16, top: isMobile ? 0 : 16 }}
tickMargin={16}
withLabels
Expand Down
8 changes: 4 additions & 4 deletions src/app/pages/ParatimeDashboardPage/TransactionsChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ChartDuration,
chartUseQueryStaleTimeMs,
durationToQueryParams,
sumBucketsByStartDuration,
sumWindowsByStartDuration,
} from '../../utils/chart-utils'
import { LineChart } from '../../components/charts/LineChart'
import { useScreenSize } from '../../hooks/useScreensize'
Expand Down Expand Up @@ -38,10 +38,10 @@ const TransactionsChartCardCmp: FC<TransactionsChartCardProps> = ({ scope, chart
})

const isDailyChart = isFetched && chartDuration === ChartDuration.TODAY
const buckets = data?.data?.buckets
const windows = data?.data?.windows
const lineChartData = isDailyChart
? sumBucketsByStartDuration(buckets, 'tx_volume', 'bucket_start', startOfHour)
: buckets
? sumWindowsByStartDuration(windows, 'tx_volume', 'window_end', startOfHour)
: windows
const formatParams = isDailyChart
? {
timestamp: {
Expand Down
12 changes: 6 additions & 6 deletions src/app/pages/ParatimeDashboardPage/TransactionsStats.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useGetLayerStatsTxVolume } from '../../../oasis-nexus/api'
import {
chartUseQueryStaleTimeMs,
durationToQueryParams,
getMonthlyBucketsDailyAverage,
getMonthlyWindowsDailyAverage,
} from '../../utils/chart-utils'
import { DurationPills } from './DurationPills'
import { CardHeaderWithResponsiveActions } from './CardHeaderWithResponsiveActions'
Expand All @@ -28,9 +28,9 @@ export const TransactionsStats: FC<{ scope: SearchScope }> = ({ scope }) => {
},
})
const allTime = dailyVolumeQuery.isFetched && chartDuration === ChartDuration.ALL_TIME
const buckets = allTime
? getMonthlyBucketsDailyAverage(dailyVolumeQuery.data?.data.buckets)
: dailyVolumeQuery.data?.data.buckets
const windows = allTime
? getMonthlyWindowsDailyAverage(dailyVolumeQuery.data?.data.windows)
: dailyVolumeQuery.data?.data.windows
const formatParams = allTime
? {
timestamp: {
Expand All @@ -49,12 +49,12 @@ export const TransactionsStats: FC<{ scope: SearchScope }> = ({ scope }) => {
title={t('transactionStats.header')}
/>
<CardContent sx={{ height: 450 }}>
{buckets && (
{windows && (
<BarChart
barSize={chartDuration === ChartDuration.WEEK ? 125 : undefined}
barRadius={chartDuration === ChartDuration.WEEK ? 20 : undefined}
cartesianGrid
data={buckets.slice().reverse()}
data={windows.slice().reverse()}
dataKey="tx_volume"
formatters={{
data: (value: number) => t('transactionStats.perDay', { value: value.toLocaleString() }),
Expand Down
53 changes: 33 additions & 20 deletions src/app/utils/chart-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,48 @@ import isSameMonth from 'date-fns/isSameMonth'
import startOfMonth from 'date-fns/startOfMonth'
import { GetLayerStatsTxVolumeParams, type TxVolume, type ActiveAccounts } from '../../oasis-nexus/api'

const fiveMinutesWindowSize = 60 * 5
const oneDayWindowSize = 24 * 60 * 60

export enum ChartDuration {
TODAY = 'TODAY',
WEEK = 'WEEK',
MONTH = 'MONTH',
ALL_TIME = 'ALL_TIME',
}

export const dailyLimitWithoutBuffer = (60 / 5) * 24 // full day for 5 minutes buckets,
export const dailyLimitWithoutBuffer = (60 / 5) * 24 // full day for 5 minutes windows

/*
window_size_seconds refers to the number of data points in one chunk
window_step_seconds is how much you move forward to create next chunk
both defaults to 86400 (1 day) if not specified
Example table of query params https://github.com/oasisprotocol/nexus/pull/477
*/
export const durationToQueryParams = {
[ChartDuration.TODAY]: {
bucket_size_seconds: 60 * 5,
window_size_seconds: fiveMinutesWindowSize,
window_step_seconds: fiveMinutesWindowSize,
limit:
dailyLimitWithoutBuffer +
// daily data needs additional 2 buckets to make sure we have at least full 24 buckets
// daily data needs additional 2 windows to make sure we have at least full 24 windows
(60 / 5) * 2,
},
[ChartDuration.WEEK]: {
bucket_size_seconds: 24 * 60 * 60,
window_size_seconds: oneDayWindowSize,
window_step_seconds: oneDayWindowSize,
limit: 7,
offset: 1, // offset to skip the first day
},
[ChartDuration.MONTH]: {
bucket_size_seconds: 24 * 60 * 60,
window_size_seconds: oneDayWindowSize,
window_step_seconds: oneDayWindowSize,
limit: 30, // Defined as 30 days, should be more dynamic depending on the month
offset: 1, // offset to skip the first day
},
[ChartDuration.ALL_TIME]: {
bucket_size_seconds: 24 * 60 * 60,
window_size_seconds: oneDayWindowSize,
window_step_seconds: oneDayWindowSize,
limit: 365, // Defined as a full year
offset: 1, // offset to skip the first day
},
Expand All @@ -45,31 +58,31 @@ export const chartDurationToDaysMap = {
[ChartDuration.ALL_TIME]: 365,
}

export const chartUseQueryStaleTimeMs = durationToQueryParams[ChartDuration.TODAY].bucket_size_seconds * 1000
export const chartUseQueryStaleTimeMs = durationToQueryParams[ChartDuration.TODAY].window_size_seconds * 1000

type Buckets = TxVolume[] | undefined
type Windows = TxVolume[] | undefined
type MonthlyTxVolume = TxVolume & { numberOfItemsInGroup: number }

const groupBucketsByMonth = (buckets: Buckets) => {
return buckets?.reduce((acc: MonthlyTxVolume[], cur, index, arr) => {
if (index > 0 && isSameMonth(new Date(cur.bucket_start), new Date(arr[index - 1].bucket_start))) {
const groupWindowsByMonth = (windows: Windows) => {
return windows?.reduce((acc: MonthlyTxVolume[], cur, index, arr) => {
if (index > 0 && isSameMonth(new Date(cur.window_end), new Date(arr[index - 1].window_end))) {
acc[acc.length - 1].tx_volume += cur.tx_volume
acc[acc.length - 1].numberOfItemsInGroup += 1
return acc
}
acc.push({
bucket_start: cur.bucket_start,
window_end: cur.window_end,
tx_volume: cur.tx_volume,
numberOfItemsInGroup: 1,
})
return acc
}, [])
}

export const getMonthlyBucketsDailyAverage = (buckets: Buckets): Buckets => {
const monthlyBuckets = groupBucketsByMonth(buckets)
export const getMonthlyWindowsDailyAverage = (windows: Windows): Windows => {
const monthlyWindows = groupWindowsByMonth(windows)

return monthlyBuckets?.map(item => ({
return monthlyWindows?.map(item => ({
...item,
tx_volume: Math.round((item.tx_volume / item.numberOfItemsInGroup) * 100) / 100,
}))
Expand Down Expand Up @@ -100,21 +113,21 @@ type StringOnly<T> = {
[key in keyof T as T[key] extends string | undefined ? key : never]: T[key]
}

export const sumBucketsByStartDuration = <
export const sumWindowsByStartDuration = <
T extends NumberOnly<any> & StringOnly<any>,
N extends keyof NumberOnly<T>,
S extends keyof StringOnly<T>,
>(
buckets: T[] | undefined,
windows: T[] | undefined,
sumKey: N,
dateKey: S,
startDurationFn: typeof startOfHour | typeof startOfDay | typeof startOfMonth,
) => {
if (!buckets) {
if (!windows) {
return []
}

const durationMap = buckets.reduce(
const durationMap = windows.reduce(
(accMap, item) => {
const key = startDurationFn(new Date(item[dateKey])).toISOString()

Expand All @@ -126,7 +139,7 @@ export const sumBucketsByStartDuration = <
{} as { [key: string]: number },
)

// For daily charts we want to skip the first and last buckets.
// For daily charts we want to skip the first and last windows.
// They are not full and we want to avoid chart drop.
const filteredDurationMap =
startDurationFn === startOfHour
Expand Down
46 changes: 23 additions & 23 deletions src/oasis-nexus/generated/api.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c3fc6b7

Please sign in to comment.