timetrap_toggl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []