Skip to content

Commit

Permalink
unpushed fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
boufni95 committed Jan 27, 2025
1 parent fb0fc3c commit b4942e6
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 131 deletions.
130 changes: 87 additions & 43 deletions src/Pages/Stats/StatsGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,96 @@
/* import * as Types from '../../Api/pub/autogenerated/ts/types'
const z = (n: number) => n < 10 ? `0${n}` : `${n}`
import { toast } from "react-toastify";
import { Buffer } from 'buffer';
import { Chart, Line } from 'react-chartjs-2'
import * as Types from '../../Api/pub/autogenerated/ts/types'
import { useMemo, useState } from 'react'
import { SpendFrom } from "../../globalTypes";
import { getNostrClient } from "../../Api";
import { parseTLV, tlvToUsageMetrics } from "./tlv";
export type MetricsData = { entries: Types.UsageMetric[], currentPage: number, allPages: number[] }
export const StatsGraph = (methodMetrics: MetricsData) => {
const labels = [] as string[]
const handleInNanoData = [] as number[]
const authInNanoData = [] as number[]
const parsedInNanoData = [] as number[]
const validateInNanoData = [] as number[]
export type SelectedMetrics = {
auth_in_nano?: boolean
handle_in_nano?: boolean
parsed_in_nano?: boolean
validate_in_nano?: boolean
}
type Props = { selectedSource: SpendFrom | undefined, appId: string, rpcMethod: string, methodMetrics: MetricsData, successFilter: 'yes' | 'no' | '', nostrFilter: 'yes' | 'no' | '', selectedMetrics: SelectedMetrics }
export const z = (n: number) => n < 10 ? `0${n}` : `${n}`
export const StatsGraph = ({ selectedSource, appId, rpcMethod, methodMetrics, nostrFilter, successFilter, selectedMetrics }: Props) => {
const [shownPage, setShownPage] = useState<number>(-1)
const [pagesData, setPagesData] = useState<Record<number, Types.UsageMetric[]>>({})
const { datasets, labels } = useMemo(() => {
const labels = [] as string[]
const handleInNanoData = [] as number[]
const authInNanoData = [] as number[]
const parsedInNanoData = [] as number[]
const validateInNanoData = [] as number[]
const pageData = shownPage === -1 ? methodMetrics.entries : pagesData[shownPage] || []
pageData.forEach(metric => {
if (successFilter === 'yes' && !metric.success) return
if (successFilter === 'no' && metric.success) return
if (nostrFilter === 'yes' && !metric.nostr) return
if (nostrFilter === 'no' && metric.nostr) return
const d = new Date(metric.processed_at_ms)
const date = `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}`
labels.push(date)
if (selectedMetrics.auth_in_nano) authInNanoData.push(metric.auth_in_nano / 1000000)
if (selectedMetrics.handle_in_nano) handleInNanoData.push(metric.handle_in_nano / 1000000)
if (selectedMetrics.parsed_in_nano) parsedInNanoData.push(metric.parsed_in_nano / 1000000)
if (selectedMetrics.validate_in_nano) validateInNanoData.push(metric.validate_in_nano / 1000000)
})
const datas = [
{ label: "Auth time (ms)", data: authInNanoData, color: '#66c2a5' },
{ label: "Handle time (ms)", data: handleInNanoData, color: '#fee08b' },
{ label: "Parsed time (ms)", data: parsedInNanoData, color: '#5e4fa2' },
{ label: "Validate time (ms)", data: validateInNanoData, color: '#9e0142' },]
const datasets = datas.filter(({ data }) => data.length > 0).map((d, i) => ({
label: d.label,
data: d.data,
backgroundColor: d.color,
borderColor: d.color,
color: d.color,
}))
console.log({ nostrFilter, successFilter, datasets, labels })
return { datasets, labels }
}, [shownPage, pagesData, methodMetrics, selectedMetrics, nostrFilter, successFilter])

const loadMore = async (app: string, rpcMethod: string, page: number) => {
if (!selectedSource) {
toast.error('Something went wrong')
return
}
if (shownPage === page) return
setShownPage(page)
const c = await getNostrClient(selectedSource.pasteField, selectedSource.keys)
const res = await c.GetSingleUsageMetrics({ app_id: app, metrics_name: rpcMethod, page })
if (res.status !== 'OK') {
toast.error('Error fetching metrics ' + res.reason)
return
}
const moreData = {
...pagesData, [page]: res.base_64_tlvs.map(tlvString => {
const decoded = Uint8Array.from(Buffer.from(tlvString, 'base64'))
const tlv = parseTLV(decoded)
return tlvToUsageMetrics("", tlv)
})
}
setPagesData(moreData)
}

const graphData = [] as number[]
methodMetrics.entries.forEach(metric => {
if (successFilter === 'yes' && !metric.success) return
if (successFilter === 'no' && metric.success) return
if (nostrFilter === 'yes' && !metric.nostr) return
if (nostrFilter === 'no' && metric.nostr) return
const d = new Date(metric.processed_at_ms)
const date = `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}`
labels.push(date)
if (selectedMetrics.auth_in_nano) authInNanoData.push(metric.auth_in_nano / 1000000)
if (selectedMetrics.handle_in_nano) handleInNanoData.push(metric.handle_in_nano / 1000000)
if (selectedMetrics.parsed_in_nano) parsedInNanoData.push(metric.parsed_in_nano / 1000000)
if (selectedMetrics.validate_in_nano) validateInNanoData.push(metric.validate_in_nano / 1000000)
})
const datas = [
{ label: "Auth time (ms)", data: authInNanoData, color: '#66c2a5' },
{ label: "Handle time (ms)", data: handleInNanoData, color: '#fee08b' },
{ label: "Parsed time (ms)", data: parsedInNanoData, color: '#5e4fa2' },
{ label: "Validate time (ms)", data: validateInNanoData, color: '#9e0142' },]
const datasets = datas.filter(({ data }) => data.length > 0).map((d, i) => ({
label: d.label,
data: d.data,
backgroundColor: d.color,
borderColor: d.color,
color: d.color,
//fill: true,
//color: randomHexColors[randomHexColors.length % i],
//tension: 0.1,
}))
return <div key={rpcMethod} style={{ width: 600, border: '1px solid black' }}>
<h2>{rpcMethod}</h2>
<Line data={{
labels,
datasets,
}} />
<div style={{ textAlign: 'center' }}>{methodMetrics.allPages.map(p =>
<span style={{ margin: '2px', textDecoration: p === methodMetrics.currentPage ? 'underline' : ' none' }}
onClick={() => loadMore(selectedApp, rpcMethod, p)} key={p}
>{p}</span>
)}<span>(last 100)</span></div>
<div style={{ textAlign: 'center' }}>
{methodMetrics.allPages.map(p =>
<span style={{ margin: '2px', textDecoration: p === shownPage ? 'underline' : ' none' }}
onClick={() => loadMore(appId, rpcMethod, p)} key={p}
>{p}</span>
)}
<span style={{ margin: '2px', textDecoration: -1 === shownPage ? 'underline' : ' none' }}
onClick={() => loadMore(appId, rpcMethod, -1)} key={-1}>(last 100)</span>
</div>
</div>
} */
}
131 changes: 43 additions & 88 deletions src/Pages/Stats/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,32 @@ import { useParams, useLocation } from "react-router";
import * as Types from '../../Api/pub/autogenerated/ts/types'
import Checkbox from "../../Components/Checkbox";
import { parseTLV, tlvToUsageMetrics } from "./tlv";
import { MetricsData, SelectedMetrics, StatsGraph, z } from "./StatsGraph";
//import { MetricsData } from "./StatsGraph";
type OfferItemType = {
title: string;
value: string;
type: string;
};
const z = (n: number) => n < 10 ? `0${n}` : `${n}`
export type MetricsData = { entries: Types.UsageMetric[], currentPage: number, allPages: number[] }
//const z = (n: number) => n < 10 ? `0${n}` : `${n}`
//export type MetricsData = { entries: Types.UsageMetric[], currentPage: number, allPages: number[] }
type UsageData = Record<string, Record<string, MetricsData>>
type PagedData = Record<string, Record<string, Record<number, Types.UsageMetric[]>>>
type SelectedMetrics = {
/* type SelectedMetrics = {
auth_in_nano?: boolean
handle_in_nano?: boolean
parsed_in_nano?: boolean
validate_in_nano?: boolean
}
} */

export const Stats = () => {
const [apps, setApps] = useState<string[]>([])
const [data, setData] = useState<UsageData>()
const [moreData, setMoreData] = useState<PagedData>()
//const [moreData, setMoreData] = useState<PagedData>()
const [range, setRange] = useState<{ from: string, to: string }>()
const [selectedApp, setSelectedApp] = useState<string>('')
const [selectedSource, setSelectedSource] = useState<string>('')
const [selectedSourceId, setSelectedSourceId] = useState<string>('')
const [selectedSource, setSelectedSource] = useState<SpendFrom>()
const [selectedMetrics, setSelectedMetrics] = useState<SelectedMetrics>({ handle_in_nano: true })
const [successFilter, setSuccessFilter] = useState<'yes' | 'no' | ''>("")
const [nostrFilter, setNostrFilter] = useState<'yes' | 'no' | ''>("")
Expand All @@ -63,14 +65,14 @@ export const Stats = () => {
const apps = Object.keys(res.apps)
console.log(apps)
const usageData = {} as UsageData
const moreDataInit = {} as PagedData
//const moreDataInit = {} as PagedData
let min = Number.MAX_SAFE_INTEGER
let max = 0
apps.forEach((app) => {
const appMetrics = res.apps[app].app_metrics
console.log(appMetrics)
usageData[app] = {}
moreDataInit[app] = {}
//moreDataInit[app] = {}
Object.keys(appMetrics).forEach((rpcMethod) => {
const methodMetrics = appMetrics[rpcMethod].base_64_tlvs.map(tlvString => {
const decoded = Uint8Array.from(Buffer.from(tlvString, 'base64'))
Expand All @@ -81,11 +83,11 @@ export const Stats = () => {
return metric
})
usageData[app][rpcMethod] = { entries: methodMetrics, allPages: appMetrics[rpcMethod].available_chunks, currentPage: appMetrics[rpcMethod].current_chunk }
moreDataInit[app][rpcMethod] = {}
//moreDataInit[app][rpcMethod] = {}
})
})
setData(usageData)
setMoreData(moreDataInit)
//setMoreData(moreDataInit)
setApps(apps)
const minTime = new Date(min)
const from = `${minTime.getFullYear()}-${z(minTime.getMonth() + 1)}-${z(minTime.getDate())} ${z(minTime.getHours())}:${z(minTime.getMinutes())}:${z(minTime.getSeconds())}`
Expand All @@ -94,92 +96,45 @@ export const Stats = () => {
setRange({ from, to })
console.log(usageData)
}

useEffect(() => {
if (!selectedSource) return
const source = enabledPaySources.find((source) => source.pasteField === selectedSource)
if (!selectedSourceId) return
const source = enabledPaySources.find((source) => source.pasteField === selectedSourceId)
if (!source) return
setSelectedSource(source)
fetchStats(source)
}, [selectedSource])
}, [selectedSourceId])

const loadMore = async (app: string, rpcMethod: string, page: number) => {
if (!selectedSource || !moreData || moreData[app][rpcMethod] === undefined) {
toast.error('Something went wrong')
return
}
const source = enabledPaySources.find((source) => source.pasteField === selectedSource)
if (!source) return
const c = await getNostrClient(source.pasteField, source.keys)
const res = await c.GetSingleUsageMetrics({ app_id: app, metrics_name: rpcMethod, page })
if (res.status !== 'OK') {
toast.error('Error fetching metrics ' + res.reason)
return
}
moreData[app][rpcMethod][page] = res.base_64_tlvs.map(tlvString => {
const decoded = Uint8Array.from(Buffer.from(tlvString, 'base64'))
const tlv = parseTLV(decoded)
return tlvToUsageMetrics("", tlv)
})
console.log(moreData)
setMoreData({ ...moreData })
}
/* const loadMore = async (app: string, rpcMethod: string, page: number) => {
if (!selectedSourceId || !moreData || moreData[app][rpcMethod] === undefined) {
toast.error('Something went wrong')
return
}
const source = enabledPaySources.find((source) => source.pasteField === selectedSourceId)
if (!source) return
const c = await getNostrClient(source.pasteField, source.keys)
const res = await c.GetSingleUsageMetrics({ app_id: app, metrics_name: rpcMethod, page })
if (res.status !== 'OK') {
toast.error('Error fetching metrics ' + res.reason)
return
}
moreData[app][rpcMethod][page] = res.base_64_tlvs.map(tlvString => {
const decoded = Uint8Array.from(Buffer.from(tlvString, 'base64'))
const tlv = parseTLV(decoded)
return tlvToUsageMetrics("", tlv)
})
console.log(moreData)
setMoreData({ ...moreData })
} */

const rows = useMemo(() => {
if (!data || !data[selectedApp]) return []
//const toRender = [] as JSX.Element[]
//let max = 0
//let min = Number.MAX_SAFE_INTEGER
return Object.keys(data[selectedApp]).map((rpcMethod) => {
const methodMetrics = data[selectedApp][rpcMethod]
const labels = [] as string[]
const handleInNanoData = [] as number[]
const authInNanoData = [] as number[]
const parsedInNanoData = [] as number[]
const validateInNanoData = [] as number[]

const graphData = [] as number[]
methodMetrics.entries.forEach(metric => {
if (successFilter === 'yes' && !metric.success) return
if (successFilter === 'no' && metric.success) return
if (nostrFilter === 'yes' && !metric.nostr) return
if (nostrFilter === 'no' && metric.nostr) return
const d = new Date(metric.processed_at_ms)
const date = `${z(d.getHours())}:${z(d.getMinutes())}:${z(d.getSeconds())}`
labels.push(date)
if (selectedMetrics.auth_in_nano) authInNanoData.push(metric.auth_in_nano / 1000000)
if (selectedMetrics.handle_in_nano) handleInNanoData.push(metric.handle_in_nano / 1000000)
if (selectedMetrics.parsed_in_nano) parsedInNanoData.push(metric.parsed_in_nano / 1000000)
if (selectedMetrics.validate_in_nano) validateInNanoData.push(metric.validate_in_nano / 1000000)
})
const datas = [
{ label: "Auth time (ms)", data: authInNanoData, color: '#66c2a5' },
{ label: "Handle time (ms)", data: handleInNanoData, color: '#fee08b' },
{ label: "Parsed time (ms)", data: parsedInNanoData, color: '#5e4fa2' },
{ label: "Validate time (ms)", data: validateInNanoData, color: '#9e0142' },]
const datasets = datas.filter(({ data }) => data.length > 0).map((d, i) => ({
label: d.label,
data: d.data,
backgroundColor: d.color,
borderColor: d.color,
color: d.color,
//fill: true,
//color: randomHexColors[randomHexColors.length % i],
//tension: 0.1,
}))
return <div key={rpcMethod} style={{ width: 600, border: '1px solid black' }}>
<h2>{rpcMethod}</h2>
<Line data={{
labels,
datasets,
}} />
<div style={{ textAlign: 'center' }}>{methodMetrics.allPages.map(p =>
<span style={{ margin: '2px', textDecoration: p === methodMetrics.currentPage ? 'underline' : ' none' }}
//onClick={() => loadMore(selectedApp, rpcMethod, p)}
key={p}
>{p}</span>
)}<span>(last 100)</span></div>
</div>
return <StatsGraph key={rpcMethod} selectedSource={selectedSource} appId={selectedApp} rpcMethod={rpcMethod}
methodMetrics={methodMetrics} nostrFilter={nostrFilter} successFilter={successFilter} selectedMetrics={selectedMetrics} />
})
}, [selectedSource, selectedApp, selectedMetrics, successFilter, nostrFilter])
}, [selectedSourceId, selectedApp, selectedMetrics, successFilter, nostrFilter])

