script.js 14KB

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