1
- import { Grid2 , Paper , Skeleton , Stack , Typography } from "@mui/material" ;
1
+ import { Grid2 , Paper , Stack , Typography } from "@mui/material" ;
2
+ import CheckBoxList from "components/common/CheckBoxList" ;
3
+ import CopyLink from "components/CopyLink" ;
2
4
import GranularityPicker from "components/GranularityPicker" ;
3
- import styles from "components/hud.module.css " ;
5
+ import LoadingPage from "components/LoadingPage " ;
4
6
import {
5
7
getTooltipMarker ,
6
8
Granularity ,
@@ -11,20 +13,21 @@ import dayjs from "dayjs";
11
13
import { EChartsOption } from "echarts" ;
12
14
import ReactECharts from "echarts-for-react" ;
13
15
import { fetcher } from "lib/GeneralUtils" ;
16
+ import {
17
+ compressToEncodedURIComponent ,
18
+ decompressFromEncodedURIComponent ,
19
+ } from "lz-string" ;
14
20
import { useRouter } from "next/router" ;
15
- import { useCallback , useState } from "react" ;
16
- import useSWR from "swr" ;
21
+ import { encodeParams } from "pages/tests/search" ;
22
+ import { useEffect , useState } from "react" ;
23
+ import useSWRImmutable from "swr/immutable" ;
17
24
import { TimeRangePicker , TtsPercentilePicker } from "../../../../metrics" ;
18
25
19
- const SUPPORTED_WORKFLOWS = [
20
- "pull" ,
21
- "trunk" ,
22
- "nightly" ,
23
- "periodic" ,
24
- "inductor" ,
25
- "inductor-periodic" ,
26
- "rocm" ,
27
- "inductor-rocm" ,
26
+ const INGORED_WORKFLOWS = [
27
+ "Upload test stats" ,
28
+ "Upload torch dynamo performance stats" ,
29
+ "Validate and merge PR" ,
30
+ "Revert merged PR" ,
28
31
] ;
29
32
30
33
function Panel ( {
@@ -74,24 +77,33 @@ function Panel({
74
77
) ;
75
78
}
76
79
77
- function Graphs ( {
78
- queryParams,
79
- granularity,
80
- ttsPercentile,
81
- checkboxRef,
82
- branchName,
83
- filter,
84
- toggleFilter,
85
- } : {
86
- queryParams : { [ key : string ] : any } ;
87
- granularity : Granularity ;
88
- ttsPercentile : number ;
89
- checkboxRef : any ;
90
- branchName : string ;
91
- filter : any ;
92
- toggleFilter : any ;
93
- } ) {
94
- const ROW_HEIGHT = 800 ;
80
+ export default function Page ( ) {
81
+ const router = useRouter ( ) ;
82
+ const repoOwner : string = ( router . query . repoOwner as string ) ?? "pytorch" ;
83
+ const repoName : string = ( router . query . repoName as string ) ?? "pytorch" ;
84
+ const branch : string = ( router . query . branch as string ) ?? "main" ;
85
+ const jobNamesCompressed : string =
86
+ ( router . query . jobNamesCompressed as string ) ?? "" ;
87
+ const [ startTime , setStartTime ] = useState ( dayjs ( ) . subtract ( 1 , "week" ) ) ;
88
+ const [ stopTime , setStopTime ] = useState ( dayjs ( ) ) ;
89
+ const [ granularity , setGranularity ] = useState < Granularity > ( "day" ) ;
90
+ const [ timeRange , setTimeRange ] = useState < number > ( 7 ) ;
91
+ const [ ttsPercentile , setTtsPercentile ] = useState < number > ( 0.5 ) ;
92
+ const [ selectedJobs , setSelectedJobs ] = useState < {
93
+ [ key : string ] : boolean ;
94
+ } > ( { } ) ;
95
+
96
+ const GRAPHS_HEIGHT = 800 ;
97
+
98
+ const queryParams : { [ key : string ] : any } = {
99
+ branch : branch ,
100
+ granularity : granularity ,
101
+ percentile : ttsPercentile ,
102
+ repo : `${ repoOwner } /${ repoName } ` ,
103
+ startTime : dayjs ( startTime ) . utc ( ) . format ( "YYYY-MM-DDTHH:mm:ss.SSS" ) ,
104
+ stopTime : dayjs ( stopTime ) . utc ( ) . format ( "YYYY-MM-DDTHH:mm:ss.SSS" ) ,
105
+ ignoredWorkflows : INGORED_WORKFLOWS ,
106
+ } ;
95
107
96
108
let queryName = "tts_duration_historical_percentile" ;
97
109
let ttsFieldName = "tts_percentile_sec" ;
@@ -104,38 +116,19 @@ function Graphs({
104
116
durationFieldName = "duration_avg_sec" ;
105
117
}
106
118
107
- const timeFieldName = "granularity_bucket" ;
108
- const groupByFieldName = "full_name" ;
109
119
const url = `/api/clickhouse/${ queryName } ?parameters=${ encodeURIComponent (
110
120
JSON . stringify ( queryParams )
111
121
) } `;
112
122
113
- const { data, error } = useSWR ( url , fetcher , {
114
- refreshInterval : 60 * 60 * 1000 , // refresh every hour
115
- } ) ;
116
-
117
- if ( error !== undefined ) {
118
- // TODO: figure out how to deterine what error it actually is, can't just log the error
119
- // because its in html format instead of json?
120
- return (
121
- < div >
122
- error occured while fetching data, perhaps there are too many results
123
- with your choice of time range and granularity?
124
- </ div >
125
- ) ;
126
- }
127
-
128
- if ( data === undefined ) {
129
- return < Skeleton variant = { "rectangular" } height = { "100%" } /> ;
130
- }
131
-
132
- // Clamp to the nearest granularity (e.g. nearest hour) so that the times will
133
- // align with the data we get from the database
134
- const startTime = dayjs ( queryParams [ "startTime" ] ) . startOf ( granularity ) ;
135
- const stopTime = dayjs ( queryParams [ "stopTime" ] ) . startOf ( granularity ) ;
123
+ const { data, error } = useSWRImmutable < { [ key : string ] : any } [ ] > (
124
+ url ,
125
+ fetcher
126
+ ) ;
136
127
128
+ const timeFieldName = "granularity_bucket" ;
129
+ const groupByFieldName = "full_name" ;
137
130
const tts_true_series = seriesWithInterpolatedTimes (
138
- data ,
131
+ data ?? [ ] ,
139
132
startTime ,
140
133
stopTime ,
141
134
granularity ,
@@ -144,109 +137,53 @@ function Graphs({
144
137
ttsFieldName
145
138
) ;
146
139
const duration_true_series = seriesWithInterpolatedTimes (
147
- data ,
140
+ data ?? [ ] ,
148
141
startTime ,
149
142
stopTime ,
150
143
granularity ,
151
144
groupByFieldName ,
152
145
timeFieldName ,
153
146
durationFieldName
154
147
) ;
155
- var tts_series = tts_true_series . filter ( ( item : any ) =>
156
- filter . has ( item [ "name" ] )
157
- ) ;
158
- var duration_series = duration_true_series . filter ( ( item : any ) =>
159
- filter . has ( item [ "name" ] )
160
- ) ;
161
148
162
- const repo = queryParams [ "repo" ] ;
163
- const encodedBranchName = encodeURIComponent ( branchName ) ;
164
- const jobUrlPrefix = `/tts/${ repo } /${ encodedBranchName } ?jobName=` ;
165
-
166
- return (
167
- < Grid2 container spacing = { 2 } >
168
- < Grid2 size = { { xs : 9 } } height = { ROW_HEIGHT } >
169
- < Paper sx = { { p : 2 , height : "50%" } } elevation = { 3 } >
170
- < Panel title = { "tts" } series = { tts_series } />
171
- </ Paper >
172
- < Paper sx = { { p : 2 , height : "50%" } } elevation = { 3 } >
173
- < Panel title = { "duration" } series = { duration_series } />
174
- </ Paper >
175
- </ Grid2 >
176
- < Grid2 size = { { xs : 3 } } height = { ROW_HEIGHT } >
177
- < div
178
- style = { { overflow : "auto" , height : ROW_HEIGHT , fontSize : "15px" } }
179
- ref = { checkboxRef }
180
- >
181
- { tts_true_series . map ( ( job ) => (
182
- < div
183
- key = { job [ "name" ] }
184
- className = { filter . has ( job [ "name" ] ) ? styles . selectedRow : "" }
185
- >
186
- < input
187
- type = "checkbox"
188
- id = { job [ "name" ] }
189
- onChange = { toggleFilter }
190
- checked = { filter . has ( job [ "name" ] ) }
191
- />
192
- < label htmlFor = { job [ "name" ] } >
193
- < a href = { jobUrlPrefix + encodeURIComponent ( job [ "name" ] ) } >
194
- { job [ "name" ] }
195
- </ a >
196
- </ label >
197
- </ div >
198
- ) ) }
199
- </ div >
200
- </ Grid2 >
201
- </ Grid2 >
149
+ var tts_series = tts_true_series . filter (
150
+ ( item : any ) => selectedJobs [ item [ "name" ] ]
151
+ ) ;
152
+ var duration_series = duration_true_series . filter (
153
+ ( item : any ) => selectedJobs [ item [ "name" ] ]
202
154
) ;
203
- }
204
-
205
- export default function Page ( ) {
206
- const router = useRouter ( ) ;
207
- const repoOwner : string = ( router . query . repoOwner as string ) ?? "pytorch" ;
208
- const repoName : string = ( router . query . repoName as string ) ?? "pytorch" ;
209
- const branch : string = ( router . query . branch as string ) ?? "main" ;
210
- const jobName : string = ( router . query . jobName as string ) ?? "none" ;
211
- const percentile : number =
212
- router . query . percentile === undefined
213
- ? 0.5
214
- : parseFloat ( router . query . percentile as string ) ;
215
-
216
- const [ startTime , setStartTime ] = useState ( dayjs ( ) . subtract ( 1 , "week" ) ) ;
217
- const [ stopTime , setStopTime ] = useState ( dayjs ( ) ) ;
218
- const [ timeRange , setTimeRange ] = useState < number > ( 7 ) ;
219
- const [ granularity , setGranularity ] = useState < Granularity > ( "day" ) ;
220
- const [ ttsPercentile , setTtsPercentile ] = useState < number > ( percentile ) ;
221
155
222
- const [ filter , setFilter ] = useState ( new Set ( ) ) ;
223
- function toggleFilter ( e : any ) {
224
- var jobName = e . target . id ;
225
- const next = new Set ( filter ) ;
226
- if ( filter . has ( jobName ) ) {
227
- next . delete ( jobName ) ;
228
- } else {
229
- next . add ( jobName ) ;
156
+ useEffect ( ( ) => {
157
+ if ( tts_true_series === undefined ) {
158
+ return ;
230
159
}
231
- setFilter ( next ) ;
232
- }
233
160
234
- const queryParams : { [ key : string ] : any } = {
235
- branch : branch ,
236
- granularity : granularity ,
237
- percentile : ttsPercentile ,
238
- repo : `${ repoOwner } /${ repoName } ` ,
239
- startTime : dayjs ( startTime ) . utc ( ) . format ( "YYYY-MM-DDTHH:mm:ss.SSS" ) ,
240
- stopTime : dayjs ( stopTime ) . utc ( ) . format ( "YYYY-MM-DDTHH:mm:ss.SSS" ) ,
241
- workflowNames : SUPPORTED_WORKFLOWS ,
242
- } ;
161
+ const jobNamesFromLink = JSON . parse (
162
+ jobNamesCompressed != ""
163
+ ? decompressFromEncodedURIComponent ( jobNamesCompressed )
164
+ : "[]"
165
+ ) ;
243
166
244
- const checkboxRef = useCallback ( ( ) => {
245
- const selectedJob = document . getElementById ( jobName ) ;
246
- if ( selectedJob != undefined ) {
247
- selectedJob . click ( ) ;
248
- }
249
- } , [ jobName ] ) ;
167
+ setSelectedJobs (
168
+ tts_true_series . reduce ( ( acc : any , item : any ) => {
169
+ acc [ item . name ] = jobNamesFromLink . includes ( item . name ) ;
170
+ return acc ;
171
+ } , { } as any )
172
+ ) ;
173
+ } , [ data , jobNamesCompressed ] ) ;
174
+
175
+ const permalink =
176
+ typeof window !== "undefined" &&
177
+ `${ window . location . protocol } /${ window . location . host } ${ router . asPath . replace (
178
+ / \? .+ / ,
179
+ ""
180
+ ) } ?${ encodeParams ( {
181
+ jobNamesCompressed : compressToEncodedURIComponent (
182
+ JSON . stringify (
183
+ Object . keys ( selectedJobs ) . filter ( ( key ) => selectedJobs [ key ] )
184
+ )
185
+ ) ,
186
+ } ) } `;
250
187
251
188
return (
252
189
< div >
@@ -270,16 +207,36 @@ export default function Page() {
270
207
ttsPercentile = { ttsPercentile }
271
208
setTtsPercentile = { setTtsPercentile }
272
209
/>
210
+ < CopyLink textToCopy = { permalink || "" } />
273
211
</ Stack >
274
- < Graphs
275
- queryParams = { queryParams }
276
- granularity = { granularity }
277
- ttsPercentile = { ttsPercentile }
278
- checkboxRef = { checkboxRef }
279
- branchName = { branch }
280
- filter = { filter }
281
- toggleFilter = { toggleFilter }
282
- />
212
+ < Grid2 container spacing = { 2 } >
213
+ < Grid2 size = { { xs : 9 } } height = { GRAPHS_HEIGHT } >
214
+ { error !== undefined ? (
215
+ < Typography >
216
+ error occured while fetching data, perhaps there are too many
217
+ results with your choice of time range and granularity?
218
+ </ Typography >
219
+ ) : data === undefined ? (
220
+ < LoadingPage height = { GRAPHS_HEIGHT } />
221
+ ) : (
222
+ < Stack spacing = { 2 } height = { GRAPHS_HEIGHT } >
223
+ < Paper sx = { { p : 2 , height : "50%" } } elevation = { 3 } >
224
+ < Panel title = { "tts" } series = { tts_series } />
225
+ </ Paper >
226
+ < Paper sx = { { p : 2 , height : "50%" } } elevation = { 3 } >
227
+ < Panel title = { "duration" } series = { duration_series } />
228
+ </ Paper >
229
+ </ Stack >
230
+ ) }
231
+ </ Grid2 >
232
+ < Grid2 size = { { xs : 3 } } height = { GRAPHS_HEIGHT } overflow = { "auto" } >
233
+ < CheckBoxList
234
+ items = { selectedJobs }
235
+ onChange = { setSelectedJobs }
236
+ onClick = { ( _val ) => { } }
237
+ />
238
+ </ Grid2 >
239
+ </ Grid2 >
283
240
</ div >
284
241
) ;
285
242
}
0 commit comments