From 9e5738b0072f8715d52801f3469be9f3742beb97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Rosen=C3=B6gger?= <123haynes@gmail.com> Date: Fri, 13 Mar 2015 11:39:26 +0100 Subject: [PATCH] Extend the commit calendar to show the actual commits for a date --- CHANGELOG | 1 + app/assets/javascripts/calendar.js.coffee | 17 ++++- app/assets/stylesheets/generic/calendar.scss | 62 ++++++++++++------- app/controllers/users_controller.rb | 19 ++++++ app/models/project_contributions.rb | 9 +++ app/models/repository.rb | 14 +++++ app/views/users/calendar.html.haml | 3 +- app/views/users/calendar_activities.html.haml | 33 ++++++++++ app/views/users/show.html.haml | 1 + config/routes.rb | 5 +- db/schema.rb | 2 +- lib/gitlab/commits_calendar.rb | 8 +++ spec/controllers/users_controller_spec.rb | 48 +++++++++++--- spec/models/repository_spec.rb | 19 +++++- 14 files changed, 202 insertions(+), 39 deletions(-) create mode 100644 app/views/users/calendar_activities.html.haml diff --git a/CHANGELOG b/CHANGELOG index 09b60e8e54a..b9b9ec964a1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Please view this file on the master branch, on stable branches it's out of date. v 7.10.0 (unreleased) + - 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) v 7.9.0 (unreleased) diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index 19ea4ccc4cf..2891a48e249 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -4,7 +4,7 @@ class @calendar day: "numeric" year: "numeric" - constructor: (timestamps, starting_year, starting_month) -> + constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> cal = new CalHeatMap() cal.init itemName: ["commit"] @@ -26,5 +26,16 @@ class @calendar ] legendCellPadding: 3 onClick: (date, count) -> - return - return + formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() + $(".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 + diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/generic/calendar.scss index 9483b26164e..e2ab7fc51a5 100644 --- a/app/assets/stylesheets/generic/calendar.scss +++ b/app/assets/stylesheets/generic/calendar.scss @@ -1,29 +1,45 @@ -.calendar_onclick_placeholder { - padding: 0 0 2px 0; -} +.user-calendar-activities { + + .calendar_commit_activity { + padding: 5px 0 0; + } + + .calendar_onclick_hr { + padding: 0; + margin: 10px 0; + } + + .calendar_commit_date { + color: #999; + } + + .calendar_activity_summary { + font-size: 14px; + } -.calendar_commit_activity { - padding: 5px 0 0; -} + .str-truncated { + max-width: 70%; + } -.calendar_onclick_second { - font-size: 14px; - display: block; + .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; + } + } } - -.calendar_onclick_hr { - padding: 0; - margin: 10px 0; -} - -.calendar_commit_date { - color: #999; -} - -.calendar_activity_summary { - font-size: 14px; -} - /** * This overwrites the default values of the cal-heatmap gem */ diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8a13394dbac..68130eb128c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -32,6 +32,7 @@ class UsersController < ApplicationController def calendar projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids) + calendar = Gitlab::CommitsCalendar.new(projects, @user) @timestamps = calendar.timestamps @starting_year = calendar.starting_year @@ -40,6 +41,24 @@ class UsersController < ApplicationController render 'calendar', layout: false 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 if current_user 'navless' diff --git a/app/models/project_contributions.rb b/app/models/project_contributions.rb index 8ab2d814a94..bfe9928b158 100644 --- a/app/models/project_contributions.rb +++ b/app/models/project_contributions.rb @@ -17,6 +17,15 @@ class ProjectContributions 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 "#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}" end diff --git a/app/models/repository.rb b/app/models/repository.rb index 47758b8ad68..7addbca8fb1 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -157,6 +157,20 @@ class Repository 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) timestamps_by_user_log(user). group_by { |commit_date| commit_date }. diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 1d1c974da24..d113ceeb753 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -4,5 +4,6 @@ new calendar( #{@timestamps.to_json}, #{@starting_year}, - #{@starting_month} + #{@starting_month}, + '#{user_calendar_activities_path}' ); diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml new file mode 100644 index 00000000000..7c0cecfadb5 --- /dev/null +++ b/app/views/users/calendar_activities.html.haml @@ -0,0 +1,33 @@ +.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 … + %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 diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index abd6b229782..6d6beb58711 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -25,6 +25,7 @@ .user-calendar %h4.center.light %i.fa.fa-spinner.fa-spin + .user-calendar-activities %hr %h4 User Activity diff --git a/config/routes.rb b/config/routes.rb index e65ef30afb7..0950bed3cf1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -198,7 +198,10 @@ Gitlab::Application.routes.draw do end 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, constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ } diff --git a/db/schema.rb b/db/schema.rb index e7dccbad4f9..1be3782dcb3 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -458,7 +458,6 @@ ActiveRecord::Schema.define(version: 20150313012111) do t.integer "notification_level", default: 1, null: false t.datetime "password_expires_at" t.integer "created_by_id" - t.datetime "last_credential_check_at" t.string "avatar" t.string "confirmation_token" t.datetime "confirmed_at" @@ -466,6 +465,7 @@ ActiveRecord::Schema.define(version: 20150313012111) do t.string "unconfirmed_email" t.boolean "hide_no_ssh_key", default: false t.string "website_url", default: "", null: false + t.datetime "last_credential_check_at" t.string "github_access_token" t.string "gitlab_access_token" t.string "notification_email" diff --git a/lib/gitlab/commits_calendar.rb b/lib/gitlab/commits_calendar.rb index 2f30d238e6b..8963d346b6f 100644 --- a/lib/gitlab/commits_calendar.rb +++ b/lib/gitlab/commits_calendar.rb @@ -22,6 +22,14 @@ module Gitlab 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 (Time.now - 1.year).strftime("%Y") end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 44225c054f2..7962bcdde71 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -1,27 +1,59 @@ require 'spec_helper' 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 sign_in(user) end - describe "GET #show" do + describe 'GET #show' do render_views - it "renders the show template" do + it 'renders the show template' do get :show, username: user.username expect(response.status).to eq(200) - expect(response).to render_template("show") + expect(response).to render_template('show') end end - describe "GET #calendar" do - it "renders calendar" do + describe 'GET #calendar' do + it 'renders calendar' do get :calendar, username: user.username - expect(response).to render_template("calendar") + expect(response).to render_template('calendar') + 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 - diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index b3a38f6c5b9..0e3e0b167d7 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -29,7 +29,7 @@ describe Repository do 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 describe 'multiple emails for user' do @@ -38,7 +38,22 @@ describe Repository do 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