moco-ruby 0.1.1 → 1.0.0.alpha
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 +4 -4
- data/.rubocop.yml +10 -3
- data/CHANGELOG.md +79 -6
- data/Gemfile +2 -1
- data/Gemfile.lock +71 -23
- data/README.md +199 -55
- data/examples/v2_api_example.rb +73 -0
- data/lib/moco/client.rb +47 -0
- data/lib/moco/collection_proxy.rb +190 -0
- data/lib/moco/connection.rb +62 -0
- data/lib/moco/entities/activity.rb +96 -0
- data/lib/moco/entities/base_entity.rb +303 -0
- data/lib/moco/entities/company.rb +28 -0
- data/lib/moco/entities/deal.rb +24 -0
- data/lib/moco/entities/expense.rb +29 -0
- data/lib/moco/entities/holiday.rb +25 -0
- data/lib/moco/entities/invoice.rb +53 -0
- data/lib/moco/entities/planning_entry.rb +26 -0
- data/lib/moco/entities/presence.rb +30 -0
- data/lib/moco/entities/project.rb +39 -0
- data/lib/moco/entities/schedule.rb +26 -0
- data/lib/moco/entities/task.rb +20 -0
- data/lib/moco/entities/user.rb +33 -0
- data/lib/moco/entities/web_hook.rb +27 -0
- data/lib/moco/entities.rb +13 -4
- data/lib/moco/entity_collection.rb +59 -0
- data/lib/moco/helpers.rb +13 -0
- data/lib/moco/nested_collection_proxy.rb +40 -0
- data/lib/moco/sync.rb +74 -19
- data/lib/moco/version.rb +1 -1
- data/lib/moco.rb +26 -2
- data/mocurl.rb +51 -34
- data/sync_activity.rb +4 -4
- metadata +44 -10
- data/lib/moco/api.rb +0 -129
data/lib/moco/sync.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "fuzzy_match"
|
4
|
-
require_relative "
|
4
|
+
require_relative "client"
|
5
5
|
|
6
6
|
module MOCO
|
7
7
|
# Match and map projects and tasks between MOCO instances and sync activities
|
@@ -9,9 +9,9 @@ module MOCO
|
|
9
9
|
attr_reader :project_mapping, :task_mapping, :source_projects, :target_projects
|
10
10
|
attr_accessor :project_match_threshold, :task_match_threshold, :dry_run
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
@
|
12
|
+
def initialize(source_client, target_client, **args)
|
13
|
+
@source = source_client
|
14
|
+
@target = target_client
|
15
15
|
@project_match_threshold = args.fetch(:project_match_threshold, 0.8)
|
16
16
|
@task_match_threshold = args.fetch(:task_match_threshold, 0.45)
|
17
17
|
@filters = args.fetch(:filters, {})
|
@@ -24,12 +24,12 @@ module MOCO
|
|
24
24
|
build_initial_mappings
|
25
25
|
end
|
26
26
|
|
27
|
-
# rubocop:todo Metrics/
|
27
|
+
# rubocop:todo Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
28
28
|
def sync(&callbacks)
|
29
29
|
results = []
|
30
30
|
|
31
|
-
source_activities_r = @
|
32
|
-
target_activities_r = @
|
31
|
+
source_activities_r = @source.activities.all(@filters.fetch(:source, {}))
|
32
|
+
target_activities_r = @target.activities.all(@filters.fetch(:target, {}))
|
33
33
|
|
34
34
|
source_activities_grouped = source_activities_r.group_by(&:date).transform_values do |activities|
|
35
35
|
activities.group_by(&:project)
|
@@ -38,6 +38,9 @@ module MOCO
|
|
38
38
|
activities.group_by(&:project)
|
39
39
|
end
|
40
40
|
|
41
|
+
used_source_activities = []
|
42
|
+
used_target_activities = []
|
43
|
+
|
41
44
|
source_activities_grouped.each do |date, activities_by_project|
|
42
45
|
activities_by_project.each do |project, source_activities|
|
43
46
|
target_activities = target_activities_grouped.fetch(date, {}).fetch(@project_mapping[project.id], [])
|
@@ -46,9 +49,6 @@ module MOCO
|
|
46
49
|
matches = calculate_matches(source_activities, target_activities)
|
47
50
|
matches.sort_by! { |match| -match[:score] }
|
48
51
|
|
49
|
-
used_source_activities = []
|
50
|
-
used_target_activities = []
|
51
|
-
|
52
52
|
matches.each do |match|
|
53
53
|
source_activity, target_activity = match[:activity]
|
54
54
|
score = match[:score]
|
@@ -70,14 +70,14 @@ module MOCO
|
|
70
70
|
end
|
71
71
|
callbacks&.call(:update, source_activity, best_match)
|
72
72
|
unless @dry_run
|
73
|
-
results << @
|
73
|
+
results << @target.activities.update(best_match)
|
74
74
|
callbacks&.call(:updated, source_activity, best_match, results.last)
|
75
75
|
end
|
76
76
|
when 0...60
|
77
77
|
# <60 - no good match found, create new entry
|
78
78
|
callbacks&.call(:create, source_activity, expected_target_activity)
|
79
79
|
unless @dry_run
|
80
|
-
results << @
|
80
|
+
results << @target.activities.create(expected_target_activity)
|
81
81
|
callbacks&.call(:created, source_activity, best_match, results.last)
|
82
82
|
end
|
83
83
|
end
|
@@ -87,6 +87,19 @@ module MOCO
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
90
|
+
|
91
|
+
source_activities_r.each do |source_activity|
|
92
|
+
next if used_source_activities.include?(source_activity)
|
93
|
+
next unless @project_mapping[source_activity.project.id]
|
94
|
+
|
95
|
+
expected_target_activity = get_expected_target_activity(source_activity)
|
96
|
+
callbacks&.call(:create, source_activity, expected_target_activity)
|
97
|
+
unless @dry_run
|
98
|
+
results << @target.activities.create(expected_target_activity)
|
99
|
+
callbacks&.call(:created, source_activity, expected_target_activity, results.last)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
90
103
|
results
|
91
104
|
end
|
92
105
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
@@ -105,7 +118,7 @@ module MOCO
|
|
105
118
|
source_activities.each do |source_activity|
|
106
119
|
target_activities.each do |target_activity|
|
107
120
|
score = score_activity_match(get_expected_target_activity(source_activity), target_activity)
|
108
|
-
matches << { activity: [source_activity, target_activity], score:
|
121
|
+
matches << { activity: [source_activity, target_activity], score: }
|
109
122
|
end
|
110
123
|
end
|
111
124
|
matches
|
@@ -138,8 +151,26 @@ module MOCO
|
|
138
151
|
# rubocop:enable Metrics/AbcSize
|
139
152
|
|
140
153
|
def fetch_assigned_projects
|
141
|
-
@source_projects = @
|
142
|
-
@target_projects = @
|
154
|
+
@source_projects = @source.projects.all(**@filters.fetch(:source, {}), active: "true")
|
155
|
+
@target_projects = @target.projects.all(**@filters.fetch(:target, {}), active: "true")
|
156
|
+
|
157
|
+
# Ensure we have proper collections
|
158
|
+
@source_projects = if @source_projects.is_a?(MOCO::EntityCollection)
|
159
|
+
@source_projects
|
160
|
+
else
|
161
|
+
MOCO::EntityCollection.new(@source,
|
162
|
+
"projects", "Project").tap do |c|
|
163
|
+
c.instance_variable_set(:@items, [@source_projects])
|
164
|
+
end
|
165
|
+
end
|
166
|
+
@target_projects = if @target_projects.is_a?(MOCO::EntityCollection)
|
167
|
+
@target_projects
|
168
|
+
else
|
169
|
+
MOCO::EntityCollection.new(@target,
|
170
|
+
"projects", "Project").tap do |c|
|
171
|
+
c.instance_variable_set(:@items, [@target_projects])
|
172
|
+
end
|
173
|
+
end
|
143
174
|
end
|
144
175
|
|
145
176
|
def build_initial_mappings
|
@@ -156,13 +187,37 @@ module MOCO
|
|
156
187
|
end
|
157
188
|
|
158
189
|
def match_project(target_project)
|
159
|
-
|
160
|
-
|
190
|
+
# Create array of search objects manually since we can't call map on EntityCollection
|
191
|
+
searchable_projects = []
|
192
|
+
|
193
|
+
# Manually iterate since we can't rely on Enumerable methods
|
194
|
+
@source_projects.each do |project|
|
195
|
+
warn project.inspect
|
196
|
+
searchable_projects << { original: project, name: project.name }
|
197
|
+
end
|
198
|
+
|
199
|
+
matcher = FuzzyMatch.new(searchable_projects, read: :name)
|
200
|
+
match = matcher.find(target_project.name, threshold: @project_match_threshold)
|
201
|
+
match[:original] if match
|
161
202
|
end
|
162
203
|
|
163
204
|
def match_task(target_task, source_project)
|
164
|
-
|
165
|
-
|
205
|
+
# Get tasks from the source project
|
206
|
+
tasks = source_project.tasks
|
207
|
+
|
208
|
+
# Create array of search objects manually since we can't rely on Enumerable methods
|
209
|
+
|
210
|
+
# Manually iterate through tasks
|
211
|
+
searchable_tasks = tasks.map do |task|
|
212
|
+
{ original: task, name: task.name }
|
213
|
+
end
|
214
|
+
|
215
|
+
# Only proceed if we have tasks to match against
|
216
|
+
return nil if searchable_tasks.empty?
|
217
|
+
|
218
|
+
matcher = FuzzyMatch.new(searchable_tasks, read: :name)
|
219
|
+
match = matcher.find(target_task.name, threshold: @task_match_threshold)
|
220
|
+
match[:original] if match
|
166
221
|
end
|
167
222
|
end
|
168
223
|
end
|
data/lib/moco/version.rb
CHANGED
data/lib/moco.rb
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/core_ext"
|
5
|
+
require "active_support/inflector"
|
6
|
+
|
3
7
|
require_relative "moco/version"
|
4
|
-
|
5
|
-
|
8
|
+
|
9
|
+
# New API (v2)
|
10
|
+
require_relative "moco/entities/base_entity"
|
11
|
+
require_relative "moco/entities/project"
|
12
|
+
require_relative "moco/entities/activity"
|
13
|
+
require_relative "moco/entities/user"
|
14
|
+
require_relative "moco/entities/company"
|
15
|
+
require_relative "moco/entities/task"
|
16
|
+
require_relative "moco/entities/invoice"
|
17
|
+
require_relative "moco/entities/deal"
|
18
|
+
require_relative "moco/entities/expense"
|
19
|
+
require_relative "moco/entities/web_hook"
|
20
|
+
require_relative "moco/entities/schedule"
|
21
|
+
require_relative "moco/entities/presence"
|
22
|
+
require_relative "moco/entities/holiday"
|
23
|
+
require_relative "moco/entities/planning_entry"
|
24
|
+
require_relative "moco/client"
|
25
|
+
require_relative "moco/connection"
|
26
|
+
require_relative "moco/collection_proxy"
|
27
|
+
require_relative "moco/nested_collection_proxy"
|
28
|
+
require_relative "moco/entity_collection"
|
29
|
+
|
6
30
|
require_relative "moco/sync"
|
7
31
|
|
8
32
|
module MOCO
|
data/mocurl.rb
CHANGED
@@ -58,42 +58,59 @@ end
|
|
58
58
|
|
59
59
|
# Load default API key from config
|
60
60
|
config = YAML.load_file("config.yml")
|
61
|
-
options[:api_key] ||= config["instances"].
|
61
|
+
options[:api_key] ||= config["instances"].dig(subdomain, "api_key")
|
62
62
|
|
63
63
|
warn "Error: No API key found for `#{subdomain}' and none given, continuing without" if options[:api_key].nil?
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
65
|
+
client = MOCO::Client.new(subdomain: subdomain, api_key: options[:api_key])
|
66
|
+
|
67
|
+
# Extract path from URL
|
68
|
+
path = url.gsub(%r{https?://#{subdomain}\.mocoapp\.com/api/v1/}, "")
|
69
|
+
|
70
|
+
begin
|
71
|
+
# Make request using the client's connection directly
|
72
|
+
result = case options[:method]
|
73
|
+
when "GET"
|
74
|
+
client.connection.get(path)
|
75
|
+
when "DELETE"
|
76
|
+
client.connection.delete(path)
|
77
|
+
when "POST"
|
78
|
+
client.connection.post(path, options[:data])
|
79
|
+
when "PUT"
|
80
|
+
client.connection.put(path, options[:data])
|
81
|
+
when "PATCH"
|
82
|
+
client.connection.patch(path, options[:data])
|
83
|
+
else
|
84
|
+
puts "Error: Invalid HTTP Method: #{options[:method]}"
|
85
|
+
exit 1
|
86
|
+
end
|
87
|
+
|
88
|
+
if options[:verbose]
|
89
|
+
puts "> #{options[:method]} #{url}"
|
90
|
+
# Print request details if available
|
91
|
+
if result.env&.request_headers
|
92
|
+
puts(result.env.request_headers.map do |k, v|
|
93
|
+
"> #{k}: #{k == "Authorization" ? "#{v[0...16]}<REDACTED>#{v[-4..]}" : v}"
|
94
|
+
end)
|
95
|
+
puts ">"
|
96
|
+
puts result.env.request_body.split.map { |l| "> #{l}" }.join if result.env.request_body
|
97
|
+
puts "---"
|
98
|
+
puts "< #{result.status} #{result.reason_phrase}"
|
99
|
+
puts(result.headers.map { |k, v| "< #{k}: #{v}" })
|
100
|
+
else
|
101
|
+
puts "> Request details not available in this response format"
|
102
|
+
end
|
103
|
+
puts ""
|
104
|
+
end
|
82
105
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
puts "
|
92
|
-
|
93
|
-
puts ""
|
94
|
-
end
|
95
|
-
if options[:no_format]
|
96
|
-
puts result.body.to_json
|
97
|
-
else
|
98
|
-
puts JSON.pretty_generate(result.body)
|
106
|
+
# Format the response
|
107
|
+
response_data = result.body
|
108
|
+
if options[:no_format]
|
109
|
+
puts response_data.to_json
|
110
|
+
else
|
111
|
+
puts JSON.pretty_generate(response_data)
|
112
|
+
end
|
113
|
+
rescue StandardError => e
|
114
|
+
puts "Error: #{e.message}"
|
115
|
+
exit 1
|
99
116
|
end
|
data/sync_activity.rb
CHANGED
@@ -64,12 +64,12 @@ config = YAML.load_file("config.yml")
|
|
64
64
|
source_config = config["instances"].fetch(source_instance, nil)
|
65
65
|
target_config = config["instances"].fetch(target_instance, nil)
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
source_client = MOCO::Client.new(subdomain: source_instance, api_key: source_config["api_key"])
|
68
|
+
target_client = MOCO::Client.new(subdomain: target_instance, api_key: target_config["api_key"])
|
69
69
|
|
70
70
|
syncer = MOCO::Sync.new(
|
71
|
-
|
72
|
-
|
71
|
+
source_client,
|
72
|
+
target_client,
|
73
73
|
project_match_threshold: options[:match_project_threshold],
|
74
74
|
task_match_threshold: options[:match_task_threshold],
|
75
75
|
filters: {
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moco-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.alpha
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Teal Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '7.0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: faraday
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,20 +69,40 @@ files:
|
|
55
69
|
- README.md
|
56
70
|
- Rakefile
|
57
71
|
- config.yml.sample
|
72
|
+
- examples/v2_api_example.rb
|
58
73
|
- lib/moco.rb
|
59
|
-
- lib/moco/
|
74
|
+
- lib/moco/client.rb
|
75
|
+
- lib/moco/collection_proxy.rb
|
76
|
+
- lib/moco/connection.rb
|
60
77
|
- lib/moco/entities.rb
|
78
|
+
- lib/moco/entities/activity.rb
|
79
|
+
- lib/moco/entities/base_entity.rb
|
80
|
+
- lib/moco/entities/company.rb
|
81
|
+
- lib/moco/entities/deal.rb
|
82
|
+
- lib/moco/entities/expense.rb
|
83
|
+
- lib/moco/entities/holiday.rb
|
84
|
+
- lib/moco/entities/invoice.rb
|
85
|
+
- lib/moco/entities/planning_entry.rb
|
86
|
+
- lib/moco/entities/presence.rb
|
87
|
+
- lib/moco/entities/project.rb
|
88
|
+
- lib/moco/entities/schedule.rb
|
89
|
+
- lib/moco/entities/task.rb
|
90
|
+
- lib/moco/entities/user.rb
|
91
|
+
- lib/moco/entities/web_hook.rb
|
92
|
+
- lib/moco/entity_collection.rb
|
93
|
+
- lib/moco/helpers.rb
|
94
|
+
- lib/moco/nested_collection_proxy.rb
|
61
95
|
- lib/moco/sync.rb
|
62
96
|
- lib/moco/version.rb
|
63
97
|
- mocurl.rb
|
64
98
|
- sync_activity.rb
|
65
|
-
homepage: https://github.com/
|
99
|
+
homepage: https://github.com/starsong-consulting/moco-ruby
|
66
100
|
licenses:
|
67
101
|
- Apache-2.0
|
68
102
|
metadata:
|
69
|
-
homepage_uri: https://github.com/
|
70
|
-
source_code_uri: https://github.com/
|
71
|
-
changelog_uri: https://github.com/
|
103
|
+
homepage_uri: https://github.com/starsong-consulting/moco-ruby
|
104
|
+
source_code_uri: https://github.com/starsong-consulting/moco-ruby
|
105
|
+
changelog_uri: https://github.com/starsong-consulting/moco-ruby/blob/main/CHANGELOG.md
|
72
106
|
rubygems_mfa_required: 'true'
|
73
107
|
post_install_message:
|
74
108
|
rdoc_options: []
|
@@ -78,12 +112,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
112
|
requirements:
|
79
113
|
- - ">="
|
80
114
|
- !ruby/object:Gem::Version
|
81
|
-
version: 2.
|
115
|
+
version: 3.2.0
|
82
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
117
|
requirements:
|
84
|
-
- - "
|
118
|
+
- - ">"
|
85
119
|
- !ruby/object:Gem::Version
|
86
|
-
version:
|
120
|
+
version: 1.3.1
|
87
121
|
requirements: []
|
88
122
|
rubygems_version: 3.4.1
|
89
123
|
signing_key:
|
data/lib/moco/api.rb
DELETED
@@ -1,129 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "faraday"
|
4
|
-
require_relative "entities"
|
5
|
-
|
6
|
-
module MOCO
|
7
|
-
# MOCO::API abstracts access to the MOCO API and its entities
|
8
|
-
class API
|
9
|
-
def initialize(subdomain, api_key)
|
10
|
-
@subdomain = subdomain
|
11
|
-
@api_key = api_key
|
12
|
-
@conn = Faraday.new do |f|
|
13
|
-
f.request :json
|
14
|
-
f.response :json
|
15
|
-
f.request :authorization, "Token", "token=#{@api_key}" if @api_key
|
16
|
-
f.url_prefix = "https://#{@subdomain}.mocoapp.com/api/v1"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
%w[get post put patch delete].each do |method|
|
21
|
-
define_method(method) do |path, *args|
|
22
|
-
@conn.send(method, path, *args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def get_projects(**args)
|
27
|
-
response = @conn.get("projects?#{Faraday::Utils.build_query(args)}")
|
28
|
-
parse_projects_response(response.body)
|
29
|
-
end
|
30
|
-
|
31
|
-
def get_assigned_projects(**args)
|
32
|
-
response = @conn.get("projects/assigned?#{Faraday::Utils.build_query(args)}")
|
33
|
-
parse_projects_response(response.body)
|
34
|
-
end
|
35
|
-
|
36
|
-
def get_activities(filters = {})
|
37
|
-
response = @conn.get("activities?#{Faraday::Utils.build_query(filters)}")
|
38
|
-
parse_activities_response(response.body)
|
39
|
-
end
|
40
|
-
|
41
|
-
def create_activity(activity)
|
42
|
-
api_entity = activity.to_h.except(:id, :project, :user, :customer).tap do |h|
|
43
|
-
h[:project_id] = activity.project.id
|
44
|
-
h[:task_id] = activity.task.id
|
45
|
-
end
|
46
|
-
@conn.post("activities", api_entity)
|
47
|
-
end
|
48
|
-
|
49
|
-
def update_activity(activity)
|
50
|
-
api_entity = activity.to_h.except(:project, :user, :customer).tap do |h|
|
51
|
-
h[:project_id] = activity.project.id
|
52
|
-
h[:task_id] = activity.task.id
|
53
|
-
end
|
54
|
-
@conn.put("activities/#{activity.id}", api_entity)
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
60
|
-
def parse_projects_response(data)
|
61
|
-
data.map do |project_data|
|
62
|
-
Project.new.tap do |project|
|
63
|
-
project.id = project_data["id"]
|
64
|
-
project.name = project_data["name"]
|
65
|
-
project.customer = parse_customer_reference(project_data["customer"])
|
66
|
-
project.tasks = project_data["tasks"].map do |task_data|
|
67
|
-
Task.new.tap do |task|
|
68
|
-
task.id = task_data["id"]
|
69
|
-
task.name = task_data["name"]
|
70
|
-
task.project_id = task_data["project_id"]
|
71
|
-
task.billable = task_data["billable"]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
78
|
-
|
79
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
80
|
-
def parse_activities_response(data)
|
81
|
-
data.map do |activity_data|
|
82
|
-
Activity.new.tap do |activity|
|
83
|
-
activity.id = activity_data["id"]
|
84
|
-
activity.date = activity_data["date"]
|
85
|
-
activity.description = activity_data["description"]
|
86
|
-
activity.user = parse_user_reference(activity_data["user"])
|
87
|
-
activity.customer = parse_customer_reference(activity_data["customer"])
|
88
|
-
activity.project = parse_project_reference(activity_data["project"])
|
89
|
-
activity.task = parse_task_reference(activity_data["task"])
|
90
|
-
activity.hours = activity_data["hours"]
|
91
|
-
activity.seconds = activity_data["seconds"]
|
92
|
-
activity.billable = activity_data["billable"]
|
93
|
-
activity.billed = activity_data["billed"]
|
94
|
-
activity.tag = activity_data["tag"]
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
99
|
-
|
100
|
-
def parse_project_reference(project_data)
|
101
|
-
Project.new.tap do |project|
|
102
|
-
project.id = project_data["id"]
|
103
|
-
project.name = project_data["name"]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def parse_task_reference(task_data)
|
108
|
-
Task.new.tap do |task|
|
109
|
-
task.id = task_data["id"]
|
110
|
-
task.name = task_data["name"]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def parse_user_reference(user_data)
|
115
|
-
User.new.tap do |user|
|
116
|
-
user.id = user_data["id"]
|
117
|
-
user.firstname = user_data["firstname"]
|
118
|
-
user.lastname = user_data["lastname"]
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
def parse_customer_reference(customer_data)
|
123
|
-
Customer.new.tap do |customer|
|
124
|
-
customer.id = customer_data["id"]
|
125
|
-
customer.name = customer_data["name"]
|
126
|
-
end
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|