time_log_robot 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +6 -0
- data/Gemfile.lock +23 -6
- data/README.md +31 -0
- data/Rakefile +3 -1
- data/bin/time_log_robot +5 -1
- data/lib/time_log_robot/entry.rb +12 -0
- data/lib/time_log_robot/jira/issue_key_parser.rb +13 -4
- data/lib/time_log_robot/jira/work_logger.rb +21 -55
- data/lib/time_log_robot/reporter.rb +32 -0
- data/lib/time_log_robot/tagger.rb +12 -0
- data/lib/time_log_robot/toggl/entry.rb +54 -0
- data/lib/time_log_robot/toggl/report.rb +1 -1
- data/lib/time_log_robot/version.rb +1 -1
- data/lib/time_log_robot.rb +6 -3
- data/test/time_log_robot/jira/issue_key_parser_test.rb +26 -5
- data/time_log_robot.gemspec +1 -0
- metadata +20 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f61caa0dc4d6525a101c6ce36384098b0b84d707
|
4
|
+
data.tar.gz: 946edcfc71a494b8687572411440988dabfb6fc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d9a8d4838ec1f7178e9ee66111d9e13dabd1324ec24de3d634d662b7d389c08739ed797cb4980470dd3144f38d82074c7ccd3ebb74a6821ab834a369f4ce973
|
7
|
+
data.tar.gz: ce614922c96cdaf8fa142bf4c7fb005d43d1f4cdac73eb421f7dd72f7a920ace010d8ebc606479ccdb89051d62970db1e5047429d98f56f611dbcb842544a6e8
|
data/.codeclimate.yml
CHANGED
@@ -12,8 +12,13 @@ engines:
|
|
12
12
|
- php
|
13
13
|
fixme:
|
14
14
|
enabled: true
|
15
|
+
exclude_fingerprints:
|
16
|
+
- 9b1a9060d3684be85a832e44ca1ea7dd
|
15
17
|
rubocop:
|
16
18
|
enabled: true
|
19
|
+
checks:
|
20
|
+
Rubocop/Lint/AssignmentInCondition:
|
21
|
+
enabled: false
|
17
22
|
ratings:
|
18
23
|
paths:
|
19
24
|
- Gemfile.lock
|
@@ -26,3 +31,4 @@ ratings:
|
|
26
31
|
- "**.rb"
|
27
32
|
exclude_paths:
|
28
33
|
- test/
|
34
|
+
- README.md
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
time_log_robot (0.
|
4
|
+
time_log_robot (0.2.0)
|
5
5
|
activesupport (~> 4.2, >= 4.2.6)
|
6
6
|
commander (~> 4.1, >= 4.1.6)
|
7
7
|
httparty (~> 0.13, >= 0.13.0)
|
@@ -18,14 +18,19 @@ GEM
|
|
18
18
|
tzinfo (~> 1.1)
|
19
19
|
ansi (1.5.0)
|
20
20
|
builder (3.2.2)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
codeclimate-test-reporter (0.5.2)
|
22
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
23
|
+
coderay (1.1.1)
|
24
|
+
commander (4.4.0)
|
25
|
+
highline (~> 1.7.2)
|
26
|
+
docile (1.1.5)
|
27
|
+
highline (1.7.8)
|
28
|
+
httparty (0.13.7)
|
25
29
|
json (~> 1.8)
|
26
30
|
multi_xml (>= 0.5.2)
|
27
31
|
i18n (0.7.0)
|
28
32
|
json (1.8.3)
|
33
|
+
method_source (0.8.2)
|
29
34
|
minitest (5.8.4)
|
30
35
|
minitest-reporters (1.1.9)
|
31
36
|
ansi
|
@@ -33,8 +38,18 @@ GEM
|
|
33
38
|
minitest (>= 5.0)
|
34
39
|
ruby-progressbar
|
35
40
|
multi_xml (0.5.5)
|
41
|
+
pry (0.10.3)
|
42
|
+
coderay (~> 1.1.0)
|
43
|
+
method_source (~> 0.8.1)
|
44
|
+
slop (~> 3.4)
|
36
45
|
rake (10.5.0)
|
37
46
|
ruby-progressbar (1.8.1)
|
47
|
+
simplecov (0.11.2)
|
48
|
+
docile (~> 1.1.0)
|
49
|
+
json (~> 1.8)
|
50
|
+
simplecov-html (~> 0.10.0)
|
51
|
+
simplecov-html (0.10.0)
|
52
|
+
slop (3.6.0)
|
38
53
|
thread_safe (0.3.5)
|
39
54
|
tzinfo (1.2.2)
|
40
55
|
thread_safe (~> 0.1)
|
@@ -44,10 +59,12 @@ PLATFORMS
|
|
44
59
|
|
45
60
|
DEPENDENCIES
|
46
61
|
bundler (~> 1.3)
|
62
|
+
codeclimate-test-reporter (~> 0.5)
|
47
63
|
minitest (~> 5.8)
|
48
64
|
minitest-reporters (~> 1.1)
|
65
|
+
pry
|
49
66
|
rake (~> 10.5)
|
50
67
|
time_log_robot!
|
51
68
|
|
52
69
|
BUNDLED WITH
|
53
|
-
1.12.
|
70
|
+
1.12.5
|
data/README.md
CHANGED
@@ -47,6 +47,35 @@ The simplest usage is just to invoke the robot:
|
|
47
47
|
|
48
48
|
$ time_log_robot
|
49
49
|
|
50
|
+
#### Format of time entries
|
51
|
+
|
52
|
+
Time entries need an issue key (in JIRA, something like `BUG-12`), a start time, and a duration. The robot will try to parse an issue key from the description first, then from the project name, then from the mapping file (see ["Mapping Keys"](#mapping-keys) section).
|
53
|
+
|
54
|
+
For example, all of these are valid:
|
55
|
+
|
56
|
+
This is a bug BUG-15
|
57
|
+
|
58
|
+
(no description) APP-20
|
59
|
+
|
60
|
+
Meeting
|
61
|
+
(In project named: ADMIN-123)
|
62
|
+
|
63
|
+
##### Comments
|
64
|
+
|
65
|
+
Some project management tools also accept comments/descriptions for each time log entry. The robot uses any text within curly braces in the time logging app (Toggl) entry as the description in the project management log entry.
|
66
|
+
|
67
|
+
For example:
|
68
|
+
|
69
|
+
This is a bug {This is my comment} BUG-15
|
70
|
+
|
71
|
+
If there are no curly braces present, the robot will use the entire description as the comment.
|
72
|
+
|
73
|
+
For example:
|
74
|
+
|
75
|
+
This whole description is my comment on issue BUG-20
|
76
|
+
|
77
|
+
#### Specifying how far back to log
|
78
|
+
|
50
79
|
By default, the robot will get all time entries since the previous Saturday. To specify a different time, run it with the optional `--since` flag (Note: the date given must be in YYYY-MM-DD format):
|
51
80
|
|
52
81
|
$ time_log_robot --since 2016-05-01
|
@@ -126,6 +155,8 @@ To run the app in IRB for debugging run
|
|
126
155
|
|
127
156
|
## Contributing
|
128
157
|
|
158
|
+
Contributions are welcome! See the [Issues](https://github.com/supremebeing7/time_log_robot/issues) for stuff that needs doing, or create a new one if you have an idea and we can discuss. Eventually it would be great to integrate this with other project management and time tracking tools, so if you use something besides JIRA or Toggl and want to build an integration, that would be welcome. (There's some refactoring that needs to be done on the existing code to make this simpler - issue [#18](https://github.com/supremebeing7/time_log_robot/issues/18).)
|
159
|
+
|
129
160
|
1. Fork it
|
130
161
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
131
162
|
3. Write tests for your new code (uses `minitest`)
|
data/Rakefile
CHANGED
data/bin/time_log_robot
CHANGED
@@ -2,9 +2,13 @@
|
|
2
2
|
|
3
3
|
require 'commander/import'
|
4
4
|
require 'time_log_robot'
|
5
|
+
require 'time_log_robot/entry'
|
6
|
+
require 'time_log_robot/reporter'
|
7
|
+
require 'time_log_robot/tagger'
|
5
8
|
require 'time_log_robot/version'
|
6
|
-
require 'time_log_robot/toggl/
|
9
|
+
require 'time_log_robot/toggl/entry'
|
7
10
|
require 'time_log_robot/toggl/report'
|
11
|
+
require 'time_log_robot/toggl/tagger'
|
8
12
|
require 'time_log_robot/jira/issue_key_parser'
|
9
13
|
require 'time_log_robot/jira/payload_builder'
|
10
14
|
require 'time_log_robot/jira/work_logger'
|
@@ -1,16 +1,25 @@
|
|
1
1
|
module TimeLogRobot
|
2
2
|
module JIRA
|
3
3
|
class IssueKeyParser
|
4
|
+
ISSUE_KEY_REGEX = /([A-Z]+-\d+)/
|
4
5
|
|
5
6
|
class << self
|
6
|
-
def parse(
|
7
|
-
|
8
|
-
|
9
|
-
get_key_from_key_mapping(description)
|
7
|
+
def parse(entry)
|
8
|
+
get_key_from_description(entry.description) ||
|
9
|
+
get_key_from_project(entry.project_name) ||
|
10
|
+
get_key_from_key_mapping(entry.description)
|
10
11
|
end
|
11
12
|
|
12
13
|
private
|
13
14
|
|
15
|
+
def get_key_from_description(description)
|
16
|
+
description.match(ISSUE_KEY_REGEX).to_a[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_key_from_project(project_name)
|
20
|
+
project_name.match(ISSUE_KEY_REGEX).to_a[1]
|
21
|
+
end
|
22
|
+
|
14
23
|
def get_key_from_key_mapping(description)
|
15
24
|
if found_key = mappings.keys.find { |key| description.include?(key) }
|
16
25
|
mappings[found_key]
|
@@ -7,15 +7,17 @@ module TimeLogRobot
|
|
7
7
|
|
8
8
|
@errors = []
|
9
9
|
@logged_count = 0
|
10
|
+
@issue_key = nil
|
10
11
|
|
11
12
|
class << self
|
12
|
-
attr_accessor :errors, :logged_count
|
13
|
+
attr_accessor :errors, :logged_count, :issue_key
|
13
14
|
|
14
|
-
def log_all(time_entries:)
|
15
|
-
time_entries.each do |
|
15
|
+
def log_all(service:, time_entries:)
|
16
|
+
time_entries.each do |raw_entry|
|
17
|
+
entry = Entry.new(service: service, raw_entry: raw_entry)
|
16
18
|
log(entry) unless is_logged?(entry)
|
17
19
|
end
|
18
|
-
|
20
|
+
report!
|
19
21
|
end
|
20
22
|
|
21
23
|
private
|
@@ -33,16 +35,16 @@ module TimeLogRobot
|
|
33
35
|
end
|
34
36
|
|
35
37
|
def is_logged?(entry)
|
36
|
-
(log_tags - entry
|
38
|
+
(log_tags - entry.tags).size < log_tags.size
|
37
39
|
end
|
38
40
|
|
39
41
|
def log(entry)
|
40
|
-
issue_key = parse_issue_key(entry)
|
41
42
|
payload = build_payload(entry)
|
43
|
+
@issue_key = parse_issue_key(entry)
|
42
44
|
response = post("/issue/#{issue_key}/worklog", basic_auth: auth, headers: headers, body: payload)
|
43
45
|
if response.success?
|
44
46
|
print "\e[32m.\e[0m"
|
45
|
-
|
47
|
+
tag!(entry) if should_tag?(entry)
|
46
48
|
@logged_count += 1
|
47
49
|
else
|
48
50
|
print "\e[31mF\e[0m"
|
@@ -54,36 +56,16 @@ module TimeLogRobot
|
|
54
56
|
end
|
55
57
|
class UnauthorizedError < Exception; end
|
56
58
|
|
57
|
-
def
|
58
|
-
|
59
|
+
def report!
|
60
|
+
Reporter.report(errors, logged_count)
|
59
61
|
end
|
60
62
|
|
61
|
-
def
|
62
|
-
|
63
|
-
puts "\n\t#{logged_count} entries logged, #{errors.size} failed.\n\n"
|
63
|
+
def tag!(entry)
|
64
|
+
Tagger.tag(entry)
|
64
65
|
end
|
65
66
|
|
66
|
-
def
|
67
|
-
|
68
|
-
errors.each_with_index do |(entry, response), index|
|
69
|
-
puts "\e[31m"
|
70
|
-
puts "\t#{index + 1})\tDescription: #{entry['description']}"
|
71
|
-
if issue_key = parse_issue_key(entry)
|
72
|
-
puts "\t\tIssue Key: #{issue_key}"
|
73
|
-
else
|
74
|
-
puts "\t\tIssue Key: Missing"
|
75
|
-
end
|
76
|
-
unless parse_comment(entry).nil?
|
77
|
-
puts "\t\tComment: #{parse_comment(entry)}"
|
78
|
-
end
|
79
|
-
puts "\t\t#{human_readable_duration(parse_duration(entry))} starting on #{parse_start(entry)}"
|
80
|
-
puts "\t\tResponse Code: #{response.code}"
|
81
|
-
puts "\e[0m"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def set_entry_as_logged(entry)
|
86
|
-
Toggl::Tagger.update(entry_id: entry['id'])
|
67
|
+
def should_tag?(entry)
|
68
|
+
entry.should_tag?
|
87
69
|
end
|
88
70
|
|
89
71
|
def auth
|
@@ -99,31 +81,15 @@ module TimeLogRobot
|
|
99
81
|
end
|
100
82
|
|
101
83
|
def build_payload(entry)
|
102
|
-
|
103
|
-
start:
|
104
|
-
duration_in_seconds:
|
105
|
-
comment:
|
84
|
+
PayloadBuilder.build(
|
85
|
+
start: entry.start,
|
86
|
+
duration_in_seconds: entry.duration_in_seconds,
|
87
|
+
comment: entry.comment
|
106
88
|
)
|
107
89
|
end
|
108
90
|
|
109
|
-
def
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
def parse_duration(entry)
|
114
|
-
entry['dur']/1000 # Toggl sends times in milliseconds
|
115
|
-
end
|
116
|
-
|
117
|
-
def human_readable_duration(seconds)
|
118
|
-
total_minutes = seconds/60
|
119
|
-
hours = total_minutes/60
|
120
|
-
remaining_minutes = total_minutes - hours * 60
|
121
|
-
"#{hours}h #{remaining_minutes}m"
|
122
|
-
end
|
123
|
-
|
124
|
-
def parse_comment(entry)
|
125
|
-
matches = entry['description'].match(/(\{(?<comment>[^\}]*)\})/)
|
126
|
-
matches['comment'] if matches.present?
|
91
|
+
def parse_issue_key(entry)
|
92
|
+
IssueKeyParser.parse(entry)
|
127
93
|
end
|
128
94
|
end
|
129
95
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module TimeLogRobot
|
2
|
+
class Reporter
|
3
|
+
class << self
|
4
|
+
attr_accessor :errors
|
5
|
+
|
6
|
+
def report(errors, logged_count)
|
7
|
+
@errors = errors
|
8
|
+
print_errors if errors.any?
|
9
|
+
puts "\n\t#{logged_count} entries logged, #{errors.size} failed.\n\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
def print_errors
|
13
|
+
puts "\n\t\e[1;31m Failed to log the following entries:\e[0m"
|
14
|
+
errors.each_with_index do |(entry, response), index|
|
15
|
+
puts "\e[31m"
|
16
|
+
puts "\t#{index + 1})\tDescription: #{entry.description}"
|
17
|
+
if entry.issue_key
|
18
|
+
puts "\t\tIssue Key: #{entry.issue_key}"
|
19
|
+
else
|
20
|
+
puts "\t\tIssue Key: Missing"
|
21
|
+
end
|
22
|
+
if entry.comment
|
23
|
+
puts "\t\tComment: #{entry.comment}"
|
24
|
+
end
|
25
|
+
puts "\t\t#{entry.human_readable_duration} starting on #{entry.start}"
|
26
|
+
puts "\t\tResponse Code: #{response.code}"
|
27
|
+
puts "\e[0m"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module TimeLogRobot
|
2
|
+
module Toggl
|
3
|
+
class Entry
|
4
|
+
attr_accessor :raw_entry, :duration
|
5
|
+
|
6
|
+
def initialize(raw_entry)
|
7
|
+
@raw_entry = raw_entry
|
8
|
+
end
|
9
|
+
|
10
|
+
def description
|
11
|
+
raw_entry['description']
|
12
|
+
end
|
13
|
+
|
14
|
+
def comment
|
15
|
+
matches = raw_entry['description'].match(/(\{(?<comment>[^\}]*)\})/)
|
16
|
+
return matches['comment'] if matches.present?
|
17
|
+
description
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
DateTime.strptime(raw_entry['start'], "%FT%T%:z").strftime("%FT%T.%L%z")
|
22
|
+
end
|
23
|
+
|
24
|
+
def duration_in_seconds
|
25
|
+
# Toggl sends times in milliseconds
|
26
|
+
@duration_in_seconds ||= raw_entry['dur']/1000
|
27
|
+
end
|
28
|
+
|
29
|
+
# @TODO This probably belongs on the reporter class?
|
30
|
+
def human_readable_duration
|
31
|
+
total_minutes = duration_in_seconds/60
|
32
|
+
hours = total_minutes/60
|
33
|
+
remaining_minutes = total_minutes - hours * 60
|
34
|
+
"#{hours}h #{remaining_minutes}m"
|
35
|
+
end
|
36
|
+
|
37
|
+
def id
|
38
|
+
raw_entry['id']
|
39
|
+
end
|
40
|
+
|
41
|
+
def tags
|
42
|
+
raw_entry['tags']
|
43
|
+
end
|
44
|
+
|
45
|
+
def project_name
|
46
|
+
raw_entry['project'] || ''
|
47
|
+
end
|
48
|
+
|
49
|
+
def should_tag?
|
50
|
+
true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/time_log_robot.rb
CHANGED
@@ -7,11 +7,14 @@ module TimeLogRobot
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.start(since)
|
10
|
-
|
11
|
-
JIRA::WorkLogger.log_all(
|
10
|
+
report = fetch_time_report(since)
|
11
|
+
JIRA::WorkLogger.log_all(
|
12
|
+
service: report[:service],
|
13
|
+
time_entries: report[:entries]
|
14
|
+
)
|
12
15
|
end
|
13
16
|
|
14
|
-
def self.
|
17
|
+
def self.fetch_time_report(since)
|
15
18
|
if since.nil?
|
16
19
|
Toggl::Report.fetch
|
17
20
|
else
|
@@ -13,26 +13,47 @@ module TimeLogRobot
|
|
13
13
|
class TestIssueKeyParser < Minitest::Test
|
14
14
|
def setup
|
15
15
|
@described_class = TimeLogRobot::JIRA::IssueKeyParser
|
16
|
+
@entry = OpenStruct.new(description: '', project_name: '')
|
16
17
|
end
|
17
18
|
|
18
19
|
def test_that_it_gets_the_issue_key
|
19
|
-
|
20
|
+
@entry.description = 'description [PM-12]'
|
21
|
+
assert_equal "PM-12", @described_class.parse(@entry)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_that_it_gets_the_issue_key_without_brackets
|
25
|
+
@entry.description = 'description PM-12'
|
26
|
+
assert_equal "PM-12", @described_class.parse(@entry)
|
20
27
|
end
|
21
28
|
|
22
29
|
def test_that_it_gives_nothing_with_no_key_present
|
23
|
-
|
30
|
+
@entry.description = 'description'
|
31
|
+
assert_equal nil, @described_class.parse(@entry)
|
24
32
|
end
|
25
33
|
|
26
34
|
def test_that_it_fetches_the_key_from_mapping
|
27
|
-
|
35
|
+
@entry.description = 'mapped description'
|
36
|
+
assert_equal "HI-5", @described_class.parse(@entry)
|
28
37
|
end
|
29
38
|
|
30
39
|
def test_that_parse_is_not_too_strict_with_mappings
|
31
|
-
|
40
|
+
@entry.description = 'mapped description with more text'
|
41
|
+
assert_equal "HI-5", @described_class.parse(@entry)
|
32
42
|
end
|
33
43
|
|
34
44
|
def test_that_parse_mappings_works_with_comments
|
35
|
-
|
45
|
+
@entry.description = 'mapped description - {with comment}'
|
46
|
+
assert_equal "HI-5", @described_class.parse(@entry)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_get_issue_key_from_project_name
|
50
|
+
@entry.project_name = 'BUG-30'
|
51
|
+
assert_equal "BUG-30", @described_class.parse(@entry)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_get_issue_key_from_project_name_with_extra_text
|
55
|
+
@entry.project_name = 'BUG-30 crazy bug'
|
56
|
+
assert_equal "BUG-30", @described_class.parse(@entry)
|
36
57
|
end
|
37
58
|
end
|
38
59
|
end
|
data/time_log_robot.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.required_ruby_version = '>= 2.0'
|
22
|
+
spec.add_development_dependency 'pry'
|
22
23
|
spec.add_development_dependency 'bundler', '~> 1.3'
|
23
24
|
spec.add_development_dependency 'rake', '~> 10.5'
|
24
25
|
spec.add_development_dependency 'minitest', '~> 5.8'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: time_log_robot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark J. Lehman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-06-
|
11
|
+
date: 2016-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,9 +193,13 @@ files:
|
|
179
193
|
- Rakefile
|
180
194
|
- bin/time_log_robot
|
181
195
|
- lib/time_log_robot.rb
|
196
|
+
- lib/time_log_robot/entry.rb
|
182
197
|
- lib/time_log_robot/jira/issue_key_parser.rb
|
183
198
|
- lib/time_log_robot/jira/payload_builder.rb
|
184
199
|
- lib/time_log_robot/jira/work_logger.rb
|
200
|
+
- lib/time_log_robot/reporter.rb
|
201
|
+
- lib/time_log_robot/tagger.rb
|
202
|
+
- lib/time_log_robot/toggl/entry.rb
|
185
203
|
- lib/time_log_robot/toggl/report.rb
|
186
204
|
- lib/time_log_robot/toggl/tagger.rb
|
187
205
|
- lib/time_log_robot/version.rb
|
@@ -215,4 +233,3 @@ summary: Automate work time logging
|
|
215
233
|
test_files:
|
216
234
|
- test/test_helper.rb
|
217
235
|
- test/time_log_robot/jira/issue_key_parser_test.rb
|
218
|
-
has_rdoc:
|