timesheet-toggl 0.0.2

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: 7144dce52c6d9a109b3e9d095540359aeecd6cae
4
+ data.tar.gz: 4e37f892a05b06dd46798c96eaf7cc22db88f6f8
5
+ SHA512:
6
+ metadata.gz: c9ba21d9910947f6c2278e83a302b0f77f67fbf158d125fdf2ef4502a96e887ab6139fa0634d411efc3ee53d889b137f38d4cd6192e806b6e688875902434e72
7
+ data.tar.gz: 133104f772bcf93c091ffa1da79cfc4ceecd8fbb99ad8f2dca488ae8abc74a63587d279b454660a9ad83de0ff7a37262a8befa980949f89ef480030113853002
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+
16
+ *.swp
17
+ *.swo
18
+ *.swap
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in timesheet-toggl.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Sergey Smagin
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,31 @@
1
+ # Timesheet::Toggl
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'timesheet-toggl'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install timesheet-toggl
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/timesheet-toggl/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,5 @@
1
+ module Timesheet
2
+ module Toggl
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -0,0 +1,150 @@
1
+ require_relative 'toggl/version'
2
+ require 'curb'
3
+ require 'active_support/core_ext/time'
4
+ require 'active_support/core_ext/date'
5
+ require 'json'
6
+ require 'pp'
7
+
8
+ module Timesheet
9
+ # Export reports from timesheet to toggle.
10
+ # @example
11
+ # Timesheet::Toggl.new(config).sync_last_month
12
+ # Timesheet::Toggl.new(config).sync_last_month([14, 88]) # where 14, 88 -- user ids.
13
+ # Timesheet::Toggl.new(config).sync(from: (Date.today - 1.month), to: Date.today)
14
+ #
15
+ class Toggl
16
+ attr_accessor :config
17
+
18
+ BASE_URI = 'https://toggl.com/reports/api/v2/details'
19
+
20
+ # Example of config:
21
+ # api_token: 1971800d4d82861d8f2c1651fea4d212
22
+ # worspace_id: 123
23
+ # source_name: 'toggl'
24
+ # redmine_time_entry_class: 'TimeEntryRedmine' # optional
25
+ #
26
+ def initialize(hash)
27
+ @config = hash
28
+ name = hash[:source_name]
29
+ src = DataSource.create_with(name: name).
30
+ find_or_create_by(config_section_id: name, connector_type: 'toggl')
31
+ @config[:source_id] = src.id
32
+ end
33
+
34
+ def sync_last_month(user_ids = [])
35
+ from = (Date.today << 1).beginning_of_month
36
+ to = Date.today
37
+ synchronize(from, to, user_ids)
38
+ end
39
+
40
+ # curl -v -u 1971800d4d82861d8f2c1651fea4d212:api_token
41
+ # -X GET "https://toggl.com/reports/api/v2/details?
42
+ # workspace_id=123&
43
+ # since=2013-05-19&
44
+ # until=2013-05-20&
45
+ # user_agent=api_test"
46
+ #
47
+ def synchronize(from, to, user_ids = [])
48
+ params = {
49
+ workspace_id: config[:workspace_id],
50
+ since: from.to_s,
51
+ until: to.to_s,
52
+ user_agent: 'export_to_timesheet',
53
+ user_ids: user_ids.join(',')
54
+ }
55
+ first_page = fetch(params, 1)
56
+ push(first_page)
57
+ pages = first_page[:total_count] / first_page[:per_page]
58
+ pages.times { |page| sync(params, page + 2) }
59
+ end
60
+
61
+ # Since toggl has per-page api, we will follow them.
62
+ #
63
+ def sync(params, page)
64
+ data = fetch(params, page)
65
+ push(data)
66
+ end
67
+
68
+ # Get data from toggl.
69
+ # @param params [Hash] query params for api.
70
+ # @param page [Integer] number of page.
71
+ # @returns [Hash] parsed response from toggl.
72
+ #
73
+ def fetch(params, page)
74
+ print "page #{page}: "
75
+ response = Curl.get(BASE_URI, params.merge(page: page)) do |request|
76
+ request.http_auth_types = :basic
77
+ request.username = config[:api_token]
78
+ request.password = 'api_token'
79
+ end
80
+ parsed = JSON.parse(response.body, symbolize_names: true)
81
+ fail "Request failed: #{parsed}" unless response.response_code == 200
82
+ parsed
83
+ end
84
+
85
+ # Push data to timesheet.
86
+ # @param parsed_response [Hash] resulf of fetch
87
+ #
88
+ def push(parsed_response)
89
+ TimeEntry.transaction do
90
+ parsed_response[:data].each { |x| push_record x }
91
+ end
92
+ end
93
+
94
+ # Push single record to database.
95
+ # Don't push time entry if no user set.
96
+ #
97
+ def push_record(record)
98
+ params = derive_params(record)
99
+ return unless params[:user_id]
100
+ te = TimeEntry.find_or_create_by(
101
+ external_id: record[:id], data_source_id: config[:source_id])
102
+ te.update params
103
+ end
104
+
105
+ def derive_params(record)
106
+ params = record.reduce({}) do |r, (k, v)|
107
+ next(r) unless params_map[k]
108
+ r.merge(params_map[k] => v)
109
+ end
110
+ params[:data_source_id] = config[:source_id]
111
+ params[:spent_on] = record[:start].to_date
112
+ params[:hours] /= 3_600_000.0 # turn milliseconds into hours
113
+ if config[:redmine_time_entry_class]
114
+ if issue_id = params[:comment].match(/\#(\d+)/)[1]
115
+ params[:client_id] = Kernel.const_get(config[:redmine_time_entry_class])
116
+ .client_id(issue_id)
117
+ end
118
+ end
119
+ unless params[:client_id]
120
+ if client = Client.find_by(name: record[:client])
121
+ params[:client_id] = client.id
122
+ else
123
+ Rails.logger.error "No client match to toggl client #{record[:client]}"
124
+ end
125
+ end
126
+ data_source_user = DataSourceUser.find_by(
127
+ data_source_id: config[:source_id], external_user_id: record[:uid])
128
+ if data_source_user
129
+ params[:user_id] = data_source_user.user_id
130
+ else
131
+ Rails.logger.error "No user match to toggl user
132
+ #{record[:user]} (id #{record[:uid]})"
133
+ end
134
+ params
135
+ end
136
+
137
+ def params_map
138
+ {
139
+ id: :external_id,
140
+ project: :project,
141
+ description: :comment,
142
+ dur: :hours,
143
+ start: :start_time,
144
+ end: :finish_time
145
+ }
146
+ end
147
+
148
+ extend self
149
+ end
150
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'timesheet/toggl/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "timesheet-toggl"
8
+ spec.version = Timesheet::Toggl::VERSION
9
+ spec.authors = ["Sergey Smagin"]
10
+ spec.email = ["smaginsergey1310@gmail.com"]
11
+ spec.summary = %q{Toggl integration for timesheet}
12
+ spec.homepage = "https://github.com/s-mage/timesheet-toggl"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+
23
+ spec.add_dependency 'curb'
24
+ spec.add_dependency 'activesupport'
25
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: timesheet-toggl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Smagin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-20 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.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
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.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: curb
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - smaginsergey1310@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - lib/timesheet/toggl.rb
82
+ - lib/timesheet/toggl/version.rb
83
+ - timesheet-toggl.gemspec
84
+ homepage: https://github.com/s-mage/timesheet-toggl
85
+ licenses:
86
+ - MIT
87
+ metadata: {}
88
+ post_install_message:
89
+ rdoc_options: []
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 2.2.2
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Toggl integration for timesheet
108
+ test_files: []
109
+ has_rdoc: