@@ -35,9 +35,11 @@ import './webstatus-gchart';
3535import { WebStatusDataObj } from './webstatus-gchart.js' ;
3636import { BaseChartsPage } from './webstatus-base-charts-page.js' ;
3737
38- /** Generate a key for globalFeatureSupport and missingOneImplementationMap. */
39- function statsDataKey ( browser : BrowsersParameter ) : string {
40- return browser ;
38+ interface MetricData < T > {
39+ label : string ;
40+ data : Array < T > ;
41+ getTimestamp : ( item : T ) => Date ; // Function to extract timestamp
42+ getData : ( item : T ) => number | undefined ; // Function to extract data
4143}
4244
4345@customElement ( 'webstatus-stats-page' )
@@ -54,26 +56,12 @@ export class StatsPage extends BaseChartsPage {
5456 @state ( )
5557 supportedBrowsers : BrowsersParameter [ ] = ALL_BROWSERS ;
5658
57- // Map from browser-channel to global feature support.
58- // The key is generated by statsDataKey().
59- @state ( )
60- globalFeatureSupport = new Map < string , Array < BrowserReleaseFeatureMetric > > ( ) ;
61-
62- @state ( )
63- globalFeatureSupportMax = Array < BaselineStatusMetric > ( ) ;
64-
6559 @state ( )
6660 globalFeatureSupportChartOptions = { } ;
6761
6862 @state ( )
6963 globalFeatureSupportChartDataObj : WebStatusDataObj | undefined ;
7064
71- @state ( )
72- missingOneImplementationMap = new Map <
73- string ,
74- Array < BrowserReleaseFeatureMetric >
75- > ( ) ;
76-
7765 @state ( )
7866 missingOneImplementationChartDataObj : WebStatusDataObj | undefined ;
7967
@@ -147,46 +135,53 @@ export class StatsPage extends BaseChartsPage {
147135 endDate : Date ,
148136 ) {
149137 if ( typeof apiClient !== 'object' ) return ;
138+
139+ const browserMetricData : Array < MetricData < BrowserReleaseFeatureMetric > > =
140+ ALL_BROWSERS . map ( browser => ( {
141+ label : browser ,
142+ data : [ ] ,
143+ getTimestamp : ( item : BrowserReleaseFeatureMetric ) =>
144+ new Date ( item . timestamp ) ,
145+ getData : ( item : BrowserReleaseFeatureMetric ) => item . count ,
146+ } ) ) ;
147+
148+ const maxMetricData : MetricData < BaselineStatusMetric > = {
149+ label : 'Total number of Baseline features' ,
150+ data : [ ] ,
151+ getTimestamp : ( item : BaselineStatusMetric ) => new Date ( item . timestamp ) ,
152+ getData : ( item : BaselineStatusMetric ) => item . count ,
153+ } ;
154+
155+ const allMetricData = [ ...browserMetricData , maxMetricData ] ;
156+
150157 const browserPromises = ALL_BROWSERS . map ( async browser => {
158+ const browserData = browserMetricData . find (
159+ data => data . label === browser ,
160+ ) ;
161+ if ( ! browserData ) return ;
162+
151163 for await ( const page of apiClient . getFeatureCountsForBrowser (
152164 browser ,
153165 startDate ,
154166 endDate ,
155167 ) ) {
156- // Append the new data to existing data
157- const existingData =
158- this . globalFeatureSupport . get ( statsDataKey ( browser ) ) || [ ] ;
159- this . globalFeatureSupport . set ( statsDataKey ( browser ) , [
160- ...existingData ,
161- ...page ,
162- ] ) ;
168+ browserData . data . push ( ...page ) ;
163169 }
164- this . globalFeatureSupportChartDataObj = this . createDisplayDataFromMap (
165- this . globalFeatureSupport ,
166- {
167- label : 'Total number of Baseline features' ,
168- data : this . globalFeatureSupportMax ,
169- } ,
170- ) ;
171170 } ) ;
172171
173172 const maxPromise = ( async ( ) => {
174173 for await ( const page of apiClient . listAggregatedBaselineStatusCounts (
175174 startDate ,
176175 endDate ,
177176 ) ) {
178- const existingMaxData = this . globalFeatureSupportMax || [ ] ;
179- this . globalFeatureSupportMax = [ ...existingMaxData , ...page ] ;
180- this . globalFeatureSupportChartDataObj = this . createDisplayDataFromMap (
181- this . globalFeatureSupport ,
182- {
183- label : 'Total number of Baseline features' ,
184- data : this . globalFeatureSupportMax ,
185- } ,
186- ) ;
177+ maxMetricData . data . push ( ...page ) ;
187178 }
188179 } ) ( ) ;
189- await Promise . all ( [ ...browserPromises , maxPromise ] ) ; // Wait for all promises to finish
180+
181+ await Promise . all ( [ ...browserPromises , maxPromise ] ) ;
182+
183+ this . globalFeatureSupportChartDataObj =
184+ this . createDisplayDataFromMap ( allMetricData ) ;
190185 }
191186
192187 async _fetchMissingOneImplemenationCounts (
@@ -195,27 +190,36 @@ export class StatsPage extends BaseChartsPage {
195190 endDate : Date ,
196191 ) {
197192 if ( typeof apiClient !== 'object' ) return ;
193+ const browserMetricData : Array < MetricData < BrowserReleaseFeatureMetric > > =
194+ ALL_BROWSERS . map ( browser => ( {
195+ label : browser ,
196+ data : [ ] ,
197+ getTimestamp : ( item : BrowserReleaseFeatureMetric ) =>
198+ new Date ( item . timestamp ) ,
199+ getData : ( item : BrowserReleaseFeatureMetric ) => item . count ,
200+ } ) ) ;
198201 const promises = ALL_BROWSERS . map ( async browser => {
202+ const browserData = browserMetricData . find (
203+ data => data . label === browser ,
204+ ) ;
205+ if ( ! browserData ) return ;
206+
199207 const otherBrowsers = ALL_BROWSERS . filter ( value => browser !== value ) ;
200208 for await ( const page of apiClient . getMissingOneImplementationCountsForBrowser (
201209 browser ,
202210 otherBrowsers ,
203211 startDate ,
204212 endDate ,
205213 ) ) {
206- // Append the new data to existing data
207- const existingData =
208- this . missingOneImplementationMap . get ( statsDataKey ( browser ) ) || [ ] ;
209- this . missingOneImplementationMap . set ( statsDataKey ( browser ) , [
210- ...existingData ,
211- ...page ,
212- ] ) ;
214+ browserData . data . push ( ...page ) ;
213215 }
214- this . missingOneImplementationChartDataObj = this . createDisplayDataFromMap (
215- this . missingOneImplementationMap ,
216- ) ;
217216 } ) ;
218217 await Promise . all ( promises ) ; // Wait for all browsers to finish
218+
219+ this . missingOneImplementationChartDataObj =
220+ this . createDisplayDataFromMap < BrowserReleaseFeatureMetric > (
221+ browserMetricData ,
222+ ) ;
219223 }
220224
221225 // Make startDate and endDate reactive so that @lit /task can detect the changes.
@@ -245,7 +249,7 @@ export class StatsPage extends BaseChartsPage {
245249 startDate ,
246250 endDate ,
247251 ) ;
248- return this . globalFeatureSupport ;
252+ return ;
249253 } ,
250254 } ) ;
251255
@@ -266,102 +270,61 @@ export class StatsPage extends BaseChartsPage {
266270 startDate ,
267271 endDate ,
268272 ) ;
269- return this . missingOneImplementationMap ;
273+ return ;
270274 } ,
271275 } ) ;
272276 }
273277
274278 // Make a DataTable from the target data map.
275279 // TODO(kyleju): refactor this method acorss feature detail page
276280 // and stats page, https://github.com/GoogleChrome/webstatus.dev/issues/964.
277- createDisplayDataFromMap (
278- targetMap : Map < string , Array < BrowserReleaseFeatureMetric > > ,
279- maxData ?: { label : string ; data : Array < BaselineStatusMetric > } ,
281+ createDisplayDataFromMap < T > (
282+ metricDataArray : Array < MetricData < T > > ,
280283 ) : WebStatusDataObj {
281- // Get the list of supported browsers.
282- const browsers = this . supportedBrowsers ;
283-
284284 const dataObj : WebStatusDataObj = { cols : [ ] , rows : [ ] } ;
285285 dataObj . cols . push ( { type : 'date' , label : 'Date' , role : 'domain' } ) ;
286- for ( const browser of browsers ) {
287- dataObj . cols . push ( { type : 'number' , label : browser , role : 'data' } ) ;
288- }
289- if ( maxData ) {
290- dataObj . cols . push ( { type : 'number' , label : maxData . label , role : 'data' } ) ;
291- }
292-
293- // Map from date to an object with counts for each browser
294- const dateToBrowserDataMap = new Map <
295- number ,
296- { [ key : string ] : number | null }
297- > ( ) ;
298286
299- // Create a template object with all browsers initialized to null
300- const browserNullTemplate : { [ key : string ] : number | null } = { } ;
301- for ( const browser of browsers ) {
302- browserNullTemplate [ browser ] = null ;
287+ for ( const metricData of metricDataArray ) {
288+ dataObj . cols . push ( {
289+ type : 'number' ,
290+ label : metricData . label ,
291+ role : 'data' ,
292+ } ) ;
303293 }
304294
305- // Merge data across all browsers into one array of rows.
306- for ( const browser of browsers ) {
307- const data = targetMap . get ( statsDataKey ( browser ) ) ;
308- if ( ! data ) continue ;
309- for ( const row of data ) {
310- if ( ! row ) continue ;
311- const dateSeconds = new Date ( row . timestamp ) . getTime ( ) ;
312- const featureCount = row . count ! ;
313- if ( ! dateToBrowserDataMap . has ( dateSeconds ) ) {
314- dateToBrowserDataMap . set ( dateSeconds , { } ) ;
315- }
316- const browserCounts = dateToBrowserDataMap . get ( dateSeconds ) ! ;
317- browserCounts [ browser ] = featureCount ;
318- }
319- }
295+ const dateToDataMap = new Map < number , { [ key : string ] : number | null } > ( ) ;
296+
297+ for ( const metricData of metricDataArray ) {
298+ if ( ! Array . isArray ( metricData . data ) ) continue ;
299+ for ( const item of metricData . data ) {
300+ const timestamp = metricData . getTimestamp ( item ) ;
301+ const dateSeconds = timestamp . getTime ( ) ;
302+ const dataValue = metricData . getData ( item ) ;
320303
321- // Process the maxArray (overall max data)
322- if ( maxData ) {
323- maxData . data . sort (
324- ( a , b ) =>
325- new Date ( a . timestamp ) . getTime ( ) - new Date ( b . timestamp ) . getTime ( ) ,
326- ) ; // Sort maxArray if not already sorted
327- for ( const maxEntry of maxData . data ) {
328- const dateSeconds = new Date ( maxEntry . timestamp ) . getTime ( ) ;
329- const maxCount = maxEntry . count ! ;
330-
331- // If this date hasn't been encountered before, initialize it with the null template
332- if ( ! dateToBrowserDataMap . has ( dateSeconds ) ) {
333- dateToBrowserDataMap . set ( dateSeconds , { ...browserNullTemplate } ) ; // Use spread to create a copy
304+ if ( ! dateToDataMap . has ( dateSeconds ) ) {
305+ dateToDataMap . set ( dateSeconds , { } ) ;
334306 }
335- const browserCounts = dateToBrowserDataMap . get ( dateSeconds ) ! ;
336- browserCounts [ maxData . label ] = maxCount ; // Add the max count
307+ const dateData = dateToDataMap . get ( dateSeconds ) ! ;
308+ dateData [ metricData . label ] = dataValue || null ;
337309 }
338310 }
339311
340- // Create array of dateToBrowserDataMap entries and sort by dateSeconds
341- const data = Array . from ( dateToBrowserDataMap . entries ( ) ) . sort (
312+ const data = Array . from ( dateToDataMap . entries ( ) ) . sort (
342313 ( [ d1 ] , [ d2 ] ) => d1 - d2 ,
343314 ) ;
344315
345- // For each date, add a row to the dataTable
346- for ( const datum of data ) {
347- const dateSeconds = datum [ 0 ] ;
316+ for ( const [ dateSeconds , dateData ] of data ) {
348317 const date = new Date ( dateSeconds ) ;
349- const browserCounts = datum [ 1 ] ;
318+ const row : [ Date , ... Array < number | string | null > ] = [ date ] ;
350319
351- const row : [ Date , ...Array < number | string | null > ] = [ date ] ; // Start the row with the date
352-
353- // Make an array of browser counts, in the order of browsers.
354- // If the browser is not in the browserCounts, add null.
355- browsers . forEach ( browser => {
356- row . push ( browserCounts [ browser ] || null ) ;
357- } ) ;
358-
359- if ( maxData ) {
360- row . push ( browserCounts [ maxData . label ] || null ) ; // Add max count (or null if missing)
320+ for ( const metricData of metricDataArray ) {
321+ row . push (
322+ dateData [ metricData . label ] ? dateData [ metricData . label ] : null ,
323+ ) ;
361324 }
362-
363325 dataObj . rows . push ( row ) ;
364326 }
327+
365328 return dataObj ;
366329 }
367330
0 commit comments