script.js 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. // menu transition
  2. $("#leftside-navigation .sub-menu > a").click(function(e) {
  3. $("#leftside-navigation ul ul").slideUp(), $(this).next().is(":visible") || $(this).next().slideDown(),
  4. e.stopPropagation()
  5. });
  6. // heatmap SVG
  7. const margin = { top: 50, right: 0, bottom: 100, left: 30 },
  8. width =520 - margin.left - margin.right,
  9. height = 320 - margin.top - margin.bottom,
  10. gridSize = Math.floor(width / 24),
  11. legendElementWidth = gridSize*2,
  12. buckets = 9,
  13. // blue colors
  14. // colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9]
  15. // orange colors
  16. // colors = ['#fff5eb','#fee6ce','#fdd0a2','#fdae6b','#fd8d3c','#f16913','#d94801','#a63603','#7f2704'],
  17. // yellow colors
  18. colors =["#ECE622","#D5CF22","#BEB922","#A7A222","#908C22","#797522","#625F22","#625F22","#4B4822","#343222"],
  19. days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
  20. times = ["1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12a", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p", "12p"];
  21. datasets = ["../static/data/data_dummy.tsv", "../static/data/data2_dummy.tsv"];
  22. const svg = d3.select("#chart").append("svg")
  23. .attr("width", width + margin.left + margin.right)
  24. .attr("height", height + margin.top + margin.bottom)
  25. .append("g")
  26. .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  27. const dayLabels = svg.selectAll(".dayLabel")
  28. .data(days)
  29. .enter().append("text")
  30. .text(function (d) { return d; })
  31. .attr("x", 0)
  32. .attr("y", (d, i) => i * gridSize)
  33. .style("text-anchor", "end")
  34. .attr("transform", "translate(-6," + gridSize / 1.5 + ")")
  35. .attr("class", (d, i) => ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis"));
  36. const timeLabels = svg.selectAll(".timeLabel")
  37. .data(times)
  38. .enter().append("text")
  39. .text((d) => d)
  40. .attr("x", (d, i) => i * gridSize)
  41. .attr("y", 0)
  42. .style("text-anchor", "middle")
  43. .attr("transform", "translate(" + gridSize / 2 + ", -6)")
  44. .attr("class", (d, i) => ((i >= 7 && i <= 16) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"));
  45. const type = (d) => {
  46. return {
  47. day: +d.day,
  48. hour: +d.hour,
  49. value: +d.value
  50. };
  51. };
  52. const heatmapChart = function(tsvFile) {
  53. d3.tsv(tsvFile, type, (error, data) => {
  54. var div = d3.select("body").append("div")
  55. .attr("class", "tooltip")
  56. .style("opacity", 0);
  57. const colorScale = d3.scaleQuantile()
  58. .domain([0, buckets - 1, d3.max(data, (d) => d.value)])
  59. .range(colors);
  60. const cards = svg.selectAll(".hour")
  61. .data(data, (d) => d.day+':'+d.hour);
  62. cards.append("title");
  63. cards.enter().append("rect")
  64. .attr("x", (d) => (d.hour - 1) * gridSize)
  65. .attr("y", (d) => (d.day - 1) * gridSize)
  66. .attr("rx", 4)
  67. .attr("ry", 4)
  68. .attr("class", "hour bordered")
  69. .attr("width", gridSize)
  70. .attr("height", gridSize)
  71. .style("fill", colors[0])
  72. .on("mouseover", function(d) {
  73. div.transition()
  74. .duration(200)
  75. .style("opacity", .9);
  76. div.html("Day: " +days[d.day-1]+ "<br>Hour: " +times[d.hour-1]+ "<br>Commits: " +d.value+ "")
  77. .style("left", (d3.event.pageX + 20) + "px")
  78. .style("top", (d3.event.pageY - 20) + "px");
  79. })
  80. .on("mouseout", function(d) {
  81. div.transition()
  82. .duration(500)
  83. .style("opacity", 0);
  84. })
  85. .merge(cards)
  86. .transition()
  87. .duration(1000)
  88. .style("fill", (d) => colorScale(d.value));
  89. // cards.select("title").text((d) => d.value);
  90. cards.exit().remove();
  91. const legend = svg.selectAll(".legend")
  92. .data([0].concat(colorScale.quantiles()), (d) => d);
  93. const legend_g = legend.enter().append("g")
  94. .attr("class", "legend");
  95. legend_g.append("rect")
  96. .attr("x", (d, i) => legendElementWidth * i)
  97. .attr("y", height)
  98. .attr("width", legendElementWidth)
  99. .attr("height", gridSize / 2)
  100. .style("fill", (d, i) => colors[i]);
  101. legend_g.append("text")
  102. .attr("class", "mono")
  103. .text((d) => "≥ " + Math.round(d))
  104. .attr("x", (d, i) => legendElementWidth * i)
  105. .attr("y", height + gridSize);
  106. legend.exit().remove();
  107. });
  108. };
  109. // use first dataset by default
  110. heatmapChart(datasets[0]);
  111. // dataset buttons
  112. const datasetpicker = d3.select("#dataset-picker")
  113. .selectAll(".btn btn-primary")
  114. .data(datasets);
  115. // dataset picking button
  116. datasetpicker.enter()
  117. .append("input")
  118. .attr("value", (d) => "Dataset " + d)
  119. .attr("type", "button")
  120. .attr("class", "btn-sm btn-primary")
  121. .on("click", (d) => heatmapChart(d));
  122. generateBarChart("../static/data/day_of_week_copy.tsv", "#day_of_week");
  123. function generateBarChart (pathToTSV,divID){
  124. // Bar chart, see function 'main' for main execution wheel
  125. function toNum(d){
  126. //cleaner function
  127. d.commits= +d.commits
  128. return d
  129. };
  130. d3.tsv(pathToTSV, toNum, function (error,data){
  131. "use strict"
  132. // declare outside for reference later
  133. var width=520
  134. var height=380
  135. var chartWidth, chartHeight
  136. var svg = d3.select(divID).append("svg")
  137. var axisLayer = svg.append("g").classed("axisLayer", true)
  138. var chartLayer = svg.append("g").classed("chartLayer", true)
  139. var xScale = d3.scaleBand()
  140. var yScale = d3.scaleLinear()
  141. var div = d3.select("body").append("div")
  142. .attr("class", "rect_tooltip")
  143. .style("opacity", 0);
  144. function main(data) {
  145. setSize();
  146. drawAxisBarChart();
  147. drawChartBarChart();
  148. }
  149. function setSize() {
  150. var margin = {top:50, right:0, bottom:100, left:30}
  151. chartWidth = width - margin.left - margin.right,
  152. chartHeight = height - margin.top - margin.bottom,
  153. svg
  154. .attr("width", 520)
  155. .attr("height", 320)
  156. axisLayer
  157. .attr("width", chartWidth)
  158. .attr("height", chartHeight)
  159. chartLayer
  160. .attr("width", chartWidth)
  161. .attr("height", chartHeight)
  162. .attr("transform", "translate("+[margin.left, margin.top]+")")
  163. xScale.domain(data.map(function(d){ return d.day_name })).range([0, chartWidth])
  164. .paddingInner(0.1)
  165. .paddingOuter(0.5)
  166. yScale.domain([0, d3.max(data, function(d){ return d.commits})]).range([chartHeight, 0])
  167. }
  168. function drawChartBarChart() {
  169. // monitor the transition
  170. var t = d3.transition()
  171. .duration(1100)
  172. .ease(d3.easeLinear)
  173. .on("start", function(d){ console.log("Bar Chart Transiton start") })
  174. .on("end", function(d){ console.log("Bar Chart Transiton end") })
  175. var bar = chartLayer
  176. .selectAll(".bar")
  177. .data(data)
  178. bar.exit().remove()
  179. bar
  180. .enter()
  181. .append("rect")
  182. .classed("bar", true)
  183. .merge(bar)
  184. .attr("fill", "rgb(236, 230, 34)")
  185. .attr("width", xScale.bandwidth())
  186. .attr("stroke", "#323232")
  187. //setup for cool transition
  188. .attr("height", 0)
  189. .attr("transform", function(d){ return "translate("+[xScale(d.day_name), chartHeight]+")"})
  190. var labels = chartLayer
  191. .selectAll("labels")
  192. .data(data)
  193. //setup for percentage display
  194. var totalCommits=0
  195. data.forEach(function(d){
  196. totalCommits+=d.commits
  197. })
  198. labels
  199. .enter()
  200. .append("text")
  201. .text(function(d){
  202. var percentage= (d.commits/totalCommits *100).toFixed(2)
  203. return ""+percentage+"%";
  204. })
  205. .attr("transform", function(d){
  206. return "translate("+[xScale(d.day_name)+5, chartHeight-5]+")"
  207. })
  208. chartLayer.selectAll(".bar").transition(t)
  209. // grows to appropriate amount
  210. .attr("height", function(d){ return chartHeight - yScale(d.commits) })
  211. .attr("transform", function(d){ return "translate("+[xScale(d.day_name), yScale(d.commits)]+")"})
  212. }
  213. function drawAxisBarChart(){
  214. var yAxis = d3.axisLeft(yScale)
  215. .tickSizeInner(-chartWidth)
  216. axisLayer.append("g")
  217. .attr("transform", "translate("+[margin.left, margin.top]+")")
  218. .attr("class", "axis y")
  219. .call(yAxis);
  220. var xAxis = d3.axisBottom(xScale)
  221. axisLayer.append("g")
  222. .attr("class", "axis x")
  223. .attr("transform", "translate("+[margin.left, (height-margin.bottom)]+")")
  224. .call(xAxis);
  225. }
  226. //kicks of execution of the bar chart
  227. main();
  228. }); //end of tsv read in
  229. } //end of generateBarChart
  230. generateLineChart("../static/data/commits_by_author_copy.tsv", "#lineChart");
  231. generateLineChart("../static/data/lines_of_code_by_author_copy.tsv", "#lineChart2");
  232. function generateLineChart(pathToTSV, divID){
  233. function allToNumber(d){
  234. //converts everything to Number type
  235. for (var key in d){
  236. d[key] = +d[key]
  237. }
  238. //fix for Unix timestamps
  239. d.date = new Date(d.date * 1000)
  240. return d;
  241. }
  242. d3.tsv(pathToTSV, allToNumber, function (error,data){
  243. if (error) throw error;
  244. var authors = data.columns.slice(1).map(function(id) {
  245. return {
  246. id: id,
  247. values: data.map(function(d) {
  248. return {date: d.date, commits: d[id]};
  249. })
  250. };
  251. });
  252. var svgWidth=1200,
  253. svgHeight=400,
  254. chartWidth= 1000;
  255. var svg = d3.select(divID).append("svg").attr("height", svgHeight).attr("width", svgWidth),
  256. margin = {top: 20, right: 80, bottom: 30, left: 50},
  257. //this is smaller than svgWidth to accomodate Legend
  258. width = chartWidth - margin.left - margin.right,
  259. height = svgHeight - margin.top - margin.bottom,
  260. //dynamically builds grid
  261. numberGridLines=30
  262. //main chart container
  263. g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  264. var xScale = d3.scaleTime().range([0, width]),
  265. yScale = d3.scaleLinear().range([height, 0]),
  266. // 10 nice colors
  267. colors = d3.scaleOrdinal(d3.schemeCategory10);
  268. xScale.domain(d3.extent(data, function(d) { return d.date; }));
  269. yScale.domain([
  270. d3.min(authors, function(c) { return d3.min(c.values, function(d) { return d.commits; }); }),
  271. d3.max(authors, function(c) { return d3.max(c.values, function(d) { return d.commits; }); })
  272. ]);
  273. var line = d3.line()
  274. // different types of interpolations here
  275. // https://bl.ocks.org/d3noob/ced1b9b18bd8192d2c898884033b5529
  276. .curve(d3.curveBasis)
  277. .x(function(d) { return xScale(d.date); })
  278. .y(function(d) { return yScale(d.commits);});
  279. colors.domain(authors.map(function(c) { return c.id; }));
  280. function main(){
  281. drawAxisLineChart();
  282. drawChartLineChart();
  283. drawLegend();
  284. }
  285. // gridlines in x axis
  286. function make_x_gridlines() {
  287. return d3.axisBottom(xScale)
  288. .ticks(numberGridLines)
  289. }
  290. // gridlines in y axis
  291. function make_y_gridlines() {
  292. return d3.axisLeft(yScale)
  293. .ticks(numberGridLines)
  294. }
  295. function drawAxisLineChart(){
  296. g.append("g")
  297. .attr("class", "axis axis--x")
  298. .attr("transform", "translate(0," + height + ")")
  299. .call(d3.axisBottom(xScale));
  300. g.append("g")
  301. .attr("class", "axis axis--y")
  302. .call(d3.axisLeft(yScale))
  303. // .append("text")
  304. // .attr("transform", "rotate(-90)")
  305. // .attr("y", 6)
  306. // .attr("dy", "0.71em")
  307. // .attr("fill", "#000")
  308. // add the X gridlines
  309. g.append("g")
  310. .attr("class", "grid")
  311. .attr("transform", "translate(0," + height + ")")
  312. .call(make_x_gridlines()
  313. .tickSize(-height)
  314. .tickFormat("")
  315. );
  316. // add the Y gridlines
  317. g.append("g")
  318. .attr("class", "grid")
  319. .call(make_y_gridlines()
  320. .tickSize(-width)
  321. .tickFormat("")
  322. );
  323. }
  324. function drawChartLineChart() {
  325. var author = g.selectAll(".author")
  326. .data(authors)
  327. .enter().append("g")
  328. .attr("class", "author");
  329. author.append("path")
  330. .attr("class", "line")
  331. .attr("d", function(d) { return line(d.values); })
  332. .style("stroke", function(d) { return colors(d.id); });
  333. // Enable to show author at the end of the line
  334. // author.append("text")
  335. // .datum(function(d) { return {id: d.id, value: d.values[d.values.length - 1]}; })
  336. // .attr("transform", function(d) { return "translate(" + xScale(d.value.date) + "," + yScale(d.value.commits) + ")"; })
  337. // .attr("x", 3)
  338. // .attr("dy", "0.35em")
  339. // .style("font", "10px sans-serif")
  340. // .text(function(d) { return d.id; });
  341. }
  342. function drawLegend(){
  343. var legend= svg.selectAll(".legend-item")
  344. .data(authors)
  345. .enter().append("g")
  346. .attr("class","legend");
  347. legend.append("rect")
  348. .attr('x', chartWidth - 20)
  349. .attr('y', function(d, i) {
  350. return (i * 35) + 20;
  351. })
  352. .attr('width', 10)
  353. .attr('height', 10)
  354. .style('fill', function(d) {
  355. return colors(d.id);
  356. });
  357. legend.append('text')
  358. .attr('x', chartWidth - 8)
  359. .attr('y', function(d, i) {
  360. return (i * 35) + 29;
  361. })
  362. .text(function(d) {
  363. return d.id;
  364. });
  365. }
  366. main(); //kicks off main execution wheel
  367. }); //end tsv read in
  368. }; //end generate line chart