Merge branch 'gitlab-ee-1312-time-spent-at' into 'master'
added date parameter for time tracking See merge request gitlab-org/gitlab-ce!14866
This commit is contained in:
commit
b0f98a2e8e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'))
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -32,7 +32,7 @@ do.
|
|||
| `/wip` | Toggle the Work In Progress status |
|
||||
| <code>/estimate <1w 3d 2h 14m></code> | Set time estimate |
|
||||
| `/remove_estimate` | Remove estimated time |
|
||||
| <code>/spend <1h 30m | -1h 5m></code> | Add or subtract spent time |
|
||||
| <code>/spend <time(1h 30m | -1h 5m)> <date(YYYY-MM-DD)></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: |
|
||||
|
|
|
@ -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
|
|
@ -496,6 +496,7 @@ Timelog:
|
|||
- merge_request_id
|
||||
- issue_id
|
||||
- user_id
|
||||
- spent_at
|
||||
- created_at
|
||||
- updated_at
|
||||
ProjectAutoDevops:
|
||||
|
|
|
@ -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
|
|
@ -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 }
|
||||
|
|
Loading…
Reference in New Issue