bloomy 0.1.1 → 0.2.1

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
  SHA256:
3
- metadata.gz: b0d0f6f6172769244a6783a916bebd1543662f84f6e53af17437c2f09484f1cf
4
- data.tar.gz: 7ad965fcfbcac1501e16c2431a2e91c88f16f5854ea3b3be9eebdaf2ec222baf
3
+ metadata.gz: 492d10b82ab6034c0113b74c8d70352ef604039018f9fcfeca60924ded9414da
4
+ data.tar.gz: 2ff5fe5f2e515ee6949ed33caaa67112a24caf47a07cada9c2dfaf88edceaedd
5
5
  SHA512:
6
- metadata.gz: 37ef8b9fb6ee8bb2f13e8ba0481a0b4ea40d1c24fe944df8dae8dbbcf37c8734f7d05c2a3267699466e2a8955e4f0424fe73ba80e1e3e25ca3b9848e6aae045d
7
- data.tar.gz: bb9965e3a7701ba7d0fd3ec5566f1e516e172cde06d6d7fe65c294e4f6be5da88fde0cedba512f5ec93cc95d8cf9289fbc15cc94e004999ec1c9361d7a7e6de6
6
+ metadata.gz: 7414ba3b18b68878812929e5b82ce23253051d1f04a1ee2dbfcec418871db4255c44253d7497b6305cf86b7b60398affe0eade73bd16838fbb4a4d31bf2c1858
7
+ data.tar.gz: 067e2ab5d9b5f92a8da3c9489c8bb3318bd5818a9adeb21e867fd05e7357bf3e24ce986c8250c31128435e8e084ede3199aeee3cbab749fa1922cb4dbbd69ce7
data/.rubocop.yml CHANGED
@@ -25,3 +25,6 @@ Naming/AccessorMethodName:
25
25
 
26
26
  Gemspec/DevelopmentDependencies:
27
27
  Enabled: false
