mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 12:08:41 -06:00
283 lines
10 KiB
TypeScript
283 lines
10 KiB
TypeScript
///<reference path="d3.d.ts" />
|
|
"use strict";
|
|
|
|
interface IDataSeries {
|
|
desc: string;
|
|
data: IRun[];
|
|
}
|
|
|
|
interface IRun {
|
|
date: Date;
|
|
pass: boolean;
|
|
}
|
|
|
|
interface IPerfDataSeries {
|
|
desc: string;
|
|
data: IPerfRun[];
|
|
}
|
|
|
|
interface IPerfRun {
|
|
x: Date;
|
|
y: number;
|
|
}
|
|
|
|
module Chart {
|
|
|
|
export class Base {
|
|
public iso8601 = d3.time.format('%Y-%m-%d');
|
|
public chartWidth = 800;
|
|
|
|
constructor (public element) { }
|
|
}
|
|
|
|
export class Bar extends Base {
|
|
public element: ID3Selection;
|
|
constructor(element: ID3Selection) {
|
|
super(element);
|
|
this.element = element;
|
|
}
|
|
|
|
public chartHeight = 400;
|
|
public chartWidth = 800;
|
|
public legendItemHeight = 30;
|
|
public legendWidth = 150;
|
|
public colors = ['rgb(0, 113, 188)', 'rgb(0, 174, 239)', 'rgb(145, 0, 145)'];
|
|
public xAxisHashHeight = 10;
|
|
public layout = 'wiggle';
|
|
|
|
|
|
public render(data: IPerfDataSeries[]) {
|
|
|
|
// Create stack layout
|
|
var stackLayout = d3.layout.stack()
|
|
.values(function(d) { return d.data })
|
|
.offset(this.layout);
|
|
|
|
var stackData = stackLayout(data);
|
|
|
|
// Maximum measurement in the dataset
|
|
var maxY = d3.max(stackData, (d) => d3.max<any, any>(d.data, (d) => d.y0 + d.y));
|
|
|
|
// Earliest day in the dataset
|
|
var minX = d3.min(data, (d) => d3.min(d.data, (d) => d.x));
|
|
|
|
// All days in the dataset (from earliest day until now)
|
|
var days = d3.time.days(minX, new Date());
|
|
|
|
// Area of the region containing the bars
|
|
var areaWidth = this.chartWidth - this.legendWidth;
|
|
|
|
var barWidth = areaWidth / days.length;
|
|
|
|
// Create scales for X and Y axis (X based on dates, Y based on performance data)
|
|
var x = d3.time.scale()
|
|
.domain([minX, d3.time.day(d3.time.day.offset(new Date(), 1))])
|
|
.range([0, this.chartWidth - this.legendWidth]);
|
|
var y = d3.scale.linear()
|
|
.domain([0, maxY])
|
|
.range([0, this.chartHeight]);
|
|
var ticks = x.ticks(d3.time.mondays, 1);
|
|
|
|
// SVG element
|
|
var svg = this.element.append('svg')
|
|
.attr('height', this.chartHeight + 25)
|
|
.attr('width', this.chartWidth);
|
|
|
|
// Groups that contain bar segments for each dataset
|
|
var barGroups = svg.selectAll('g.bars')
|
|
.data(stackData)
|
|
.enter().append('g')
|
|
.attr('class', 'bars')
|
|
.style('fill', (d, i) => this.colors[<number>i])
|
|
.attr('transform', 'translate(' + this.legendWidth + ', 0)');
|
|
|
|
// Legend
|
|
var legendGroup = svg.append('g')
|
|
.attr('class', 'legend')
|
|
|
|
// Legend items
|
|
var legendItem = legendGroup.selectAll('g.legendItem')
|
|
.data(stackData)
|
|
.enter().append('g')
|
|
.attr('class', 'legendItem')
|
|
.style('fill', (d, i) => this.colors[<number>i])
|
|
.attr('transform', (d, i) => 'translate(0, ' + (this.legendItemHeight * (2 - i)) + ')');
|
|
|
|
legendItem.append('rect')
|
|
.attr('width', 25)
|
|
.attr('height', 25);
|
|
|
|
legendItem.append('text')
|
|
.text((d) => d.desc)
|
|
.attr('x', 30)
|
|
.attr('dy', '1em');
|
|
|
|
|
|
// Bars
|
|
var rects = barGroups.selectAll('rect')
|
|
.data((d) => d.data)
|
|
.enter()
|
|
.append('rect')
|
|
.attr('x', (d, i) => x(d.x))
|
|
.attr('y', (d, i) => this.chartHeight - y(d.y + d.y0))
|
|
.attr('width', barWidth)
|
|
.attr('height', (d, i) => y(d.y));
|
|
|
|
// Add title (mouseover popup) to bars
|
|
rects.append('title')
|
|
.text((d) => this.iso8601(d.x) + ' - ' + d.y + 'ms');
|
|
|
|
|
|
// Add an axis marker to the bottom
|
|
var axis = d3.svg.axis();
|
|
axis.scale(x)
|
|
.ticks(d3.time.mondays, 1)
|
|
.tickSubdivide(6)
|
|
.tickFormat(this.iso8601)
|
|
.tickSize(10, 5, 0);
|
|
|
|
var axisGroup = svg.append('g')
|
|
.attr('class', 'axis')
|
|
.attr('transform', 'translate(' + this.legendWidth + ',' + this.chartHeight + ')')
|
|
.call(axis);
|
|
|
|
}
|
|
}
|
|
|
|
export class DailyBuild extends Base {
|
|
public element: ID3Selection;
|
|
constructor (element: ID3Selection) {
|
|
super(element);
|
|
this.element = element;
|
|
}
|
|
|
|
public labelWidth = 140;
|
|
public labelGutter = 10;
|
|
public chartWidth = 800;
|
|
|
|
private getTextDataString(data) {
|
|
return data.pass ? 'pass' : 'fail';
|
|
}
|
|
|
|
public render(data: IDataSeries[]) {
|
|
// Minimum date in the dataset
|
|
var minDate = d3.min(data.map(function(d) { return d3.time.day(d.data[0].date) }));
|
|
|
|
// All the days in the dataset, from min until now.
|
|
var days = d3.time.days(minDate, new Date());
|
|
|
|
var boxSize = (this.chartWidth - this.labelWidth - this.labelGutter) / days.length;
|
|
|
|
// Create our scales for x and y axis
|
|
var x = d3.time.scale()
|
|
.domain([minDate, d3.time.day(new Date())])
|
|
.range([0, boxSize * (days.length - 1)]);
|
|
|
|
var y = d3.scale.linear()
|
|
.domain([0, 1])
|
|
.range([0, boxSize]);
|
|
|
|
|
|
// SVG element
|
|
var svg = this.element.append('svg')
|
|
.attr('height', y(data.length + 1))
|
|
.attr('width', this.chartWidth);
|
|
|
|
svg.selectAll('text')
|
|
.data(data)
|
|
.enter().append('text')
|
|
.attr('x', this.labelWidth)
|
|
.attr('transform', function(d, i) { return 'translate(0,' + y(i) + ')' })
|
|
.attr('dy', '1em')
|
|
.attr('text-anchor', 'end')
|
|
.text(function(d) { return d.desc });
|
|
|
|
// Groups of boxes for updated builds
|
|
var g = svg.selectAll('g.boxes')
|
|
.data(data)
|
|
.enter().append('g')
|
|
.attr('class', 'boxes')
|
|
.attr('transform', function(d, i) { return 'translate(0,' + y(i) + ')' });
|
|
|
|
// Boxes of build info
|
|
var rects = g.selectAll('rect')
|
|
.data(function(d, i) { return d.data; })
|
|
.enter().append('rect')
|
|
.attr('class', (d) => 'day ' + this.getTextDataString(d))
|
|
.attr('x', (d, i) => this.labelWidth + this.labelGutter + x(d.date))
|
|
.attr('width', boxSize)
|
|
.attr('height', boxSize);
|
|
|
|
rects.append('title')
|
|
.text( (d) => this.iso8601(d.date) + ' - ' + this.getTextDataString(d) );
|
|
|
|
var ticks = x.ticks(d3.time.mondays, 1);
|
|
|
|
// Date text boxes
|
|
svg.append('g').attr('class', 'dates').selectAll('text')
|
|
.data(ticks)
|
|
.enter().append('text')
|
|
.text((d) => this.iso8601(d) )
|
|
.attr('transform', (d, i) => 'translate(' + (this.labelWidth + this.labelGutter + x(d) + 5) + ', ' + y(data.length + 1) + ')')
|
|
.attr('text-anchor', 'start')
|
|
|
|
// Vertical hashes at week boundaries
|
|
svg.append('g').attr('class', 'hashes').selectAll('line')
|
|
.data(ticks)
|
|
.enter().append('line')
|
|
.attr('x1', (d) => this.labelWidth + this.labelGutter + x(d) )
|
|
.attr('x2', (d) => this.labelWidth + this.labelGutter + x(d) )
|
|
.attr('y1', 0)
|
|
.attr('y2', y(data.length + 1));
|
|
|
|
return svg;
|
|
}
|
|
}
|
|
}
|
|
|
|
var start = d3.time.day.offset(new Date(), -30);
|
|
var end = new Date()
|
|
var days = d3.time.days(start, end);
|
|
|
|
var buildData: IRun[] = days.map(day => ({ date: day, pass: Math.random() > 0.1 }));
|
|
var compilerTestData: IRun[] = days.map(day => ({ date: day, pass: Math.random() > 0.1 }));
|
|
var servicesTestData: IRun[] = days.map(day => ({ date: day, pass: Math.random() > 0.1 }));
|
|
|
|
function decreasingRandom(start: number, deviation: number, factor: number) {
|
|
var factorRandom = d3.random.normal(factor, 0.05);
|
|
|
|
return function () {
|
|
var random = d3.random.normal(start, deviation)();
|
|
start = start * factorRandom();
|
|
|
|
return parseFloat(random.toFixed())
|
|
}
|
|
}
|
|
|
|
var parseRandom = decreasingRandom(400, 20, 0.97);
|
|
var typecheckRandom = decreasingRandom(500, 20, 0.97);
|
|
var emitRandom = decreasingRandom(100, 10, 0.97);
|
|
|
|
var parseData: IPerfRun[] = days.map(day => ({ x: day, y: parseRandom() }));
|
|
var typecheckData: IPerfRun[] = days.map(day => ({ x: day, y: typecheckRandom() }));
|
|
var emitData: IPerfRun[] = days.map(day => ({ x: day, y: emitRandom() }));
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
var chart = new Chart.DailyBuild(d3.select('#passchart'));
|
|
|
|
chart.render([
|
|
{ desc: "Build Status", data: buildData },
|
|
{ desc: "Compiler Tests", data: compilerTestData },
|
|
{ desc: "Services Tests", data: servicesTestData },
|
|
]);
|
|
|
|
var normalizedData = [
|
|
{ desc: 'Emit', data: emitData },
|
|
{ desc: 'Typecheck', data: typecheckData },
|
|
{ desc: 'Parse', data: parseData }
|
|
]
|
|
|
|
var perfchart = new Chart.Bar(d3.select('#performanceChart'));
|
|
perfchart.render(normalizedData);
|
|
});
|