diff options
Diffstat (limited to 'app/assets/javascripts/graphs')
5 files changed, 351 insertions, 0 deletions
diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee new file mode 100644 index 00000000000..e0f681acf0b --- /dev/null +++ b/app/assets/javascripts/graphs/application.js.coffee @@ -0,0 +1,7 @@ +# This is a manifest file that'll be compiled into including all the files listed below. +# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically +# be included in the compiled file accessible from http://example.com/assets/application.js +# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +# the compiled file. +# +#= require_tree . diff --git a/app/assets/javascripts/graphs/stat_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph.js.coffee new file mode 100644 index 00000000000..f36c71fd25e --- /dev/null +++ b/app/assets/javascripts/graphs/stat_graph.js.coffee @@ -0,0 +1,6 @@ +class @StatGraph + @log: {} + @get_log: -> + @log + @set_log: (data) -> + @log = data diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors.js.coffee new file mode 100644 index 00000000000..1d9fae7cf79 --- /dev/null +++ b/app/assets/javascripts/graphs/stat_graph_contributors.js.coffee @@ -0,0 +1,71 @@ +#= require d3 + +class @ContributorsStatGraph + init: (log) -> + @parsed_log = ContributorsStatGraphUtil.parse_log(log) + @set_current_field("commits") + total_commits = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field) + author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field) + @add_master_graph(total_commits) + @add_authors_graph(author_commits) + @change_date_header() + add_master_graph: (total_data) -> + @master_graph = new ContributorsMasterGraph(total_data) + @master_graph.draw() + add_authors_graph: (author_data) -> + @authors = [] + limited_author_data = author_data.slice(0, 100) + _.each(limited_author_data, (d) => + author_header = @create_author_header(d) + $(".contributors-list").append(author_header) + @authors[d.author_name] = author_graph = new ContributorsAuthorGraph(d.dates) + author_graph.draw() + ) + format_author_commit_info: (author) -> + commits = $('<span/>', { + class: 'graph-author-commits-count' + }) + commits.text(author.commits + " commits") + $('<span/>').append(commits) + + create_author_header: (author) -> + list_item = $('<li/>', { + class: 'person' + style: 'display: block;' + }) + author_name = $('<h4>' + author.author_name + '</h4>') + author_email = $('<p class="graph-author-email">' + author.author_email + '</p>') + author_commit_info_span = $('<span/>', { + class: 'commits' + }) + author_commit_info = @format_author_commit_info(author) + author_commit_info_span.html(author_commit_info) + list_item.append(author_name) + list_item.append(author_email) + list_item.append(author_commit_info_span) + list_item + redraw_master: -> + total_data = ContributorsStatGraphUtil.get_total_data(@parsed_log, @field) + @master_graph.set_data(total_data) + @master_graph.redraw() + redraw_authors: -> + $("ol").html("") + x_domain = ContributorsGraph.prototype.x_domain + author_commits = ContributorsStatGraphUtil.get_author_data(@parsed_log, @field, x_domain) + _.each(author_commits, (d) => + @redraw_author_commit_info(d) + $(@authors[d.author_name].list_item).appendTo("ol") + @authors[d.author_name].set_data(d.dates) + @authors[d.author_name].redraw() + ) + set_current_field: (field) -> + @field = field + change_date_header: -> + x_domain = ContributorsGraph.prototype.x_domain + print_date_format = d3.time.format("%B %e %Y") + print = print_date_format(x_domain[0]) + " - " + print_date_format(x_domain[1]) + $("#date_header").text(print) + redraw_author_commit_info: (author) -> + author_list_item = $(@authors[author.author_name].list_item) + author_commit_info = @format_author_commit_info(author) + author_list_item.find("span").html(author_commit_info) diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee new file mode 100644 index 00000000000..584d281a510 --- /dev/null +++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee @@ -0,0 +1,169 @@ +#= require d3 + +class @ContributorsGraph + MARGIN: + top: 20 + right: 20 + bottom: 30 + left: 50 + x_domain: null + y_domain: null + dates: [] + @set_x_domain: (data) => + @prototype.x_domain = data + @set_y_domain: (data) => + @prototype.y_domain = [0, d3.max(data, (d) -> + d.commits = d.commits ? d.additions ? d.deletions + )] + @init_x_domain: (data) => + @prototype.x_domain = d3.extent(data, (d) -> + d.date + ) + @init_y_domain: (data) => + @prototype.y_domain = [0, d3.max(data, (d) -> + d.commits = d.commits ? d.additions ? d.deletions + )] + @init_domain: (data) => + @init_x_domain(data) + @init_y_domain(data) + @set_dates: (data) => + @prototype.dates = data + set_x_domain: -> + @x.domain(@x_domain) + set_y_domain: -> + @y.domain(@y_domain) + set_domain: -> + @set_x_domain() + @set_y_domain() + create_scale: (width, height) -> + @x = d3.time.scale().range([0, width]).clamp(true) + @y = d3.scale.linear().range([height, 0]).nice() + draw_x_axis: -> + @svg.append("g").attr("class", "x axis").attr("transform", "translate(0, #{@height})") + .call(@x_axis) + draw_y_axis: -> + @svg.append("g").attr("class", "y axis").call(@y_axis) + set_data: (data) -> + @data = data + +class @ContributorsMasterGraph extends ContributorsGraph + constructor: (@data) -> + @width = $('.content').width() - 70 + @height = 200 + @x = null + @y = null + @x_axis = null + @y_axis = null + @area = null + @svg = null + @brush = null + @x_max_domain = null + process_dates: (data) -> + dates = @get_dates(data) + @parse_dates(data) + ContributorsGraph.set_dates(dates) + get_dates: (data) -> + _.pluck(data, 'date') + parse_dates: (data) -> + parseDate = d3.time.format("%Y-%m-%d").parse + data.forEach((d) -> + d.date = parseDate(d.date) + ) + create_scale: -> + super @width, @height + create_axes: -> + @x_axis = d3.svg.axis().scale(@x).orient("bottom") + @y_axis = d3.svg.axis().scale(@y).orient("left").ticks(5) + create_svg: -> + @svg = d3.select("#contributors-master").append("svg") + .attr("width", @width + @MARGIN.left + @MARGIN.right) + .attr("height", @height + @MARGIN.top + @MARGIN.bottom) + .attr("class", "tint-box") + .append("g") + .attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")") + create_area: (x, y) -> + @area = d3.svg.area().x((d) -> + x(d.date) + ).y0(@height).y1((d) -> + xa = d.commits = d.commits ? d.additions ? d.deletions + y(xa) + ).interpolate("basis") + create_brush: -> + @brush = d3.svg.brush().x(@x).on("brushend", @update_content) + draw_path: (data) -> + @svg.append("path").datum(data).attr("class", "area").attr("d", @area) + add_brush: -> + @svg.append("g").attr("class", "selection").call(@brush).selectAll("rect").attr("height", @height) + update_content: => + ContributorsGraph.set_x_domain(if @brush.empty() then @x_max_domain else @brush.extent()) + $("#brush_change").trigger('change') + draw: -> + @process_dates(@data) + @create_scale() + @create_axes() + ContributorsGraph.init_domain(@data) + @x_max_domain = @x_domain + @set_domain() + @create_area(@x, @y) + @create_svg() + @create_brush() + @draw_path(@data) + @draw_x_axis() + @draw_y_axis() + @add_brush() + redraw: -> + @process_dates(@data) + ContributorsGraph.set_y_domain(@data) + @set_y_domain() + @svg.select("path").datum(@data) + @svg.select("path").attr("d", @area) + @svg.select(".y.axis").call(@y_axis) + +class @ContributorsAuthorGraph extends ContributorsGraph + constructor: (@data) -> + @width = $('.content').width()/2 - 100 + @height = 200 + @x = null + @y = null + @x_axis = null + @y_axis = null + @area = null + @svg = null + @list_item = null + create_scale: -> + super @width, @height + create_axes: -> + @x_axis = d3.svg.axis().scale(@x).orient("bottom").ticks(8) + @y_axis = d3.svg.axis().scale(@y).orient("left").ticks(5) + create_area: (x, y) -> + @area = d3.svg.area().x((d) -> + parseDate = d3.time.format("%Y-%m-%d").parse + x(parseDate(d)) + ).y0(@height).y1((d) => + if @data[d]? then y(@data[d]) else y(0) + ).interpolate("basis") + create_svg: -> + @list_item = d3.selectAll(".person")[0].pop() + @svg = d3.select(@list_item).append("svg") + .attr("width", @width + @MARGIN.left + @MARGIN.right) + .attr("height", @height + @MARGIN.top + @MARGIN.bottom) + .attr("class", "spark") + .append("g") + .attr("transform", "translate(" + @MARGIN.left + "," + @MARGIN.top + ")") + draw_path: (data) -> + @svg.append("path").datum(data).attr("class", "area-contributor").attr("d", @area) + draw: -> + @create_scale() + @create_axes() + @set_domain() + @create_area(@x, @y) + @create_svg() + @draw_path(@dates) + @draw_x_axis() + @draw_y_axis() + redraw: -> + @set_domain() + @svg.select("path").datum(@dates) + @svg.select("path").attr("d", @area) + @svg.select(".x.axis").call(@x_axis) + @svg.select(".y.axis").call(@y_axis) diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_util.js.coffee new file mode 100644 index 00000000000..31617c88b4a --- /dev/null +++ b/app/assets/javascripts/graphs/stat_graph_contributors_util.js.coffee @@ -0,0 +1,98 @@ +window.ContributorsStatGraphUtil = + parse_log: (log) -> + total = {} + by_author = {} + by_email = {} + for entry in log + @add_date(entry.date, total) unless total[entry.date]? + + data = by_author[entry.author_name] || by_email[entry.author_email] + data ?= @add_author(entry, by_author, by_email) + + @add_date(entry.date, data) unless data[entry.date] + @store_data(entry, total[entry.date], data[entry.date]) + total = _.toArray(total) + by_author = _.toArray(by_author) + total: total, by_author: by_author + + add_date: (date, collection) -> + collection[date] = {} + collection[date].date = date + + add_author: (author, by_author, by_email) -> + data = {} + data.author_name = author.author_name + data.author_email = author.author_email + by_author[author.author_name] = data + by_email[author.author_email] = data + + store_data: (entry, total, by_author) -> + @store_commits(total, by_author) + @store_additions(entry, total, by_author) + @store_deletions(entry, total, by_author) + + store_commits: (total, by_author) -> + @add(total, "commits", 1) + @add(by_author, "commits", 1) + + add: (collection, field, value) -> + collection[field] ?= 0 + collection[field] += value + + store_additions: (entry, total, by_author) -> + entry.additions ?= 0 + @add(total, "additions", entry.additions) + @add(by_author, "additions", entry.additions) + + store_deletions: (entry, total, by_author) -> + entry.deletions ?= 0 + @add(total, "deletions", entry.deletions) + @add(by_author, "deletions", entry.deletions) + + get_total_data: (parsed_log, field) -> + log = parsed_log.total + total_data = @pick_field(log, field) + _.sortBy(total_data, (d) -> + d.date + ) + pick_field: (log, field) -> + total_data = [] + _.each(log, (d) -> + total_data.push(_.pick(d, [field, 'date'])) + ) + total_data + + get_author_data: (parsed_log, field, date_range = null) -> + log = parsed_log.by_author + author_data = [] + + _.each(log, (log_entry) => + parsed_log_entry = @parse_log_entry(log_entry, field, date_range) + if not _.isEmpty(parsed_log_entry.dates) + author_data.push(parsed_log_entry) + ) + + _.sortBy(author_data, (d) -> + d[field] + ).reverse() + + parse_log_entry: (log_entry, field, date_range) -> + parsed_entry = {} + parsed_entry.author_name = log_entry.author_name + parsed_entry.author_email = log_entry.author_email + parsed_entry.dates = {} + parsed_entry.commits = parsed_entry.additions = parsed_entry.deletions = 0 + _.each(_.omit(log_entry, 'author_name', 'author_email'), (value, key) => + if @in_range(value.date, date_range) + parsed_entry.dates[value.date] = value[field] + parsed_entry.commits += value.commits + parsed_entry.additions += value.additions + parsed_entry.deletions += value.deletions + ) + return parsed_entry + + in_range: (date, date_range) -> + if date_range is null || date_range[0] <= new Date(date) <= date_range[1] + true + else + false |