28
+
29
+ Style/StringLiterals:
30
+ EnforcedStyle: double_quotes
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Bloomy
2
- [![RSpec Tests](https://github.com/franccesco/bloomy/actions/workflows/main.yml/badge.svg)](https://github.com/franccesco/bloomy/actions/workflows/main.yml) [![Deploy Docs](https://github.com/franccesco/bloomy/actions/workflows/deploy_docs.yml/badge.svg)](https://github.com/franccesco/bloomy/actions/workflows/deploy_docs.yml)
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/bloomy.svg)](https://badge.fury.io/rb/bloomy)[![RSpec Tests](https://github.com/franccesco/bloomy/actions/workflows/main.yml/badge.svg)](https://github.com/franccesco/bloomy/actions/workflows/main.yml) [![Deploy Docs](https://github.com/franccesco/bloomy/actions/workflows/deploy_docs.yml/badge.svg)](https://github.com/franccesco/bloomy/actions/workflows/deploy_docs.yml)
3
4
 
4
5
  Bloomy is a Ruby library for interacting with the Bloom Growth API. It provides convenient methods for getting user details, todos, rocks, meetings, measurables, and issues.
5
6
 
@@ -8,22 +9,18 @@ Bloomy is a Ruby library for interacting with the Bloom Growth API. It provides
8
9
  Add this line to your application's Gemfile:
9
10
 
10
11
  ```ruby
11
- gem 'bloomy'
12
- ```
13
-
14
- And then execute:
15
-
16
- ```sh
17
- bundle install
12
+ bundle add bloomy
18
13
  ```
19
14
 
20
- Or install it yourself as:
15
+ Or install it to your system:
21
16
 
22
17
  ```sh
23
18
  gem install bloomy
24
19
  ```
25
20
 
26
- ## Configuration
21
+ ## Get Started
22
+
23
+ You can find the [full docs here](https://franccesco.github.io/bloomy/) but here's a quick overview to get you started.
27
24
 
28
25
  ### Initialize the Configuration
29
26
 
@@ -106,16 +103,16 @@ rocks = client.rock.list
106
103
  new_rock = client.rock.create(title: "New Rock", meeting_id: 1)
107
104
  ```
108
105
 
109
- ### Measurable Management
106
+ ### Scorecard Management
110
107
 
111
108
  To interact with measurable-related features:
112
109
 
113
110
  ```ruby
114
- # Get current week details
115
- current_week = client.measurable.current_week
111
+ # Get user scorecard
112
+ user_scorecard = client.scorecard.list(user_id: 1)
116
113
 
117
- # Get the scorecard for the user
118
- scorecard = client.measurable.scorecard
114
+ # Get a meeting scorecard
115
+ meeting_scorecard = client.scorecard.list(meeting_id: 1)
119
116
  ```
120
117
 
121
118
  ### Issue Management
data/lib/bloomy/client.rb CHANGED
@@ -5,14 +5,15 @@ require_relative "operations/users"
5
5
  require_relative "operations/todos"
6
6
  require_relative "operations/rocks"
7
7
  require_relative "operations/meetings"
8
- require_relative "operations/measurables"
8
+ require_relative "operations/scorecard"
9
9
  require_relative "operations/issues"
10
+ require_relative "operations/headlines"
10
11
 
11
12
  module Bloomy
12
13
  # The Client class is the main entry point for interacting with the Bloomy API.
13
- # It provides methods for managing users, todos, rocks, meetings, measurables, and issues.
14
+ # It provides methods for managing Bloom Growth features.
14
15
  class Client
15
- attr_reader :configuration, :user, :todo, :rock, :meeting, :measurable, :issue
16
+ attr_reader :configuration, :user, :todo, :rock, :meeting, :scorecard, :issue, :headline
16
17
 
17
18
  # Initializes a new Client instance
18
19
  #
@@ -37,8 +38,9 @@ module Bloomy
37
38
  @todo = Todo.new(@conn, @user_id)
38
39
  @rock = Rock.new(@conn, @user_id)
39
40
  @meeting = Meeting.new(@conn, @user_id)
40
- @measurable = Measurable.new(@conn, @user_id)
41
+ @scorecard = Scorecard.new(@conn, @user_id)
41
42
  @issue = Issue.new(@conn, @user_id)
43
+ @headline = Headline.new(@conn, @user_id)
42
44
  end
43
45
  end
44
46
  end
@@ -11,7 +11,7 @@ module Bloomy
11
11
 
12
12
  # Initializes a new Configuration instance
13
13
  #
14
- # @param [String, nil] optional_parameter Pass an optional API key
14
+ # @param [String, nil] api_key Pass an optional API key
15
15
  # @example
16
16
  # config = Bloomy::Configuration.new(api_key)
17
17
  def initialize(api_key = nil)
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Headline
4
+ # Initializes a new headline instance
5
+ #
6
+ # @param conn [Object] the connection object to interact with the API
7
+ # @param user_id [Integer] the ID of the user
8
+ def initialize(conn, user_id)
9
+ @conn = conn
10
+ @user_id = user_id
11
+ end
12
+
13
+ # Creates a new headline
14
+ #
15
+ # @param meeting_id [Integer] the ID of the meeting
16
+ # @param title [String] the title of the headline
17
+ # @param owner_id [Integer] the ID of the owner
18
+ # @param notes [String] additional notes for the headline
19
+ # @return [Hash] the created headline details
20
+ def create(meeting_id, title, owner_id: @user_id, notes: nil)
21
+ response = @conn.post("/api/v1/L10/#{meeting_id}/headlines",
22
+ {title: title, ownerId: owner_id, notes: notes}.to_json)
23
+ raise "Failed to create headline" unless response.status == 200
24
+
25
+ {
26
+ id: response.body["Id"],
27
+ title: response.body["Title"],
28
+ owner_id: response.body["OwnerId"],
29
+ notes: response.body["Notes"]
30
+ }
31
+ end
32
+
33
+ # Updates a headline
34
+ #
35
+ # @param headline_id [Integer] the ID of the headline to update
36
+ # @param title [String] the new title of the headline
37
+ # @return [Hash] the updated headline details
38
+ def update(headline_id, title)
39
+ response = @conn.put("/api/v1/headline/#{headline_id}", {title: title}.to_json)
40
+ raise "Failed to update headline" unless response.status == 200
41
+ true
42
+ end
43
+
44
+ # Get headline details
45
+ #
46
+ # @param headline_id [Integer] the ID of the headline
47
+ # @return [Hash] the details of the headline
48
+ def details(headline_id)
49
+ response = @conn.get("/api/v1/headline/#{headline_id}?Include_Origin=true")
50
+ raise "Failed to get headline details" unless response.status == 200
51
+
52
+ {
53
+ id: response.body["Id"],
54
+ title: response.body["Name"],
55
+ meeting_details: {
56
+ id: response.body["OriginId"],
57
+ name: response.body["Origin"]
58
+ },
59
+ owner_details: {
60
+ id: response.body["Owner"]["Id"],
61
+ name: response.body["Owner"]["Name"]
62
+ },
63
+ archived: response.body["Archived"],
64
+ created_at: response.body["CreateTime"],
65
+ closed_at: response.body["CloseTime"]
66
+ }
67
+ end
68
+
69
+ # Get user headlines
70
+ #
71
+ # @param user_id [Integer] the ID of the user
72
+ # @return [Array] the list of headlines for the user
73
+ def user_headlines(user_id: @user_id)
74
+ response = @conn.get("/api/v1/headline/users/#{user_id}")
75
+ raise "Failed to list headlines" unless response.status == 200
76
+ response.body.map do |headline|
77
+ {
78
+ id: headline["Id"],
79
+ title: headline["Name"],
80
+ meeting_details: {
81
+ id: headline["OriginId"],
82
+ name: headline["Origin"]
83
+ },
84
+ owner_details: {
85
+ id: headline["Owner"]["Id"],
86
+ name: headline["Owner"]["Name"]
87
+ },
88
+ archived: headline["Archived"],
89
+ created_at: headline["CreateTime"],
90
+ closed_at: headline["CloseTime"]
91
+ }
92
+ end
93
+ end
94
+
95
+ # Deletes a headline
96
+ #
97
+ # @param meeting_id [Integer] the ID of the meeting
98
+ # @param headline_id [Integer] the ID of the headline to delete
99
+ # @return [Boolean] true if the deletion was successful
100
+ def delete(meeting_id, headline_id)
101
+ response = @conn.delete("/api/v1/L10/#{meeting_id}/headlines/#{headline_id}")
102
+
103
+ # Raise an issue if response.status != 200
104
+ raise "Failed to delete headline" unless response.status == 200
105
+ true
106
+ end
107
+ end
@@ -74,17 +74,21 @@ class Issue
74
74
 
75
75
  # Creates a new issue
76
76
  #
77
- # @param issue_title [String] the title of the new issue
77
+ # @param title [String] the title of the new issue
78
78
  # @param meeting_id [Integer] the ID of the meeting associated with the issue
79
79
  # @return [Hash] a hash containing the new issue's ID and title
80
80
  # @example
81
81
  # issue.create("New Issue", 456)
82
82
  # #=> { id: 789, title: "New Issue" }
83
- def create(issue_title, meeting_id)
84
- response = @conn.post("issues/create", {title: issue_title, meetingid: meeting_id}.to_json)
83
+ def create(meeting_id:, title:, user_id: @user_id, notes: nil)
84
+ response = @conn.post("issues/create", {title: title, meetingid: meeting_id, ownerid: user_id, notes: notes}.to_json)
85
85
  {
86
86
  id: response.body["Id"],
87
- title: response.body["Name"]
87
+ meeting_id: response.body["OriginId"],
88
+ meeting_title: response.body["Origin"],
89
+ title: response.body["Name"],
90
+ user_id: response.body["Owner"]["Id"],
91
+ details_url: response.body["DetailsUrl"]
88
92
  }
89
93
  end
90
94
  end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ # Class to handle all the operations related to scorecards
6
+ # @note
7
+ # This class is already initialized via the client and usable as `client.scorecard.method`
8
+ class Scorecard
9
+ # Initializes a new Scorecard instance
10
+ #
11
+ # @param conn [Object] the connection object to interact with the API
12
+ # @param user_id [Integer] the ID of the user
13
+ def initialize(conn, user_id)
14
+ @conn = conn
15
+ @user_id = user_id
16
+ end
17
+
18
+ # Retrieves the current week details
19
+ #
20
+ # @return [Hash] a hash containing current week details
21
+ # @example
22
+ # client.scorecard.current_week
23
+ # #=> { id: 123, week_number: 24, week_start: "2024-06-10", week_end: "2024-06-16" }
24
+ def current_week
25
+ response = @conn.get("weeks/current").body
26
+ {
27
+ id: response["Id"],
28
+ week_number: response["ForWeekNumber"],
29
+ week_start: response["LocalDate"]["Date"],
30
+ week_end: response["ForWeek"]
31
+ }
32
+ end
33
+
34
+ # Retrieves the scorecards for a user or a meeting.
35
+ #
36
+ # @param user_id [Integer, nil] the ID of the user (defaults to initialized user_id)
37
+ # @param meeting_id [Integer, nil] the ID of the meeting
38
+ # @param show_empty [Boolean] whether to include scores with nil values (default: false)
39
+ # @param week_offset [Integer, nil] offset for the week number to filter scores
40
+ # @raise [ArgumentError] if both `user_id` and `meeting_id` are provided
41
+ # @return [Array<Hash>] an array of hashes containing scorecard details
42
+ # @example
43
+ # # Fetch scorecards for the current user
44
+ # client.scorecard.list
45
+ #
46
+ # # Fetch scorecards for a specific user
47
+ # client.scorecard.list(user_id: 42)
48
+ #
49
+ # # Fetch scorecards for a specific meeting
50
+ # client.scorecard.list(meeting_id: 99)
51
+ def list(user_id: nil, meeting_id: nil, show_empty: false, week_offset: nil)
52
+ if user_id && meeting_id
53
+ raise ArgumentError, "Please provide either `user_id` or `meeting_id`, not both."
54
+ end
55
+
56
+ if meeting_id
57
+ response = @conn.get("scorecard/meeting/#{meeting_id}").body
58
+ else
59
+ user_id ||= @user_id
60
+ response = @conn.get("scorecard/user/#{user_id}").body
61
+ end
62
+
63
+ scorecards = response["Scores"].map do |scorecard|
64
+ {
65
+ id: scorecard["Id"],
66
+ measurable_id: scorecard["MeasurableId"],
67
+ accountable_user_id: scorecard["AccountableUserId"],
68
+ title: scorecard["MeasurableName"],
69
+ target: scorecard["Target"],
70
+ value: scorecard["Measured"],
71
+ week: scorecard["Week"],
72
+ updated_at: scorecard["DateEntered"]
73
+ }
74
+ end
75
+
76
+ if week_offset
77
+ week_data = current_week
78
+ week_id = week_data[:week_number] - week_offset
79
+ scorecards.select! { |scorecard| scorecard[:week] == week_id }
80
+ end
81
+
82
+ scorecards.select! { |scorecard| scorecard[:value] || show_empty } unless show_empty
83
+ scorecards
84
+ end
85
+
86
+ # Updates the score for a measurable item for a specific week.
87
+ #
88
+ # @param measurable_id [Integer] the ID of the measurable item.
89
+ # @param score [Numeric] the score to be assigned to the measurable item.
90
+ # @param week_offset [Integer] the number of weeks to offset from the current week (default is 0).
91
+ # @return [Boolean] true if the score was successfully updated, false otherwise.
92
+ # @example
93
+ # client.scorecard.score(measurable_id: 123, score: 5)
94
+ # #=> true
95
+ # @note
96
+ # The `week_offset` parameter is useful when updating scores for previous weeks.
97
+ # For example, to update the score for the previous week, you can set `week_offset` to 1.
98
+ # To update a future week's score, you can set `week_offset` to a negative value.
99
+ def score(measurable_id:, score:, week_offset: 0)
100
+ week_data = current_week
101
+ week_id = week_data[:week_number] - week_offset
102
+
103
+ response = @conn.put("measurables/#{measurable_id}/week/#{week_id}", {value: score}.to_json)
104
+ response.success?
105
+ end
106
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "date"
4
+
3
5
  # Class to handle all the operations related to todos
4
6
  class Todo
5
7
  # Initializes a new Todo instance
@@ -67,4 +69,31 @@ class Todo
67
69
  response = @conn.post("/api/v1/todo/#{todo_id}/complete?status=true")
68
70
  {status: response.status}
69
71
  end
72
+
73
+ # Updates an existing todo
74
+ #
75
+ # @param todo_id [Integer] the ID of the todo to update
76
+ # @param title [String, nil] the new title of the todo (optional)
77
+ # @param due_date [String, nil] the new due date of the todo (optional)
78
+ # @return [Hash] a hash containing the updated todo's details
79
+ # @example
80
+ # todo.update(1, title: "Updated Todo", due_date: "2024-11-01T01:41:41.528Z")
81
+ # #=> { id: 1, title: "Updated Todo", due_date: "2024-11-01T01:41:41.528Z", ... }
82
+ def update(todo_id:, title: nil, due_date: nil)
83
+ payload = {}
84
+ payload[:title] = title if title
85
+ payload[:dueDate] = due_date if due_date
86
+
87
+ raise ArgumentError, "At least one field must be provided" if payload.empty?
88
+
89
+ response = @conn.put("/api/v1/todo/#{todo_id}", payload.to_json)
90
+ raise "Failed to update todo. Status: #{response.status}" unless response.status == 200
91
+
92
+ {
93
+ id: todo_id,
94
+ title: title,
95
+ due_date: due_date,
96
+ updated_at: DateTime.now.to_s
97
+ }
98
+ end
70
99
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bloomy
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bloomy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Franccesco Orozco
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-19 00:00:00.000000000 Z
11
+ date: 2024-11-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -57,10 +57,11 @@ files:
57
57
  - lib/bloomy.rb
58
58
  - lib/bloomy/client.rb
59
59
  - lib/bloomy/configuration.rb
60
+ - lib/bloomy/operations/headlines.rb
60
61
  - lib/bloomy/operations/issues.rb
61
- - lib/bloomy/operations/measurables.rb
62
62
  - lib/bloomy/operations/meetings.rb
63
63
  - lib/bloomy/operations/rocks.rb
64
+ - lib/bloomy/operations/scorecard.rb
64
65
  - lib/bloomy/operations/todos.rb
65
66
  - lib/bloomy/operations/users.rb
66
67
  - lib/bloomy/version.rb
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  - !ruby/object:Gem::Version
87
88
  version: '0'
88
89
  requirements: []
89
- rubygems_version: 3.5.11
90
+ rubygems_version: 3.5.17
90
91
  signing_key:
91
92
  specification_version: 4
92
93
  summary: Manage your Bloom Growth account from the command line.
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "json"
4
-
5
- # Class to handle all the operations related to measurables
6
- # @note
7
- # This class is already initialized via the client and usable as `client.measurable.method`
8
- class Measurable
9
- # Initializes a new Measurable instance
10
- #
11
- # @param conn [Object] the connection object to interact with the API
12
- # @param user_id [Integer] the ID of the user
13
- def initialize(conn, user_id)
14
- @conn = conn
15
- @user_id = user_id
16
- end
17
-
18
- # Retrieves the current week details
19
- #
20
- # @return [Hash] a hash containing current week details
21
- # @example
22
- # client.measurable.current_week
23
- # #=> { id: 123, week_number: 24, week_start: "2024-06-10", week_end: "2024-06-16" }
24
- def current_week
25
- response = @conn.get("weeks/current").body
26
- {
27
- id: response["Id"],
28
- week_number: response["ForWeekNumber"],
29
- week_start: response["LocalDate"]["Date"],
30
- week_end: response["ForWeek"]
31
- }
32
- end
33
-
34
- # Retrieves the scorecard for the user
35
- #
36
- # @param current_week_only [Boolean] whether to include only the current week's scores (default: true)
37
- # @param show_empty [Boolean] whether to include scores with nil values (default: true)
38
- # @return [Array<Hash>] an array of hashes containing scorecard details
39
- # @example
40
- # client.measurable.scorecard
41
- # #=> [{ id: 123, title: "Sales", target: 100, value: 80, updated_at: "2024-06-12", week_number: 24 }, ...]
42
- def scorecard(current_week_only: true, show_empty: true)
43
- response = @conn.get("scorecard/user/mine").body
44
- scorecards = response["Scores"].map do |scorecard|
45
- {
46
- id: scorecard["Id"],
47
- title: scorecard["MeasurableName"],
48
- target: scorecard["Target"],
49
- value: scorecard["Measured"],
50
- updated_at: scorecard["DateEntered"],
51
- week_number: scorecard["ForWeek"]
52
- }
53
- end
54
-
55
- if current_week_only
56
- week_id = current_week[:week_number]
57
- scorecards.select do |scorecard|
58
- scorecard[:week_number] == week_id && (show_empty || scorecard[:value].nil?)
59
- end
60
- else
61
- scorecards.select { |scorecard| show_empty || scorecard[:value].nil? }
62
- end
63
- end
64
-
65
- # Updates a scorecard with a new measured value
66
- #
67
- # @param scorecard_id [Integer] the ID of the scorecard to update
68
- # @param measured [Numeric] the new measured value
69
- # @return [Boolean] true if the operation was successful, false otherwise
70
- # @example
71
- # client.measurable.update(1, 85)
72
- # #=> true
73
- def update(scorecard_id, measured)
74
- response = @conn.put("scores/#{scorecard_id}", {value: measured}.to_json).status
75
- response == 200
76
- end
77
- end