summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad <vlad.bilanchuk@teamvoy.com>2017-09-22 19:20:44 +0300
committerSean McGivern <sean@gitlab.com>2017-10-13 11:41:49 +0100
commit945e0684afc3c22ac658dbb68005cb2ebf5ac51c (patch)
tree90cb7fdd46b13735002d0ecd4e8ae5d75c94201d
parent5843a43c16e007193f5e26522d1e7368a0bdb2d7 (diff)
downloadgitlab-ce-gitlab-ee-1312-time-spent-at.tar.gz
added date parameter for time trackinggitlab-ee-1312-time-spent-at
-rw-r--r--app/models/concerns/time_trackable.rb9
-rw-r--r--app/services/quick_actions/interpret_service.rb16
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--changelogs/unreleased/1312-time-spent-at.yml5
-rw-r--r--db/migrate/20170909150936_add_spent_at_to_timelogs.rb11
-rw-r--r--db/schema.rb1
-rw-r--r--doc/user/project/quick_actions.md2
-rw-r--r--lib/gitlab/quick_actions/spend_time_and_date_separator.rb54
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb81
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb56
11 files changed, 227 insertions, 11 deletions
diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb
index b517ddaebd7..9f403d96ed5 100644
--- a/app/models/concerns/time_trackable.rb
+++ b/app/models/concerns/time_trackable.rb
@@ -9,7 +9,7 @@ module TimeTrackable
extend ActiveSupport::Concern
included do
- attr_reader :time_spent, :time_spent_user
+ attr_reader :time_spent, :time_spent_user, :spent_at
alias_method :time_spent?, :time_spent
@@ -24,6 +24,7 @@ module TimeTrackable
def spend_time(options)
@time_spent = options[:duration]
@time_spent_user = options[:user]
+ @spent_at = options[:spent_at]
@original_total_time_spent = nil
return if @time_spent == 0
@@ -55,7 +56,11 @@ module TimeTrackable
end
def add_or_subtract_spent_time
- timelogs.new(time_spent: time_spent, user: @time_spent_user)
+ timelogs.new(
+ time_spent: time_spent,
+ user: @time_spent_user,
+ spent_at: @spent_at
+ )
end
def check_negative_time_spent
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 955d934838b..06ac86cd5a9 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -381,7 +381,7 @@ module QuickActions
end
desc 'Add or substract spent time'
- explanation do |time_spent|
+ explanation do |time_spent, time_spent_date|
if time_spent
if time_spent > 0
verb = 'Adds'
@@ -394,16 +394,20 @@ module QuickActions
"#{verb} #{Gitlab::TimeTrackingFormatter.output(value)} spent time."
end
end
- params '<1h 30m | -1h 30m>'
+ params '<time(1h30m | -1h30m)> <date(YYYY-MM-DD)>'
condition do
current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
end
- parse_params do |raw_duration|
- Gitlab::TimeTrackingFormatter.parse(raw_duration)
+ parse_params do |raw_time_date|
+ Gitlab::QuickActions::SpendTimeAndDateSeparator.new(raw_time_date).execute
end
- command :spend do |time_spent|
+ command :spend do |time_spent, time_spent_date|
if time_spent
- @updates[:spend_time] = { duration: time_spent, user: current_user }
+ @updates[:spend_time] = {
+ duration: time_spent,
+ user: current_user,
+ spent_at: time_spent_date
+ }
end
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index a52dce6cb4b..0bce20ae5b7 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -195,9 +195,11 @@ module SystemNoteService
if time_spent == :reset
body = "removed time spent"
else
+ spent_at = noteable.spent_at
parsed_time = Gitlab::TimeTrackingFormatter.output(time_spent.abs)
action = time_spent > 0 ? 'added' : 'subtracted'
body = "#{action} #{parsed_time} of time spent"
+ body << " at #{spent_at}" if spent_at
end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
diff --git a/changelogs/unreleased/1312-time-spent-at.yml b/changelogs/unreleased/1312-time-spent-at.yml
new file mode 100644
index 00000000000..c029497e9ab
--- /dev/null
+++ b/changelogs/unreleased/1312-time-spent-at.yml
@@ -0,0 +1,5 @@
+---
+title: Added possibility to enter past date in /spend command to log time in the past
+merge_request: 3044
+author: g3dinua, LockiStrike
+type: changed
diff --git a/db/migrate/20170909150936_add_spent_at_to_timelogs.rb b/db/migrate/20170909150936_add_spent_at_to_timelogs.rb
new file mode 100644
index 00000000000..ffff719c289
--- /dev/null
+++ b/db/migrate/20170909150936_add_spent_at_to_timelogs.rb
@@ -0,0 +1,11 @@
+class AddSpentAtToTimelogs < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def up
+ add_column :timelogs, :spent_at, :datetime_with_timezone
+ end
+
+ def down
+ remove_column :timelogs, :spent_at
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index aac37b6b455..8aadcfeb7d1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1550,6 +1550,7 @@ ActiveRecord::Schema.define(version: 20171006091000) do
t.datetime "updated_at", null: false
t.integer "issue_id"
t.integer "merge_request_id"
+ t.datetime_with_timezone "spent_at"
end
add_index "timelogs", ["issue_id"], name: "index_timelogs_on_issue_id", using: :btree
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 6a5d2d40927..e81e935e37d 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -32,7 +32,7 @@ do.
| `/wip` | Toggle the Work In Progress status |
| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
| `/remove_estimate` | Remove estimated time |
-| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
+| <code>/spend &lt;time(1h 30m &#124; -1h 5m)&gt; &lt;date(YYYY-MM-DD)&gt;</code> | Add or subtract spent time; optionally, specify the date that time was spent on |
| `/remove_time_spent` | Remove time spent |
| `/target_branch <Branch Name>` | Set target branch for current merge request |
| `/award :emoji:` | Toggle award for :emoji: |
diff --git a/lib/gitlab/quick_actions/spend_time_and_date_separator.rb b/lib/gitlab/quick_actions/spend_time_and_date_separator.rb
new file mode 100644
index 00000000000..3f52402b31f
--- /dev/null
+++ b/lib/gitlab/quick_actions/spend_time_and_date_separator.rb
@@ -0,0 +1,54 @@
+module Gitlab
+ module QuickActions
+ # This class takes spend command argument
+ # and separates date and time from spend command arguments if it present
+ # example:
+ # spend_command_time_and_date = "15m 2017-01-02"
+ # SpendTimeAndDateSeparator.new(spend_command_time_and_date).execute
+ # => [900, Mon, 02 Jan 2017]
+ # if date doesn't present return time with current date
+ # in other cases return nil
+ class SpendTimeAndDateSeparator
+ DATE_REGEX = /(\d{2,4}[\/\-.]\d{1,2}[\/\-.]\d{1,2})/
+
+ def initialize(spend_command_arg)
+ @spend_arg = spend_command_arg
+ end
+
+ def execute
+ return if @spend_arg.blank?
+ return [get_time, DateTime.now.to_date] unless date_present?
+ return unless valid_date?
+
+ [get_time, get_date]
+ end
+
+ private
+
+ def get_time
+ raw_time = @spend_arg.gsub(DATE_REGEX, '')
+ Gitlab::TimeTrackingFormatter.parse(raw_time)
+ end
+
+ def get_date
+ string_date = @spend_arg.match(DATE_REGEX)[0]
+ Date.parse(string_date)
+ end
+
+ def date_present?
+ DATE_REGEX =~ @spend_arg
+ end
+
+ def valid_date?
+ string_date = @spend_arg.match(DATE_REGEX)[0]
+ date = Date.parse(string_date) rescue nil
+
+ date_past_or_today?(date)
+ end
+
+ def date_past_or_today?(date)
+ date&.past? || date&.today?
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 80d92b2e6a3..121c0ed04ed 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -496,6 +496,7 @@ Timelog:
- merge_request_id
- issue_id
- user_id
+- spent_at
- created_at
- updated_at
ProjectAutoDevops:
diff --git a/spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb b/spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb
new file mode 100644
index 00000000000..8b58f0b3725
--- /dev/null
+++ b/spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+describe Gitlab::QuickActions::SpendTimeAndDateSeparator do
+ subject { described_class }
+
+ shared_examples 'arg line with invalid parameters' do
+ it 'return nil' do
+ expect(subject.new(invalid_arg).execute).to eq(nil)
+ end
+ end
+
+ shared_examples 'arg line with valid parameters' do
+ it 'return time and date array' do
+ expect(subject.new(valid_arg).execute).to eq(expected_response)
+ end
+ end
+
+ describe '#execute' do
+ context 'invalid paramenter in arg line' do
+ context 'empty arg line' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { '' }
+ end
+ end
+
+ context 'future date in arg line' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { '10m 6023-02-02' }
+ end
+ end
+
+ context 'unparseable date(invalid mixes of delimiters)' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { '10m 2017.02-02' }
+ end
+ end
+
+ context 'trash in arg line' do
+ let(:invalid_arg) { 'dfjkghdskjfghdjskfgdfg' }
+
+ it 'return nil as time value' do
+ time_date_response = subject.new(invalid_arg).execute
+
+ expect(time_date_response).to be_an_instance_of(Array)
+ expect(time_date_response.first).to eq(nil)
+ end
+ end
+ end
+
+ context 'only time present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:valid_arg) { '2m 3m 5m 1h' }
+ let(:time) { Gitlab::TimeTrackingFormatter.parse(valid_arg) }
+ let(:date) { DateTime.now.to_date }
+ let(:expected_response) { [time, date] }
+ end
+ end
+
+ context 'simple time with date in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:raw_time) { '10m' }
+ let(:raw_date) { '2016-02-02' }
+ let(:valid_arg) { "#{raw_time} #{raw_date}" }
+ let(:date) { Date.parse(raw_date) }
+ let(:time) { Gitlab::TimeTrackingFormatter.parse(raw_time) }
+ let(:expected_response) { [time, date] }
+ end
+ end
+
+ context 'composite time with date in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:raw_time) { '2m 10m 1h 3d' }
+ let(:raw_date) { '2016/02/02' }
+ let(:valid_arg) { "#{raw_time} #{raw_date}" }
+ let(:date) { Date.parse(raw_date) }
+ let(:time) { Gitlab::TimeTrackingFormatter.parse(raw_time) }
+ let(:expected_response) { [time, date] }
+ end
+ end
+ end
+end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 6926ac85de3..c35177f6ebc 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -207,7 +207,11 @@ describe QuickActions::InterpretService do
it 'populates spend_time: 3600 if content contains /spend 1h' do
_, updates = service.execute(content, issuable)
- expect(updates).to eq(spend_time: { duration: 3600, user: developer })
+ expect(updates).to eq(spend_time: {
+ duration: 3600,
+ user: developer,
+ spent_at: DateTime.now.to_date
+ })
end
end
@@ -215,7 +219,39 @@ describe QuickActions::InterpretService do
it 'populates spend_time: -1800 if content contains /spend -30m' do
_, updates = service.execute(content, issuable)
- expect(updates).to eq(spend_time: { duration: -1800, user: developer })
+ expect(updates).to eq(spend_time: {
+ duration: -1800,
+ user: developer,
+ spent_at: DateTime.now.to_date
+ })
+ end
+ end
+
+ shared_examples 'spend command with valid date' do
+ it 'populates spend time: 1800 with date in date type format' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(spend_time: {
+ duration: 1800,
+ user: developer,
+ spent_at: Date.parse(date)
+ })
+ end
+ end
+
+ shared_examples 'spend command with invalid date' do
+ it 'will not create any note and timelog' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq({})
+ end
+ end
+
+ shared_examples 'spend command with future date' do
+ it 'will not create any note and timelog' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq({})
end
end
@@ -669,6 +705,22 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ it_behaves_like 'spend command with valid date' do
+ let(:date) { '2016-02-02' }
+ let(:content) { "/spend 30m #{date}" }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'spend command with invalid date' do
+ let(:content) { '/spend 30m 17-99-99' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'spend command with future date' do
+ let(:content) { '/spend 30m 6017-10-10' }
+ let(:issuable) { issue }
+ end
+
it_behaves_like 'empty command' do
let(:content) { '/spend' }
let(:issuable) { issue }