Commit 4b8154af authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'group-members-stats' into 'master'

Add group contribution statistics page
Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>


Implementation for gitlab-org/gitlab-ce#3824

See merge request !74
parents 9dd64994 221e3486
v 8.3.0 (unreleased) v 8.3.0 (unreleased)
- License information can now be retrieved via the API - License information can now be retrieved via the API
- Fix bug with negative approvals required - Fix bug with negative approvals required
- Add group contribution statistics page
v 8.2.3 v 8.2.3
- No EE-specific changes - No EE-specific changes
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#= require jquery.scrollTo #= require jquery.scrollTo
#= require jquery.blockUI #= require jquery.blockUI
#= require jquery.turbolinks #= require jquery.turbolinks
#= require jquery.tablesorter
#= require turbolinks #= require turbolinks
#= require autosave #= require autosave
#= require bootstrap #= require bootstrap
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
table { table {
&.table { &.table {
margin-bottom: $gl-padding; margin-bottom: $gl-padding;
.dropdown-menu a { .dropdown-menu a {
text-decoration: none; text-decoration: none;
} }
...@@ -35,6 +35,10 @@ table { ...@@ -35,6 +35,10 @@ table {
font-weight: normal; font-weight: normal;
font-size: 15px; font-size: 15px;
border-bottom: 1px solid $border-color !important; border-bottom: 1px solid $border-color !important;
&.sortable {
cursor: pointer;
}
} }
td { td {
......
class Groups::StatsController < Groups::ApplicationController
before_action :group
layout 'group'
def show
@users = @group.users
@start_date = params[:start_date] || Date.today - 1.week
@events = Event.contributions.
where("created_at > ?", @start_date).
where(project_id: @group.projects)
@stats = {}
@stats[:merge_requests] = @users.map do |user|
@events.merge_requests.created.where(author_id: user).count
end
@stats[:issues] = @users.map do |user|
@events.issues.closed.where(author_id: user).count
end
@stats[:push] = @users.map do |user|
@events.code_push.where(author_id: user).count
end
end
end
...@@ -50,6 +50,12 @@ class Event < ActiveRecord::Base ...@@ -50,6 +50,12 @@ class Event < ActiveRecord::Base
scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent } scope :in_projects, ->(project_ids) { where(project_id: project_ids).recent }
scope :with_associations, -> { includes(project: :namespace) } scope :with_associations, -> { includes(project: :namespace) }
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
scope :issues, -> { where(target_type: 'Issue') }
scope :merge_requests, -> { where(target_type: 'MergeRequest') }
scope :created, -> { where(action: CREATED) }
scope :closed, -> { where(action: CLOSED) }
scope :merged, -> { where(action: MERGED) }
class << self class << self
def reset_event_cache_for(target) def reset_event_cache_for(target)
......
- page_title "Statistics"
- header_title group_title(@group, "Statistics", group_stats_path(@group))
.gray-content-block
.pull-right
.dropdown.inline
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
= icon('calendar-o')
%b.caret
%ul.dropdown-menu
%li
= link_to group_stats_path(@group, start_date: Date.today - 1.week) do
Last week
%li
= link_to group_stats_path(@group, start_date: Date.today - 1.month) do
Last month
%li
= link_to group_stats_path(@group, start_date: Date.today - 3.months) do
Last 3 months
.oneline
Contribution statistics for issues, merge requests and push events since #{@start_date}
%h3 Push
.row
.col-md-4
%ul
%li
= @events.code_push.count
times
%li
more than
= @events.code_push.map(&:commits_count).sum
commits
%li
by
= pluralize @events.code_push.pluck(:author_id).uniq.count, 'person'
.col-md-8
%div
%p.light Push events per group member
%canvas#push{height: 250}
%h3 Merge Requests
.row
.col-md-4
%ul
%li
= @events.merge_requests.created.count
created
%li
= @events.merge_requests.merged.count
accepted
.col-md-8
%div
%p.light Merge requests created per group member
%canvas#merge_requests{height: 250}
%h3 Issues
.row
.col-md-4
%ul
%li
= @events.issues.created.count
created
%li
= @events.issues.closed.pluck(:target_id).uniq.count
closed
.col-md-8
%div
%p.light Issues closed per group member
%canvas#issues{height: 250}
.gray-content-block
.oneline
Contributions per group member
.table-holder
%table.table.sortable-table#event-stats
%thead
%tr
%th.sortable
Name
= icon('sort')
%th.sortable
Pushed
= icon('sort')
%th.sortable
Opened issues
= icon('sort')
%th.sortable
Closed issues
= icon('sort')
%th.sortable
Opened MR
= icon('sort')
%th.sortable
Accepted MR
= icon('sort')
%th.sortable
Total Contributions
= icon('sort')
%tbody
- @users.each do |user|
%tr
%td
%strong
= link_to user.name, user
%td= @events.code_push.where(author_id: user).count
%td= @events.issues.created.where(author_id: user).count
%td= @events.issues.closed.where(author_id: user).count
%td= @events.merge_requests.created.where(author_id: user).count
%td= @events.merge_requests.merged.where(author_id: user).count
%td= @events.where(author_id: user).count
- [:push, :issues, :merge_requests].each do |scope|
:javascript
var data = {
labels : #{@users.map(&:name).to_json},
datasets : [
{
fillColor : "rgba(220,220,220,0.5)",
strokeColor : "rgba(220,220,220,1)",
barStrokeWidth: 1,
barValueSpacing: 1,
barDatasetSpacing: 1,
data : #{@stats[scope].to_json}
}
]
}
var ctx = $("##{scope}").get(0).getContext("2d");
new Chart(ctx).Bar(data,{"scaleOverlay": true, responsive: true, maintainAspectRatio: false});
:javascript
$("#event-stats").tablesorter();
...@@ -38,6 +38,11 @@ ...@@ -38,6 +38,11 @@
= icon('users fw') = icon('users fw')
%span %span
Members Members
= nav_link(controller: [:stats]) do
= link_to group_stats_path(@group), title: 'Stats', data: {placement: 'right'} do
= icon('table fw')
%span
Statistics
- if can?(current_user, :admin_group, @group) - if can?(current_user, :admin_group, @group)
= nav_link(html_options: { class: "separate-item" }) do = nav_link(html_options: { class: "separate-item" }) do
= link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do = link_to edit_group_path(@group), title: 'Settings', data: {placement: 'right'} do
......
...@@ -386,6 +386,7 @@ Rails.application.routes.draw do ...@@ -386,6 +386,7 @@ Rails.application.routes.draw do
end end
scope module: :groups do scope module: :groups do
resource :stats, only: [:show]
resource :ldap, only: [] do resource :ldap, only: [] do
member do member do
put :reset_access put :reset_access
......
Feature: Group Statistics
Background:
Given I sign in as "John Doe"
And "John Doe" is owner of group "Owned"
Scenario: I should see group "Owned" statistics page
When I visit group "Owned" page
And I click on group statistics
Then I should see group statistics page
class Spinach::Features::GroupStatistics < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedGroup
include SharedUser
step 'I click on group statistics' do
click_link 'Statistics'
end
step 'I should see group statistics page' do
expect(page).to have_content "Contribution statistics for issues, merge requests and push"
end
end
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment