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 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
@@ -0,0 +1,3 @@
1
+ module TimetrapToggl
2
+ VERSION = '0.1.0'
3
+ 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: []