return <div>
<h1>Stats</h1>
Expand All @@ -194,7 +149,7 @@ export const Stats = () => {
<p>Percent of Errors in the last 10m {errorStats.past10m.total > 0 ? Math.ceil(100 * errorStats.past10m.errors / errorStats.past10m.total) : 0}%</p>
<p>Percent of Errors in the last 1m {errorStats.past1m.total > 0 ? Math.ceil(100 * errorStats.past1m.errors / errorStats.past1m.total) : 0}%</p>
</>}
<select style={{ color: "black" }} value={selectedSource} onChange={(e) => setSelectedSource(e.target.value)}>
<select style={{ color: "black" }} value={selectedSourceId} onChange={(e) => setSelectedSourceId(e.target.value)}>
<option value="" hidden>Select Source</option>
{enabledPaySources.map((item, index) => <option key={index} value={item.pasteField}>{item.label}</option>)}
</select>
Expand All @@ -204,7 +159,7 @@ export const Stats = () => {
{apps.map((app, index) => <option key={index} value={app}>{app}</option>)}
</select>
<br />
{selectedApp && selectedSource && <>
{selectedApp && selectedSourceId && <>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Checkbox id="auth_in_nano" state={!!selectedMetrics.auth_in_nano} setState={(e) => setSelectedMetrics({ ...selectedMetrics, auth_in_nano: e.target.checked })} />
<label>Auth In (ms)</label>
Expand Down

0 comments on commit b4942e6

Please sign in to comment.