timesheet-toggl 0.0.2 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7144dce52c6d9a109b3e9d095540359aeecd6cae
4
- data.tar.gz: 4e37f892a05b06dd46798c96eaf7cc22db88f6f8
3
+ metadata.gz: 5abbba2899768c7931df772a3807f6e2707861c6
4
+ data.tar.gz: 1bdf674dc5fa703fd0f0b4fdd441b828dc48ebd6
5
5
  SHA512:
6
- metadata.gz: c9ba21d9910947f6c2278e83a302b0f77f67fbf158d125fdf2ef4502a96e887ab6139fa0634d411efc3ee53d889b137f38d4cd6192e806b6e688875902434e72
7
- data.tar.gz: 133104f772bcf93c091ffa1da79cfc4ceecd8fbb99ad8f2dca488ae8abc74a63587d279b454660a9ad83de0ff7a37262a8befa980949f89ef480030113853002
6
+ metadata.gz: 0a870ed228f2c8dda4bc835063d7f4d0ae843f7c2732a213f833df4743da7901700bfd714e28213e345a35ee86fbfbd3fb9489855051bfb4810b30577d890682
7
+ data.tar.gz: c216d7566349691f264df6427299de3e1137766025552311dc0e2aeec6eec323d715d5bef657c1f6a4da5964163f40e8ee245d23b1c8fd507c522a90e6cdb930
@@ -0,0 +1,29 @@
1
+ module Timesheet
2
+ module Clients
3
+ CLIENTS_URI = 'https://www.toggl.com/api/v8/clients'
4
+
5
+ def create_client(name, workspace_id)
6
+ params = {
7
+ client: {
8
+ name: name,
9
+ wid: workspace_id,
10
+ }
11
+ }
12
+ headers = {}
13
+ headers['Content-Type']='application/json'
14
+ headers['X-Requested-With']='XMLHttpRequest'
15
+ headers['Accept']='application/json'
16
+ response = Curl::Easy.http_post(CLIENTS_URI, params.to_json) do |request|
17
+ request.http_auth_types = :basic
18
+ request.username = config[:api_token]
19
+ request.password = 'api_token'
20
+ request.headers = headers
21
+ end
22
+ if response.response_code == 200
23
+ JSON.parse(response.body, symbolize_names: true)[:data][:id]
24
+ else
25
+ Rails.logger.error "Client creation failed: #{response.body}"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,129 @@
1
+ module Timesheet
2
+ # get issue id
3
+ # issue-related params
4
+ # get project
5
+ # split by several issues
6
+ # if one issue, then all good
7
+ # if several:
8
+ # delete time entries from timesheet
9
+ # delete time entries from kibana
10
+ # add new time entries to timesheet
11
+ # @time_part
12
+ class TogglRecord
13
+ attr_accessor :record, :config
14
+ Time.zone = 'UTC' # not to corrupt start_time and end_time
15
+
16
+ def initialize(hash, config)
17
+ @descriptions = parse_description hash[:description]
18
+ @record = hash
19
+ @config = config
20
+ end
21
+
22
+ def parse_description(description)
23
+ result = description.scan(/(#\s?\d+[^#]+)/).flatten
24
+ result.size < 2 ? [description] : result
25
+ end
26
+
27
+ def push
28
+ return unless params = descriptions_params
29
+ if params.size > 1
30
+ TimeEntry
31
+ .where(external_id: record[:id], data_source_id: config[:source_id])
32
+ .each { |x| x.delete_from_kibana; x.delete }
33
+ params.each { |x| return unless x[:user_id]; TimeEntry.create x }
34
+ else
35
+ params = params.first
36
+ return unless params[:user_id]
37
+ te = TimeEntry.find_or_create_by(
38
+ external_id: record[:id], data_source_id: config[:source_id])
39
+ te.update params
40
+ end
41
+ end
42
+
43
+ def descriptions_params
44
+ return unless params = derive_params
45
+ time_proc = proc { |x| x.scan(/@\s?(\d+)/).flatten.first.to_i }
46
+ times = @descriptions.map(&time_proc).reject(&:zero?)
47
+ one_part = (times.size / @descriptions.size.to_f) * params[:hours] / times.sum
48
+ @descriptions.map do |x|
49
+ time = time_proc.call(x)
50
+ hours = time.zero? ?
51
+ (params[:hours] / @descriptions.size) :
52
+ (one_part * time)
53
+ iid = issue_id(comment: x)
54
+ params.merge(comment: x, hours: hours).merge(issue_related_params(iid))
55
+ end
56
+ end
57
+
58
+ def derive_params
59
+ params = record.reduce({}) do |r, (k, v)|
60
+ next(r) unless params_map[k]
61
+ r.merge(params_map[k] => v)
62
+ end
63
+ params[:data_source_id] = config[:source_id]
64
+ params[:spent_on] = record[:start]
65
+ params[:user_id] = user_id
66
+ return unless params[:user_id]
67
+ params[:hours] /= 3_600_000.0 # turn milliseconds into hours
68
+ if iid = issue_id(params)
69
+ params.merge!(issue_related_params(iid))
70
+ end
71
+ params[:client_id] ||= client_id
72
+ params
73
+ end
74
+
75
+ def params_map
76
+ {
77
+ id: :external_id,
78
+ project: :project,
79
+ description: :comment,
80
+ dur: :hours,
81
+ start: :start_time,
82
+ end: :finish_time
83
+ }
84
+ end
85
+
86
+ def issue_id(params)
87
+ return unless config[:redmine_time_entry_class]
88
+ params[:comment].match(/#\s?(\d+)/).try(:[], 1)
89
+ end
90
+
91
+ def issue_related_params(issue_id)
92
+ time_entry_class = Kernel.const_get(config[:redmine_time_entry_class])
93
+ begin
94
+ project_id = time_entry_class.issue_class.find(issue_id).project_id
95
+ rescue
96
+ return {}
97
+ end
98
+ project_company = time_entry_class.project_company(project_id)
99
+ issue_company = time_entry_class.issue_company(issue_id)
100
+ company = issue_company.empty? ? project_company : issue_company
101
+ alert = time_entry_class.alert? project_company, issue_company
102
+ {
103
+ project: time_entry_class.project(project_id),
104
+ task: time_entry_class.task(issue_id),
105
+ client_id: time_entry_class.client_id(company),
106
+ project_company: project_company,
107
+ issue_company: issue_company,
108
+ alert: alert
109
+ }
110
+ end
111
+
112
+ def client_id
113
+ if client = Client.find_by(name: record[:client])
114
+ client_id = client.id
115
+ else
116
+ Rails.logger.error "No client match to toggl client #{record[:client]}"
117
+ end
118
+ client_id
119
+ end
120
+
121
+ def user_id
122
+ data_source_user = DataSourceUser.find_by(
123
+ data_source_id: config[:source_id], external_user_id: record[:uid])
124
+ error = "No user match to toggl user #{record[:user]} (id #{record[:uid]})"
125
+ (Rails.logger.error(error); return) unless data_source_user
126
+ data_source_user.user_id
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,28 @@
1
+ module Timesheet
2
+ module Projects
3
+ PROJECTS_URI = 'https://www.toggl.com/api/v8/projects'
4
+
5
+ def create_project(name, workspace_id, client_id)
6
+ params = {
7
+ project: {
8
+ name: name,
9
+ wid: workspace_id,
10
+ cid: client_id
11
+ }
12
+ }
13
+ headers = {}
14
+ headers['Content-Type']='application/json'
15
+ headers['X-Requested-With']='XMLHttpRequest'
16
+ headers['Accept']='application/json'
17
+ response = Curl::Easy.http_post(PROJECTS_URI, params.to_json) do |request|
18
+ request.http_auth_types = :basic
19
+ request.username = config[:api_token]
20
+ request.password = 'api_token'
21
+ request.headers = headers
22
+ end
23
+ unless response.response_code == 200
24
+ Rails.logger.error "Project creation failed: #{response.body}"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,5 +1,5 @@
1
1
  module Timesheet
2
- module Toggl
3
- VERSION = "0.0.2"
2
+ class Toggl
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
@@ -1,4 +1,7 @@
1
1
  require_relative 'toggl/version'
2
+ require_relative 'toggl/clients'
3
+ require_relative 'toggl/projects'
4
+ require_relative 'toggl/parser'
2
5
  require 'curb'
3
6
  require 'active_support/core_ext/time'
4
7
  require 'active_support/core_ext/date'
@@ -13,6 +16,9 @@ module Timesheet
13
16
  # Timesheet::Toggl.new(config).sync(from: (Date.today - 1.month), to: Date.today)
14
17
  #
15
18
  class Toggl
19
+ include Clients
20
+ include Projects
21
+
16
22
  attr_accessor :config
17
23
 
18
24
  BASE_URI = 'https://toggl.com/reports/api/v2/details'
@@ -44,18 +50,20 @@ module Timesheet
44
50
  # until=2013-05-20&
45
51
  # user_agent=api_test"
46
52
  #
53
+ # @return array of time entries' ids
54
+ #
47
55
  def synchronize(from, to, user_ids = [])
48
56
  params = {
49
57
  workspace_id: config[:workspace_id],
50
58
  since: from.to_s,
51
59
  until: to.to_s,
52
- user_agent: 'export_to_timesheet',
53
- user_ids: user_ids.join(',')
60
+ user_agent: 'export_to_timesheet'
54
61
  }
62
+ params[:user_ids] = user_ids.join(',') unless user_ids.empty?
55
63
  first_page = fetch(params, 1)
56
- push(first_page)
64
+ te_ids = push(first_page)
57
65
  pages = first_page[:total_count] / first_page[:per_page]
58
- pages.times { |page| sync(params, page + 2) }
66
+ pages.times.map { |page| sync(params, page + 2) }.flatten + te_ids
59
67
  end
60
68
 
61
69
  # Since toggl has per-page api, we will follow them.
@@ -78,7 +86,7 @@ module Timesheet
78
86
  request.password = 'api_token'
79
87
  end
80
88
  parsed = JSON.parse(response.body, symbolize_names: true)
81
- fail "Request failed: #{parsed}" unless response.response_code == 200
89
+ fail "Request failed: #{parsed} with params #{params}" unless response.response_code == 200
82
90
  parsed
83
91
  end
84
92
 
@@ -87,7 +95,7 @@ module Timesheet
87
95
  #
88
96
  def push(parsed_response)
89
97
  TimeEntry.transaction do
90
- parsed_response[:data].each { |x| push_record x }
98
+ parsed_response[:data].map { |x| push_record x }
91
99
  end
92
100
  end
93
101
 
@@ -95,56 +103,8 @@ module Timesheet
95
103
  # Don't push time entry if no user set.
96
104
  #
97
105
  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
- }
106
+ params = TogglRecord.new(record, config).push
107
+ record[:id]
146
108
  end
147
-
148
- extend self
149
109
  end
150
110
  end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: timesheet-toggl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Smagin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-20 00:00:00.000000000 Z
11
+ date: 2015-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -79,7 +79,11 @@ files:
79
79
  - README.md
80
80
  - Rakefile
81
81
  - lib/timesheet/toggl.rb
82
+ - lib/timesheet/toggl/clients.rb
83
+ - lib/timesheet/toggl/parser.rb
84
+ - lib/timesheet/toggl/projects.rb
82
85
  - lib/timesheet/toggl/version.rb
86
+ - timesheet-toggl-0.0.2.gem
83
87
  - timesheet-toggl.gemspec
84
88
  homepage: https://github.com/s-mage/timesheet-toggl
85
89
  licenses:
@@ -101,9 +105,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
105
  version: '0'
102
106
  requirements: []
103
107
  rubyforge_project:
104
- rubygems_version: 2.2.2
108
+ rubygems_version: 2.4.5
105
109
  signing_key:
106
110
  specification_version: 4
107
111
  summary: Toggl integration for timesheet
108
112
  test_files: []
109
- has_rdoc: