time_log_robot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.settings.yml +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +43 -0
- data/LICENSE.txt +22 -0
- data/README.md +81 -0
- data/Rakefile +10 -0
- data/bin/time_log_robot +111 -0
- data/lib/time_log_robot/jira/payload_builder.rb +21 -0
- data/lib/time_log_robot/jira/work_logger.rb +100 -0
- data/lib/time_log_robot/toggl/report.rb +48 -0
- data/lib/time_log_robot/toggl/tagger.rb +42 -0
- data/lib/time_log_robot/version.rb +3 -0
- data/lib/time_log_robot.rb +55 -0
- data/time_log_robot.gemspec +30 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 02779016739df4a954e1bdb2a1497dec6fc5c75f
|
4
|
+
data.tar.gz: bb7e9647e14ed514a143ac06f9e3e855388a2446
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: dfec06ec09c95fef5e4085cbdbc8a8fe4a23efc9a58fa18f1f09b7f01cc0396592c78b00b4c5f1c5b225d1a54906bdaeb91ab31ca32aba5dab4e11a26b6fe916
|
7
|
+
data.tar.gz: 7fad04cb28781642f91bb496551f3cf38ddf6e5caeb633a22ce7c7d8811ba40823402fb7725e070d9839aa9b8ff0ac686b316e932aa9e884ac16b363d19f99df
|
data/.gitignore
ADDED
data/.settings.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
---
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
time_log_robot (0.1.0)
|
5
|
+
activesupport (~> 4.2, >= 4.2.6)
|
6
|
+
commander (~> 4.1, >= 4.1.6)
|
7
|
+
httparty (~> 0.13, >= 0.13.0)
|
8
|
+
json (~> 1.8, >= 1.8.1)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activesupport (4.2.6)
|
14
|
+
i18n (~> 0.7)
|
15
|
+
json (~> 1.7, >= 1.7.7)
|
16
|
+
minitest (~> 5.1)
|
17
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
18
|
+
tzinfo (~> 1.1)
|
19
|
+
commander (4.1.6)
|
20
|
+
highline (~> 1.6.11)
|
21
|
+
highline (1.6.21)
|
22
|
+
httparty (0.13.3)
|
23
|
+
json (~> 1.8)
|
24
|
+
multi_xml (>= 0.5.2)
|
25
|
+
i18n (0.7.0)
|
26
|
+
json (1.8.3)
|
27
|
+
minitest (5.8.4)
|
28
|
+
multi_xml (0.5.5)
|
29
|
+
rake (10.5.0)
|
30
|
+
thread_safe (0.3.5)
|
31
|
+
tzinfo (1.2.2)
|
32
|
+
thread_safe (~> 0.1)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
bundler (~> 1.3)
|
39
|
+
rake (~> 10.5)
|
40
|
+
time_log_robot!
|
41
|
+
|
42
|
+
BUNDLED WITH
|
43
|
+
1.12.3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 Mark J. Lehman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Time Log Robot
|
2
|
+
***Let the robot do your work time logging.***
|
3
|
+
|
4
|
+
This is an integration between project management tools (like JIRA) and time logging apps (like Toggl). Currently, it only works as an import from Toggl into JIRA.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
~~Download this gem:~~ This is not yet on RubyGems, but once it is, you will be able to download like so:
|
9
|
+
|
10
|
+
gem install time_log_robot
|
11
|
+
|
12
|
+
Until then...
|
13
|
+
|
14
|
+
rake install
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
The simplest usage is just to invoke the robot:
|
19
|
+
|
20
|
+
time_log_robot
|
21
|
+
|
22
|
+
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):
|
23
|
+
|
24
|
+
time_log_robot --since 2016-05-01
|
25
|
+
|
26
|
+
On start, the robot will ask you for these details:
|
27
|
+
|
28
|
+
TOGGL_TOKEN
|
29
|
+
TOGGL_WORKSPACE_ID
|
30
|
+
TOGGL_USER_AGENT
|
31
|
+
TOGGL_DEFAULT_LOG_TAG
|
32
|
+
JIRA_USERNAME
|
33
|
+
JIRA_PASSWORD
|
34
|
+
|
35
|
+
The robot has a memory like a steel trap, so after you run it the first time, it will remember your configuration settings and you'll never need to enter them again. However, if you enter the wrong info, or need to change it at some point, you can always overwrite the configuration:
|
36
|
+
|
37
|
+
time_log_robot --overwrite
|
38
|
+
|
39
|
+
Or, if you want to pop open the internals, the robot saves all configuration in `path/to/gem/config/settings.yml`, so open up that file and edit to your heart's content.
|
40
|
+
|
41
|
+
### Configuration
|
42
|
+
|
43
|
+
Here are some notes about how to find the appropriate values for those environment variables:
|
44
|
+
|
45
|
+
* **TOGGL_TOKEN**: In your Toggl account, go to your profile page and look for the API token at the bottom.
|
46
|
+
* **TOGGL_WORKSPACE_ID**: This is a little trickier. Your workspaces usually only show a human-readable name to you in Toggl's UI, and here you need the workspace's machine ID. But you can do a curl request to find it like this (replacing **TOGGL_TOKEN** with your token from above):
|
47
|
+
|
48
|
+
curl -v -u TOGGL_TOKEN:api_token \ -X GET https://www.toggl.com/api/v8/workspaces
|
49
|
+
|
50
|
+
Look at the result and find the id given for the workspace you want to use.
|
51
|
+
|
52
|
+
* **TOGGL_USER_AGENT**: This is your Toggl username, usually your email.
|
53
|
+
* **TOGGL_DEFAULT_LOG_TAG**: This is the tag name you would like to use for tagging your Toggl time entries as they are logged to JIRA.
|
54
|
+
* **JIRA_USERNAME**: This is your JIRA username, which is not an email, but usually your email minus the "@domain.com"
|
55
|
+
* **JIRA_PASSWORD**: I know there's a lot of jargon, but some of these are pretty self-explanatory. :trollface:
|
56
|
+
|
57
|
+
### Help
|
58
|
+
|
59
|
+
For more details use the help flag:
|
60
|
+
|
61
|
+
time_log_robot --help
|
62
|
+
|
63
|
+
|
64
|
+
## Development
|
65
|
+
To see available rake tasks for development
|
66
|
+
|
67
|
+
rake -T
|
68
|
+
|
69
|
+
To run the app in IRB for debugging run
|
70
|
+
|
71
|
+
rake console
|
72
|
+
|
73
|
+
(Note: If you've built the gem and have committed the `.gem` file, none of your `rake` commands will work. You'll need to remove the built gem and commit the deletion.)
|
74
|
+
|
75
|
+
## Contributing
|
76
|
+
|
77
|
+
1. Fork it
|
78
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
79
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
80
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
81
|
+
5. Create new Pull Request to Dev
|
data/Rakefile
ADDED
data/bin/time_log_robot
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'commander/import'
|
4
|
+
require 'time_log_robot'
|
5
|
+
require 'time_log_robot/version'
|
6
|
+
require 'time_log_robot/toggl/tagger'
|
7
|
+
require 'time_log_robot/toggl/report'
|
8
|
+
require 'time_log_robot/jira/payload_builder'
|
9
|
+
require 'time_log_robot/jira/work_logger'
|
10
|
+
require 'yaml'
|
11
|
+
|
12
|
+
program :name, 'time_log_robot'
|
13
|
+
program :version, TimeLogRobot::VERSION
|
14
|
+
program :description, 'Logs work from a time tracker tool to a project
|
15
|
+
management tool. Currently only supports Toggl and JIRA.'
|
16
|
+
|
17
|
+
program :help, 'Author', 'Mark J. Lehman <markopolo@gmail.com>'
|
18
|
+
|
19
|
+
global_option('-i', '--inputs_help', 'Learn how and where to find the inputs you need to get this working')
|
20
|
+
|
21
|
+
command :run do |c|
|
22
|
+
c.syntax = 'time_log_robot run [options]'
|
23
|
+
|
24
|
+
c.description = 'Logs your time with optional since (default is the previous Saturday).'
|
25
|
+
|
26
|
+
c.example 'usage', 'time_log_robot run'
|
27
|
+
c.example 'shorthand', 'time_log_robot'
|
28
|
+
c.example 'with optional parameter "since"', 'time_log_robot run --since "2015-10-21"'
|
29
|
+
|
30
|
+
c.option '--since STRING', String, 'The date from which to log time entries. Must be in YYYY-MM-DD format. Default is the previous Saturday.'
|
31
|
+
|
32
|
+
c.option '--overwrite', 'Your settings are automatically written to a file - `~/.time_log_robot_settings.yml` - to be used again the next time you invoke the robot. Use this flag if you need to overwrite those settings.'
|
33
|
+
|
34
|
+
c.action do |args, options|
|
35
|
+
if options.inputs_help
|
36
|
+
print_inputs_help
|
37
|
+
else
|
38
|
+
create_settings_file_if_nonexistent
|
39
|
+
|
40
|
+
fetch_envars_from_config unless options.overwrite
|
41
|
+
|
42
|
+
missing_envars = get_missing_envars
|
43
|
+
|
44
|
+
since = DateTime.parse(options.since).to_s unless options.since.nil?
|
45
|
+
|
46
|
+
TimeLogRobot.start(since)
|
47
|
+
|
48
|
+
write_missing_envars(missing_envars) if missing_envars.any?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def print_inputs_help
|
54
|
+
TimeLogRobot.envars_help.each_pair do |envar, help_text|
|
55
|
+
puts envar
|
56
|
+
puts help_text
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_missing_envars
|
61
|
+
missing_envars = {}
|
62
|
+
|
63
|
+
TimeLogRobot.envars.each do |key|
|
64
|
+
string_key = key.to_s
|
65
|
+
unless ENV[string_key]
|
66
|
+
ENV[string_key] = ask "Enter your #{string_key}: " do |char|
|
67
|
+
char.echo = '*' if string_key =~ /password|token/i
|
68
|
+
end
|
69
|
+
if ENV[string_key].length == 0
|
70
|
+
puts 'Invalid input. This is a required field.'
|
71
|
+
exit
|
72
|
+
end
|
73
|
+
missing_envars[string_key] = ENV[string_key]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
return missing_envars
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_envars_from_config
|
81
|
+
return unless envars = YAML.load_file(settings_file_path)
|
82
|
+
envars.each_pair do |key, value|
|
83
|
+
ENV[key.upcase] = value
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def write_missing_envars(missing_envars={})
|
88
|
+
puts "\nTo avoid entering setup information each time, the following configuration has been stored in `#{settings_file_path}`:"
|
89
|
+
missing_envars.each_pair do |key, value|
|
90
|
+
if key =~ /password|token/i
|
91
|
+
puts "\t#{key}=[FILTERED]"
|
92
|
+
else
|
93
|
+
puts "\t#{key}=#{value}"
|
94
|
+
end
|
95
|
+
|
96
|
+
data = YAML.load_file(settings_file_path) || {}
|
97
|
+
data[key.downcase] = value
|
98
|
+
File.open(settings_file_path, 'w') { |f| YAML.dump(data, f) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def create_settings_file_if_nonexistent
|
103
|
+
File.new(settings_file_path, "w+") unless File.exists?(settings_file_path)
|
104
|
+
end
|
105
|
+
|
106
|
+
def settings_file_path
|
107
|
+
File.join(TimeLogRobot.root, '.settings.yml')
|
108
|
+
end
|
109
|
+
|
110
|
+
# set default action for gem
|
111
|
+
default_command :run
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module TimeLogRobot
|
2
|
+
module JIRA
|
3
|
+
class PayloadBuilder
|
4
|
+
attr_accessor :start, :duration_in_seconds, :comment
|
5
|
+
|
6
|
+
def initialize(start:, duration_in_seconds:, comment:)
|
7
|
+
@start = start
|
8
|
+
@duration_in_seconds = duration_in_seconds
|
9
|
+
@comment = comment
|
10
|
+
end
|
11
|
+
|
12
|
+
def build
|
13
|
+
{
|
14
|
+
"comment" => comment,
|
15
|
+
"started" => start.to_s,
|
16
|
+
"timeSpentSeconds" => duration_in_seconds
|
17
|
+
}.to_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module TimeLogRobot
|
2
|
+
module JIRA
|
3
|
+
class WorkLogger
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
attr_accessor :username, :password, :time_entries, :log_tags
|
7
|
+
|
8
|
+
base_uri 'https://hranswerlink.atlassian.net/rest/api/2'
|
9
|
+
|
10
|
+
def initialize(time_entries:, log_tags: [ENV['TOGGL_DEFAULT_LOG_TAG']])
|
11
|
+
@username = ENV['JIRA_USERNAME']
|
12
|
+
@password = ENV['JIRA_PASSWORD']
|
13
|
+
@time_entries = time_entries
|
14
|
+
@log_tags = log_tags
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_all
|
18
|
+
time_entries.each do |entry|
|
19
|
+
log(entry) unless is_logged?(entry)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def is_logged?(entry)
|
26
|
+
(log_tags - entry['tags']).size < log_tags.size
|
27
|
+
end
|
28
|
+
|
29
|
+
def log(entry)
|
30
|
+
issue_key = parse_issue_key(entry)
|
31
|
+
payload = build_payload(entry)
|
32
|
+
puts "Attempting to log #{human_readable_duration(parse_duration(entry))}"
|
33
|
+
puts "starting on #{parse_start(entry)}"
|
34
|
+
puts "to #{entry['description']}"
|
35
|
+
puts "with comment #{parse_comment(entry)}" unless parse_comment(entry).nil?
|
36
|
+
response = self.class.post("/issue/#{issue_key}/worklog", basic_auth: auth, headers: headers, body: payload)
|
37
|
+
if response.success?
|
38
|
+
puts "Success"
|
39
|
+
puts '*' * 20
|
40
|
+
set_entry_as_logged(entry)
|
41
|
+
else
|
42
|
+
puts "Failed! Response from JIRA:"
|
43
|
+
puts response
|
44
|
+
puts "(Hint: Did you forget to put the JIRA issue key in your Toggl entry?"
|
45
|
+
puts '*' * 20
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_entry_as_logged(entry)
|
50
|
+
Toggl::Tagger.new(tags: log_tags).update(entry_id: entry['id'])
|
51
|
+
end
|
52
|
+
|
53
|
+
def auth
|
54
|
+
{
|
55
|
+
username: username,
|
56
|
+
password: password
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# @TODO Extract since it's used in several different models
|
61
|
+
def headers
|
62
|
+
{ 'Content-Type' => 'application/json' }
|
63
|
+
end
|
64
|
+
|
65
|
+
def build_payload(entry)
|
66
|
+
JIRA::PayloadBuilder.new(
|
67
|
+
start: parse_start(entry),
|
68
|
+
duration_in_seconds: parse_duration(entry),
|
69
|
+
comment: parse_comment(entry)
|
70
|
+
).build
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_start(entry)
|
74
|
+
DateTime.strptime(entry['start'], "%FT%T%:z").strftime("%FT%T.%L%z")
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_duration(entry)
|
78
|
+
entry['dur']/1000 # Toggl sends times in milliseconds
|
79
|
+
end
|
80
|
+
|
81
|
+
def human_readable_duration(seconds)
|
82
|
+
total_minutes = seconds/60
|
83
|
+
hours = total_minutes/60
|
84
|
+
remaining_minutes = total_minutes - hours * 60
|
85
|
+
"#{hours}h #{remaining_minutes}m"
|
86
|
+
end
|
87
|
+
|
88
|
+
# @TODO figure out how to capture both of this in one .match call with one set of regex
|
89
|
+
def parse_issue_key(entry)
|
90
|
+
matches = entry['description'].match(/(\[(?<issue_key>[^\]]*)\])/)
|
91
|
+
matches['issue_key'] if matches.present?
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse_comment(entry)
|
95
|
+
matches = entry['description'].match(/(\{(?<comment>[^\}]*)\})/)
|
96
|
+
matches['comment'] if matches.present?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'active_support/core_ext/date/calculations'
|
2
|
+
|
3
|
+
module TimeLogRobot
|
4
|
+
module Toggl
|
5
|
+
class Report
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
attr_accessor :token, :workspace_id, :user_agent
|
9
|
+
|
10
|
+
base_uri 'https://toggl.com/reports/api/v2'
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@token = ENV['TOGGL_TOKEN']
|
14
|
+
@workspace_id = ENV['TOGGL_WORKSPACE_ID']
|
15
|
+
@user_agent = ENV['TOGGL_USER_AGENT']
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch(since: nil)
|
19
|
+
since ||= Date.today.beginning_of_week(:saturday).to_time
|
20
|
+
response = self.class.get('/details', basic_auth: auth, query: query(since))
|
21
|
+
if response.success?
|
22
|
+
response['data']
|
23
|
+
else
|
24
|
+
raise FetchError, response['error']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
class FetchError < Exception; end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def auth
|
32
|
+
{
|
33
|
+
username: token,
|
34
|
+
password: "api_token"
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def query(since)
|
39
|
+
{
|
40
|
+
workspace_id: workspace_id,
|
41
|
+
user_agent: user_agent,
|
42
|
+
since: since
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module TimeLogRobot
|
2
|
+
module Toggl
|
3
|
+
class Tagger
|
4
|
+
include HTTParty
|
5
|
+
|
6
|
+
attr_accessor :token, :tags
|
7
|
+
|
8
|
+
base_uri 'https://toggl.com/api/v8/time_entries'
|
9
|
+
|
10
|
+
def initialize(tags:[ENV['TOGGL_DEFAULT_LOG_TAG']])
|
11
|
+
@token = ENV['TOGGL_TOKEN']
|
12
|
+
@tags = tags
|
13
|
+
end
|
14
|
+
|
15
|
+
def update(entry_id:)
|
16
|
+
self.class.put("/#{entry_id}", basic_auth: auth, headers: headers, body: body)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def auth
|
22
|
+
{
|
23
|
+
username: token,
|
24
|
+
password: "api_token"
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def headers
|
29
|
+
{ 'Content-Type' => 'application/json' }
|
30
|
+
end
|
31
|
+
|
32
|
+
def body
|
33
|
+
{
|
34
|
+
time_entry:
|
35
|
+
{
|
36
|
+
tags: tags
|
37
|
+
}
|
38
|
+
}.to_json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
module TimeLogRobot
|
5
|
+
def self.root
|
6
|
+
File.dirname __dir__
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.start(since)
|
10
|
+
time_entries = fetch_time_entries(since)
|
11
|
+
JIRA::WorkLogger.new(time_entries: time_entries).log_all
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.fetch_time_entries(since)
|
15
|
+
if since.nil?
|
16
|
+
Toggl::Report.new.fetch
|
17
|
+
else
|
18
|
+
Toggl::Report.new.fetch(since: since)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.envars
|
23
|
+
envars_help.keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.envars_help
|
27
|
+
{
|
28
|
+
TOGGL_TOKEN:
|
29
|
+
"In your Toggl account, go to your profile page and look for the API token at the bottom.\n\n",
|
30
|
+
|
31
|
+
TOGGL_WORKSPACE_ID:
|
32
|
+
"This is a little trickier. Your workspaces usually only show a human-readable name to you in Toggl's UI, and here you need the workspace machine ID. But you can do a curl request to find it like this (replacing TOGGL_TOKEN with your token from above):
|
33
|
+
|
34
|
+
\tcurl -v -u TOGGL_TOKEN:api_token \ -X GET https://www.toggl.com/api/v8/workspaces
|
35
|
+
|
36
|
+
Look at the result and find the id given for the workspace you want to use.\n\n",
|
37
|
+
|
38
|
+
TOGGL_USER_AGENT:
|
39
|
+
"This is your Toggl username, usually your email.\n\n",
|
40
|
+
|
41
|
+
TOGGL_DEFAULT_LOG_TAG:
|
42
|
+
"This is the tag name you would like to use for tagging your Toggl time entries as they are logged to JIRA.\n\n",
|
43
|
+
|
44
|
+
JIRA_USERNAME:
|
45
|
+
"This is your JIRA username, which is not an email, but usually your email minus the '@domain.com'\n\n",
|
46
|
+
|
47
|
+
JIRA_PASSWORD:
|
48
|
+
"I know there's a lot of jargon, but some of these are pretty self-explanatory
|
49
|
+
┌─┐
|
50
|
+
┴─┴
|
51
|
+
ಠ_ರೃ
|
52
|
+
\n"
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
# require 'time_log_robot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'time_log_robot'
|
8
|
+
spec.version = '0.1.0'
|
9
|
+
# spec.version = TimeLogRobot::VERSION
|
10
|
+
spec.authors = ['Mark J. Lehman']
|
11
|
+
spec.email = ['markopolo@gmail.com']
|
12
|
+
spec.description = %q{Automate time logging from tools like Toggl to project management software such as JIRA}
|
13
|
+
spec.summary = %q{Automate work time logging}
|
14
|
+
spec.homepage = 'https://github.com/supremebeing7/time_log_robot'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.required_ruby_version = '>= 2.0'
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.5'
|
25
|
+
|
26
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.6'
|
27
|
+
spec.add_runtime_dependency 'commander', '~> 4.1', '>= 4.1.6'
|
28
|
+
spec.add_runtime_dependency 'httparty', '~> 0.13', '>= 0.13.0'
|
29
|
+
spec.add_runtime_dependency 'json', '~> 1.8', '>= 1.8.1'
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: time_log_robot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark J. Lehman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.2'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 4.2.6
|
51
|
+
type: :runtime
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '4.2'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 4.2.6
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: commander
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4.1'
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 4.1.6
|
71
|
+
type: :runtime
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '4.1'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 4.1.6
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: httparty
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0.13'
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 0.13.0
|
91
|
+
type: :runtime
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0.13'
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 0.13.0
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: json
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - "~>"
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '1.8'
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.8.1
|
111
|
+
type: :runtime
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.8'
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: 1.8.1
|
121
|
+
description: Automate time logging from tools like Toggl to project management software
|
122
|
+
such as JIRA
|
123
|
+
email:
|
124
|
+
- markopolo@gmail.com
|
125
|
+
executables:
|
126
|
+
- time_log_robot
|
127
|
+
extensions: []
|
128
|
+
extra_rdoc_files: []
|
129
|
+
files:
|
130
|
+
- ".gitignore"
|
131
|
+
- ".settings.yml"
|
132
|
+
- Gemfile
|
133
|
+
- Gemfile.lock
|
134
|
+
- LICENSE.txt
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- bin/time_log_robot
|
138
|
+
- lib/time_log_robot.rb
|
139
|
+
- lib/time_log_robot/jira/payload_builder.rb
|
140
|
+
- lib/time_log_robot/jira/work_logger.rb
|
141
|
+
- lib/time_log_robot/toggl/report.rb
|
142
|
+
- lib/time_log_robot/toggl/tagger.rb
|
143
|
+
- lib/time_log_robot/version.rb
|
144
|
+
- time_log_robot.gemspec
|
145
|
+
homepage: https://github.com/supremebeing7/time_log_robot
|
146
|
+
licenses:
|
147
|
+
- MIT
|
148
|
+
metadata: {}
|
149
|
+
post_install_message:
|
150
|
+
rdoc_options: []
|
151
|
+
require_paths:
|
152
|
+
- lib
|
153
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
154
|
+
requirements:
|
155
|
+
- - ">="
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '2.0'
|
158
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
requirements: []
|
164
|
+
rubyforge_project:
|
165
|
+
rubygems_version: 2.4.8
|
166
|
+
signing_key:
|
167
|
+
specification_version: 4
|
168
|
+
summary: Automate work time logging
|
169
|
+
test_files: []
|
170
|
+
has_rdoc:
|