timetrap_toggl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/timetrap-toggl.rb +38 -0
- data/lib/timetrap_toggl/config.rb +64 -0
- data/lib/timetrap_toggl/formatter.rb +61 -0
- data/lib/timetrap_toggl/output.rb +58 -0
- data/lib/timetrap_toggl/toggler.rb +29 -0
- data/lib/timetrap_toggl/version.rb +3 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7e4b6a94a6eeec97774861268215f1ab067c7f26
|
4
|
+
data.tar.gz: 7f7fe8f8e2d77187db7e0e92ed6c5348dd315b56
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a82dc3f012b016b12499579d970012bc9723c8109e02edc81c0bfa7c043be070aea0a3353614820d9da342ae2f955e5054497fb9e4c5ba43940dbcd1b5bed39c
|
7
|
+
data.tar.gz: df9e79006be48f7fc3813044ce4db54cb1ef8c0dffa8349df31adcb05b39313ac69c8eda84554651f3484ed574f85174f1ee06296006340f03dbafb952621061
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative './timetrap_toggl/version'
|
2
|
+
require_relative './timetrap_toggl/config'
|
3
|
+
require_relative './timetrap_toggl/formatter'
|
4
|
+
require_relative './timetrap_toggl/toggler'
|
5
|
+
require_relative './timetrap_toggl/output'
|
6
|
+
|
7
|
+
begin
|
8
|
+
Module.const_get('Timetrap')
|
9
|
+
rescue NameError
|
10
|
+
module Timetrap;
|
11
|
+
module Formatters; end
|
12
|
+
Config = { 'toggl' => { 'aliases' => {} } }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Timetrap::Formatters::Toggl
|
17
|
+
attr_reader :entries
|
18
|
+
attr_writer :client, :config
|
19
|
+
|
20
|
+
def initialize(entries)
|
21
|
+
@entries = entries
|
22
|
+
end
|
23
|
+
|
24
|
+
def output
|
25
|
+
results = entries.map { |entry| TimetrapToggl::Formatter.new(entry, config).format }
|
26
|
+
|
27
|
+
toggler = TimetrapToggl::Toggler.new(results, config.client)
|
28
|
+
results = toggler.toggl
|
29
|
+
|
30
|
+
TimetrapToggl::Output.new(results, config.projects).generate
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def config
|
36
|
+
@config ||= TimetrapToggl::Config.new
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "togglv8"
|
2
|
+
|
3
|
+
class TimetrapToggl::Config
|
4
|
+
MissingTogglConfig = Class.new(StandardError)
|
5
|
+
MissingTogglAliases = Class.new(StandardError)
|
6
|
+
MissingTogglSubdomain = Class.new(StandardError)
|
7
|
+
DEFAULT_ROUND_IN_MINUTES = 15
|
8
|
+
|
9
|
+
attr_reader :timetrap_config
|
10
|
+
|
11
|
+
def initialize(timetrap_config = Timetrap::Config)
|
12
|
+
@timetrap_config = timetrap_config
|
13
|
+
end
|
14
|
+
|
15
|
+
def api_token
|
16
|
+
config['api_token']
|
17
|
+
end
|
18
|
+
|
19
|
+
def subdomain
|
20
|
+
ensure_subdomain!
|
21
|
+
|
22
|
+
config['subdomain']
|
23
|
+
end
|
24
|
+
|
25
|
+
def round_in_minutes
|
26
|
+
config['round_in_minutes'] || DEFAULT_ROUND_IN_MINUTES
|
27
|
+
end
|
28
|
+
|
29
|
+
def client
|
30
|
+
@client ||= TogglV8::API.new(api_token)
|
31
|
+
end
|
32
|
+
|
33
|
+
def workspace
|
34
|
+
@workspace ||= client.workspaces.find { |w| w["name"] == config["workspace"] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def projects
|
38
|
+
@projects ||= client.projects(workspace["id"])
|
39
|
+
end
|
40
|
+
|
41
|
+
def aliases
|
42
|
+
ensure_aliases!
|
43
|
+
|
44
|
+
config['aliases']
|
45
|
+
end
|
46
|
+
|
47
|
+
def config
|
48
|
+
ensure_config!
|
49
|
+
|
50
|
+
timetrap_config['toggl']
|
51
|
+
end
|
52
|
+
|
53
|
+
def ensure_config!
|
54
|
+
fail(MissingTogglConfig, 'Missing toggl key in .timetrap.yml config file') if timetrap_config.nil? || timetrap_config['toggl'].nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def ensure_aliases!
|
58
|
+
fail(MissingTogglAliases, 'Missing aliases key in .timetrap.yml config file') if config['aliases'].nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
def ensure_subdomain!
|
62
|
+
fail(MissingTogglSubdomain, 'Missing subdomain key in .timetrap.yml config file') if config['subdomain'].nil?
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class TimetrapToggl::Formatter
|
2
|
+
TOGGABLE_REGEX = /@(.*)/
|
3
|
+
|
4
|
+
attr_reader :entry, :config
|
5
|
+
|
6
|
+
def initialize(entry, config)
|
7
|
+
@entry = entry
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def format
|
12
|
+
if project["id"]
|
13
|
+
{
|
14
|
+
"description" => description,
|
15
|
+
"duration" => duration,
|
16
|
+
"start" => start,
|
17
|
+
"pid" => project["id"],
|
18
|
+
"created_with" => "timetrap-toggl",
|
19
|
+
}
|
20
|
+
else
|
21
|
+
{
|
22
|
+
error: "Project #{project["name"]} does not exist",
|
23
|
+
note: entry[:note]
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
TogglV8::API.new.iso8601(entry[:start].to_datetime)
|
30
|
+
end
|
31
|
+
|
32
|
+
def project
|
33
|
+
config.projects.find { |p| p["name"].downcase == code.downcase }
|
34
|
+
end
|
35
|
+
|
36
|
+
def task_id
|
37
|
+
alias_config[:task_id]
|
38
|
+
end
|
39
|
+
|
40
|
+
def round_in_minutes
|
41
|
+
config.round_in_minutes
|
42
|
+
end
|
43
|
+
|
44
|
+
def code
|
45
|
+
if match = TOGGABLE_REGEX.match(entry[:note])
|
46
|
+
code = match[1]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
entry[:note].gsub("@#{code}", '').strip
|
52
|
+
end
|
53
|
+
|
54
|
+
def duration
|
55
|
+
seconds_for_time(entry[:start], entry[:end])
|
56
|
+
end
|
57
|
+
|
58
|
+
def seconds_for_time(start_time, end_time)
|
59
|
+
(end_time - start_time).to_i
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class TimetrapToggl::Output
|
2
|
+
LINE_DIVIDER = '-' * 80
|
3
|
+
SUBMITTED_HEADER = "Submitted entries\n#{LINE_DIVIDER}"
|
4
|
+
FAILED_HEADER = "Failed entries\n#{LINE_DIVIDER}"
|
5
|
+
|
6
|
+
attr_reader :results, :projects
|
7
|
+
|
8
|
+
def initialize(results, projects)
|
9
|
+
@results = results
|
10
|
+
@projects = projects
|
11
|
+
end
|
12
|
+
|
13
|
+
def generate
|
14
|
+
messages = [stats]
|
15
|
+
|
16
|
+
unless submitted.empty?
|
17
|
+
messages << SUBMITTED_HEADER
|
18
|
+
messages += submitted.map do |submitted|
|
19
|
+
project = projects.find { |p| p["id"] == submitted["pid"] }
|
20
|
+
success_message("#{project["name"]} - #{submitted["description"]}")
|
21
|
+
end
|
22
|
+
messages << "\n"
|
23
|
+
end
|
24
|
+
|
25
|
+
unless failed.empty?
|
26
|
+
messages << FAILED_HEADER
|
27
|
+
messages += failed.map do |failed|
|
28
|
+
project = projects.find { |p| p["id"] == failed["pid"] }
|
29
|
+
failed_message(failed[:note], failed[:error])
|
30
|
+
end
|
31
|
+
messages << "\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
messages.join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def stats
|
40
|
+
"Submitted: #{submitted.count}\nFailed: #{failed.count}\n"
|
41
|
+
end
|
42
|
+
|
43
|
+
def submitted
|
44
|
+
results.fetch(:submitted, [])
|
45
|
+
end
|
46
|
+
|
47
|
+
def failed
|
48
|
+
results.fetch(:failed, [])
|
49
|
+
end
|
50
|
+
|
51
|
+
def success_message(note)
|
52
|
+
"Submitted: #{note}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def failed_message(note, error)
|
56
|
+
"Failed (#{error}): #{note}"
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class TimetrapToggl::Toggler
|
2
|
+
attr_reader :entries, :client
|
3
|
+
|
4
|
+
def initialize(entries, client)
|
5
|
+
@entries = entries
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def toggl
|
10
|
+
entries.each do |entry|
|
11
|
+
if entry.key? :error
|
12
|
+
failed << entry
|
13
|
+
else
|
14
|
+
client.create_time_entry(entry)
|
15
|
+
submitted << entry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
{ submitted: submitted, failed: failed }
|
20
|
+
end
|
21
|
+
|
22
|
+
def submitted
|
23
|
+
@submitted ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def failed
|
27
|
+
@failed ||= []
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: timetrap_toggl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Miguel Palhas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: timetrap
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.7.0
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.7'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.7.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: togglv8
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.2.1
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.2.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.0'
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 3.0.0
|
57
|
+
type: :development
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '3.0'
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 3.0.0
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: pry
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0.10'
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: 0.10.0
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.10'
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: 0.10.0
|
87
|
+
description: |2
|
88
|
+
timetrap-toggl bridges the gap between your entries in Timetrap and your
|
89
|
+
project tasks in Toggl allowing for incredible easy timesheet
|
90
|
+
submissions.
|
91
|
+
email: mpalhas@gmail.com
|
92
|
+
executables: []
|
93
|
+
extensions: []
|
94
|
+
extra_rdoc_files: []
|
95
|
+
files:
|
96
|
+
- lib/timetrap-toggl.rb
|
97
|
+
- lib/timetrap_toggl/config.rb
|
98
|
+
- lib/timetrap_toggl/formatter.rb
|
99
|
+
- lib/timetrap_toggl/output.rb
|
100
|
+
- lib/timetrap_toggl/toggler.rb
|
101
|
+
- lib/timetrap_toggl/version.rb
|
102
|
+
homepage: https://github.com/dblandin/timetrap-toggl
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
106
|
+
post_install_message:
|
107
|
+
rdoc_options: []
|
108
|
+
require_paths:
|
109
|
+
- lib
|
110
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
requirements: []
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.6.7
|
123
|
+
signing_key:
|
124
|
+
specification_version: 4
|
125
|
+
summary: A toggl formatter for Timetrap
|
126
|
+
test_files: []
|