Commit 945e0684 authored by Vlad's avatar Vlad Committed by Sean McGivern

added date parameter for time tracking

parent 5843a43c
......@@ -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'))
......
---
title: Added possibility to enter past date in /spend command to log time in the past
merge_request: 3044
author: g3dinua, LockiStrike
type: changed
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 &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: |
......
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:
......
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 }
......
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