Commit 1f21dfae authored by Douwe Maan's avatar Douwe Maan

Merge branch 'commit_calendar_activity' into 'master'

Extend commit calendar to actually show what commits were made on a date

## What does this MR do?
This MR extends the commit calendar so it acutally shows what commits were made on a date and in which project.
It is based on the optimizations @dzaporozhets made for the calendar.

## Are there points in the code the reviewer needs to double check?
The UI and how the links are generated i guess. That feels hacky at the moment :/

## Screenshot:
![commit_calendar_extend_display](https://gitlab.com/uploads/gitlab-org/gitlab-ce/5bf1631660/commit_calendar_extend_display.png)

I assume that i have to refactor this a bit more to make it a cleaner implementation, so please give me feedback on what needs to be changed :)

See merge request !326
parents 5c4d6606 9e5738b0
...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,6 +2,7 @@ Please view this file on the master branch, on stable branches it's out of date.
v 7.10.0 (unreleased) v 7.10.0 (unreleased)
- enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger) - enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger)
- extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger)
- Add a service to support external wikis (Hannes Rosenögger) - Add a service to support external wikis (Hannes Rosenögger)
v 7.9.0 (unreleased) v 7.9.0 (unreleased)
......
...@@ -4,7 +4,7 @@ class @calendar ...@@ -4,7 +4,7 @@ class @calendar
day: "numeric" day: "numeric"
year: "numeric" year: "numeric"
constructor: (timestamps, starting_year, starting_month) -> constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
cal = new CalHeatMap() cal = new CalHeatMap()
cal.init cal.init
itemName: ["commit"] itemName: ["commit"]
...@@ -26,5 +26,16 @@ class @calendar ...@@ -26,5 +26,16 @@ class @calendar
] ]
legendCellPadding: 3 legendCellPadding: 3
onClick: (date, count) -> onClick: (date, count) ->
return formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
return $(".calendar_commit_activity").fadeOut 400
$.ajax
url: calendar_activities_path
data:
date: formated_date
cache: false
dataType: "html"
success: (data) ->
$(".user-calendar-activities").html data
$(".calendar_commit_activity").find(".js-toggle-content").hide()
$(".calendar_commit_activity").fadeIn 400
.calendar_onclick_placeholder { .user-calendar-activities {
padding: 0 0 2px 0;
}
.calendar_commit_activity { .calendar_commit_activity {
padding: 5px 0 0; padding: 5px 0 0;
} }
.calendar_onclick_second {
font-size: 14px;
display: block;
}
.calendar_onclick_hr { .calendar_onclick_hr {
padding: 0; padding: 0;
margin: 10px 0; margin: 10px 0;
} }
.calendar_commit_date { .calendar_commit_date {
color: #999; color: #999;
} }
.calendar_activity_summary { .calendar_activity_summary {
font-size: 14px; font-size: 14px;
} }
.str-truncated {
max-width: 70%;
}
.text-expander {
background: #eee;
color: #555;
padding: 0 5px;
cursor: pointer;
margin-left: 4px;
&:hover {
background-color: #ddd;
}
}
.commit-row-message {
color: #333;
&:hover {
color: #444;
text-decoration: underline;
}
}
}
/** /**
* This overwrites the default values of the cal-heatmap gem * This overwrites the default values of the cal-heatmap gem
*/ */
......
...@@ -32,6 +32,7 @@ class UsersController < ApplicationController ...@@ -32,6 +32,7 @@ class UsersController < ApplicationController
def calendar def calendar
projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids) projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
calendar = Gitlab::CommitsCalendar.new(projects, @user) calendar = Gitlab::CommitsCalendar.new(projects, @user)
@timestamps = calendar.timestamps @timestamps = calendar.timestamps
@starting_year = calendar.starting_year @starting_year = calendar.starting_year
...@@ -40,6 +41,24 @@ class UsersController < ApplicationController ...@@ -40,6 +41,24 @@ class UsersController < ApplicationController
render 'calendar', layout: false render 'calendar', layout: false
end end
def calendar_activities
projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
date = Date.parse(params[:date]) rescue nil
if date
@calendar_activities = Gitlab::CommitsCalendar.get_commits_for_date(projects, @user, date)
else
@calendar_activities = {}
end
# get the total number of unique commits
@commit_count = @calendar_activities.values.flatten.map(&:id).uniq.count
@calendar_date = date
render 'calendar_activities', layout: false
end
def determine_layout def determine_layout
if current_user if current_user
'navless' 'navless'
......
...@@ -17,6 +17,15 @@ class ProjectContributions ...@@ -17,6 +17,15 @@ class ProjectContributions
end end
end end
def user_commits_on_date(date)
repository = @project.repository
if !repository.exists? || repository.empty?
return []
end
commits = repository.commits_by_user_on_date_log(@user, date)
end
def cache_key def cache_key
"#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}" "#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}"
end end
......
...@@ -157,6 +157,20 @@ class Repository ...@@ -157,6 +157,20 @@ class Repository
end end
end end
def commits_by_user_on_date_log(user, date)
# format the date string for git
start_date = date.strftime("%Y-%m-%d 00:00:00")
end_date = date.strftime("%Y-%m-%d 23:59:59")
author_emails = '(' + user.all_emails.map{ |e| Regexp.escape(e) }.join('|') + ')'
args = %W(git log -E --author=#{author_emails} --after=#{start_date.to_s} --until=#{end_date.to_s} --branches --pretty=format:%h)
commits = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
commits.map! do |commit_id|
commit(commit_id)
end
end
def commits_per_day_for_user(user) def commits_per_day_for_user(user)
timestamps_by_user_log(user). timestamps_by_user_log(user).
group_by { |commit_date| commit_date }. group_by { |commit_date| commit_date }.
......
...@@ -4,5 +4,6 @@ ...@@ -4,5 +4,6 @@
new calendar( new calendar(
#{@timestamps.to_json}, #{@timestamps.to_json},
#{@starting_year}, #{@starting_year},
#{@starting_month} #{@starting_month},
'#{user_calendar_activities_path}'
); );
.calendar_commit_activity
%hr
%h4
Commit Activity
%strong
- if @commit_count == 0
no
- else
= @commit_count
%span.calendar_commit_date
unique
= 'commit'.pluralize(@commit_count)
on
= @calendar_date.strftime("%b %d, %Y") rescue ''
-unless @commit_count == 0
%hr
- @calendar_activities.each do |project, commits|
- next if commits.empty?
%div.js-toggle-container
%strong
= pluralize(commits.count, 'commit')
in project
= link_to project.name_with_namespace, project_path(project)
%a.text-expander.js-toggle-button &hellip;
%hr
%div.js-toggle-content
- commits.each do |commit|
%span.monospace
= commit.committed_date.strftime("%H:%M")
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= link_to commit.message, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message str-truncated"
%br
%hr
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
.user-calendar .user-calendar
%h4.center.light %h4.center.light
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
.user-calendar-activities
%hr %hr
%h4 %h4
User Activity User Activity
......
...@@ -198,7 +198,10 @@ Gitlab::Application.routes.draw do ...@@ -198,7 +198,10 @@ Gitlab::Application.routes.draw do
end end
get 'u/:username/calendar' => 'users#calendar', as: :user_calendar, get 'u/:username/calendar' => 'users#calendar', as: :user_calendar,
constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } constraints: { username: /.*/ }
get 'u/:username/calendar_activities' => 'users#calendar_activities', as: :user_calendar_activities,
constraints: { username: /.*/ }
get '/u/:username' => 'users#show', as: :user, get '/u/:username' => 'users#show', as: :user,
constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
......
...@@ -458,7 +458,6 @@ ActiveRecord::Schema.define(version: 20150313012111) do ...@@ -458,7 +458,6 @@ ActiveRecord::Schema.define(version: 20150313012111) do
t.integer "notification_level", default: 1, null: false t.integer "notification_level", default: 1, null: false
t.datetime "password_expires_at" t.datetime "password_expires_at"
t.integer "created_by_id" t.integer "created_by_id"
t.datetime "last_credential_check_at"
t.string "avatar" t.string "avatar"
t.string "confirmation_token" t.string "confirmation_token"
t.datetime "confirmed_at" t.datetime "confirmed_at"
...@@ -466,6 +465,7 @@ ActiveRecord::Schema.define(version: 20150313012111) do ...@@ -466,6 +465,7 @@ ActiveRecord::Schema.define(version: 20150313012111) do
t.string "unconfirmed_email" t.string "unconfirmed_email"
t.boolean "hide_no_ssh_key", default: false t.boolean "hide_no_ssh_key", default: false
t.string "website_url", default: "", null: false t.string "website_url", default: "", null: false
t.datetime "last_credential_check_at"
t.string "github_access_token" t.string "github_access_token"
t.string "gitlab_access_token" t.string "gitlab_access_token"
t.string "notification_email" t.string "notification_email"
......
...@@ -22,6 +22,14 @@ module Gitlab ...@@ -22,6 +22,14 @@ module Gitlab
end end
end end
def self.get_commits_for_date(projects, user, date)
user_commits = {}
projects.reject(&:forked?).each do |project|
user_commits[project] = ProjectContributions.new(project, user).user_commits_on_date(date)
end
user_commits
end
def starting_year def starting_year
(Time.now - 1.year).strftime("%Y") (Time.now - 1.year).strftime("%Y")
end end
......
require 'spec_helper' require 'spec_helper'
describe UsersController do describe UsersController do
let(:user) { create(:user, username: "user1", name: "User 1", email: "user1@gitlab.com") } let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') }
before do before do
sign_in(user) sign_in(user)
end end
describe "GET #show" do describe 'GET #show' do
render_views render_views
it "renders the show template" do it 'renders the show template' do
get :show, username: user.username get :show, username: user.username
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response).to render_template("show") expect(response).to render_template('show')
end end
end end
describe "GET #calendar" do describe 'GET #calendar' do
it "renders calendar" do it 'renders calendar' do
get :calendar, username: user.username get :calendar, username: user.username
expect(response).to render_template("calendar") expect(response).to render_template('calendar')
end end
end end
end
describe 'GET #calendar_activities' do
include RepoHelpers
let(:project) { create(:project) }
let(:calendar_user) { create(:user, email: sample_commit.author_email) }
let(:commit1) { '0ed8c6c6752e8c6ea63e7b92a517bf5ac1209c80' }
let(:commit2) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
before do
allow_any_instance_of(User).to receive(:contributed_projects_ids).and_return([project.id])
project.team << [user, :developer]
end
it 'assigns @commit_count' do
get :calendar_activities, username: calendar_user.username, date: '2014-07-31'
expect(assigns(:commit_count)).to eq(2)
end
it 'assigns @calendar_date' do
get :calendar_activities, username: calendar_user.username, date: '2014-07-31'
expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31'))
end
it 'assigns @calendar_activities' do
get :calendar_activities, username: calendar_user.username, date: '2014-07-31'
expect(assigns(:calendar_activities).values.flatten.map(&:id)).to eq([commit1, commit2])
end
it 'renders calendar_activities' do
get :calendar_activities, username: calendar_user.username
expect(response).to render_template('calendar_activities')
end
end
end
...@@ -29,7 +29,7 @@ describe Repository do ...@@ -29,7 +29,7 @@ describe Repository do
subject { repository.timestamps_by_user_log(user) } subject { repository.timestamps_by_user_log(user) }
it { is_expected.to eq(["2014-08-06", "2014-07-31", "2014-07-31"]) } it { is_expected.to eq(['2014-08-06', '2014-07-31', '2014-07-31']) }
end end
describe 'multiple emails for user' do describe 'multiple emails for user' do
...@@ -38,7 +38,22 @@ describe Repository do ...@@ -38,7 +38,22 @@ describe Repository do
subject { repository.timestamps_by_user_log(user) } subject { repository.timestamps_by_user_log(user) }
it { is_expected.to eq(["2015-01-10", "2014-08-06", "2014-07-31", "2014-07-31"]) } it { is_expected.to eq(['2015-01-10', '2014-08-06', '2014-07-31', '2014-07-31']) }
end
end
context :commits_by_user_on_date_log do
describe 'single e-mail for user' do
let(:user) { create(:user, email: sample_commit.author_email) }
let(:commit1) { '0ed8c6c6752e8c6ea63e7b92a517bf5ac1209c80' }
let(:commit2) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' }
subject { repository.commits_by_user_on_date_log(user,Date.new(2014, 07, 31)) }
it 'contains the exepected commits' do
expect(subject.flatten.map(&:id)).to eq([commit1, commit2])
end
end end
end end
end end
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