Class: Checkoff::Tasks
- Inherits:
-
Object
- Object
- Checkoff::Tasks
- Extended by:
- T::Sig
- Includes:
- Logging
- Defined in:
- lib/checkoff/tasks.rb
Overview
Pull tasks from Asana
Constant Summary collapse
- MINUTE =
60
- HOUR =
MINUTE * 60
- DAY =
24 * HOUR
- REALLY_LONG_CACHE_TIME =
MINUTE * 30
- LONG_CACHE_TIME =
MINUTE * 15
- SHORT_CACHE_TIME =
MINUTE * 5
Instance Method Summary collapse
-
#add_task(name, workspace_gid: @workspaces.default_workspace_gid, assignee_gid: default_assignee_gid) ⇒ Asana::Resources::Task
Add a task.
- #all_dependent_tasks(task, extra_task_fields: []) ⇒ Array<Asana::Resources::Task>
- #as_cache_key ⇒ Hash
- #date_or_time_field_by_name(task, field_name) ⇒ Date, ...
-
#gid_for_task(workspace_name, project_name, section_name, task_name) ⇒ String?
@sg-ignore.
- #h_to_task(task_data) ⇒ Asana::Resources::Task
- #in_period?(task, field_name, period) ⇒ Boolean
-
#in_portfolio_more_than_once?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) ⇒ Boolean
True if the task is in a project which is in the given portfolio.
-
#in_portfolio_named?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) ⇒ Boolean
True if the task is in a project which is in the given portfolio.
-
#incomplete_dependencies?(task) ⇒ Boolean
True if any of the task’s dependencies are marked incomplete.
-
#initialize(config: Checkoff::Internal::ConfigLoader.load(:asana), client: Checkoff::Clients.new(config:).client, workspaces: Checkoff::Workspaces.new(config:, client:), sections: Checkoff::Sections.new(config:, client:), portfolios: Checkoff::Portfolios.new(config:, client:), custom_fields: Checkoff::CustomFields.new(config:, client:), time_class: Time, date_class: Date, asana_task: Asana::Resources::Task) ⇒ Tasks
constructor
A new instance of Tasks.
-
#task(workspace_name, project_name, task_name, section_name: :unspecified, only_uncompleted: true, extra_fields: []) ⇒ Asana::Resources::Task?
Pull a specific task by name.
-
#task_by_gid(task_gid, extra_fields: [], only_uncompleted: true) ⇒ Asana::Resources::Task?
Pull a specific task by GID.
-
#task_ready?(task, period: :now_or_before, ignore_dependencies: false) ⇒ Boolean
Indicates a task is ready for a person to work on it.
-
#task_to_h(task) ⇒ Hash
Builds on the standard API representation of an Asana task with some convenience keys:.
-
#url_of_task(task) ⇒ String
Return user-accessible URL for a task.
Methods included from Logging
#debug, #error, #finer, #info, #logger, #warn
Constructor Details
#initialize(config: Checkoff::Internal::ConfigLoader.load(:asana), client: Checkoff::Clients.new(config:).client, workspaces: Checkoff::Workspaces.new(config:, client:), sections: Checkoff::Sections.new(config:, client:), portfolios: Checkoff::Portfolios.new(config:, client:), custom_fields: Checkoff::CustomFields.new(config:, client:), time_class: Time, date_class: Date, asana_task: Asana::Resources::Task) ⇒ Tasks
Returns a new instance of Tasks.
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/checkoff/tasks.rb', line 44 def initialize(config: Checkoff::Internal::ConfigLoader.load(:asana), client: Checkoff::Clients.new(config:).client, workspaces: Checkoff::Workspaces.new(config:, client:), sections: Checkoff::Sections.new(config:, client:), portfolios: Checkoff::Portfolios.new(config:, client:), custom_fields: Checkoff::CustomFields.new(config:, client:), time_class: Time, date_class: Date, asana_task: Asana::Resources::Task) @config = config @sections = sections @time_class = time_class @date_class = date_class @asana_task = asana_task @client = client @portfolios = portfolios @custom_fields = custom_fields @workspaces = workspaces @timing = Checkoff::Timing.new(today_getter: date_class, now_getter: time_class) end |
Instance Method Details
#add_task(name, workspace_gid: @workspaces.default_workspace_gid, assignee_gid: default_assignee_gid) ⇒ Asana::Resources::Task
Add a task
184 185 186 187 188 189 190 |
# File 'lib/checkoff/tasks.rb', line 184 def add_task(name, workspace_gid: @workspaces.default_workspace_gid, assignee_gid: default_assignee_gid) @asana_task.create(client, assignee: assignee_gid, workspace: workspace_gid, name:) end |
#all_dependent_tasks(task, extra_task_fields: []) ⇒ Array<Asana::Resources::Task>
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/checkoff/tasks.rb', line 234 def all_dependent_tasks(task, extra_task_fields: []) # @type [Array<Asana::Resources::Task>] dependent_tasks = T.let([], T::Array[Asana::Resources::Task]) # See note above - same applies as does in @dependencies # # @type [Array<Hash>] dependents = task.instance_variable_get(:@dependents) || [] dependents.each do |dependent_task_hash_or_obj| # seems like if we ever .inspect the task, it stashes the task # object instead of the hash. Try to avoid this - but maybe we # need to convert if it does happen. raise 'Found dependent task object!' if dependent_task_hash_or_obj.is_a?(Asana::Resources::Task) dependent_task_hash = dependent_task_hash_or_obj dependent_task = task_by_gid(dependent_task_hash.fetch('gid'), only_uncompleted: true, extra_fields: ['dependents'] + extra_task_fields) debug { "#{task.name} has dependent task #{T.must(dependent_task).name}" } next if dependent_task.nil? dependent_tasks << dependent_task dependent_tasks += all_dependent_tasks(dependent_task, extra_task_fields:) end dependent_tasks end |
#as_cache_key ⇒ Hash
326 327 328 |
# File 'lib/checkoff/tasks.rb', line 326 def as_cache_key {} end |
#date_or_time_field_by_name(task, field_name) ⇒ Date, ...
107 108 109 |
# File 'lib/checkoff/tasks.rb', line 107 def date_or_time_field_by_name(task, field_name) task_timing.date_or_time_field_by_name(task, field_name) end |
#gid_for_task(workspace_name, project_name, section_name, task_name) ⇒ String?
@sg-ignore
144 145 146 147 148 149 150 151 152 |
# File 'lib/checkoff/tasks.rb', line 144 def gid_for_task(workspace_name, project_name, section_name, task_name) # @sg-ignore t = tasks(workspace_name, project_name, section_name:, only_uncompleted: false) task_for_gid = t.find { |task| task.name == task_name } task_for_gid&.gid end |
#h_to_task(task_data) ⇒ Asana::Resources::Task
283 284 285 |
# File 'lib/checkoff/tasks.rb', line 283 def h_to_task(task_data) task_hashes.h_to_task(task_data, client:) end |
#in_period?(task, field_name, period) ⇒ Boolean
91 92 93 94 95 96 |
# File 'lib/checkoff/tasks.rb', line 91 def in_period?(task, field_name, period) # @type [Date,Time,nil] task_date_or_time = task_timing.date_or_time_field_by_name(task, field_name) timing.in_period?(task_date_or_time, period) end |
#in_portfolio_more_than_once?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) ⇒ Boolean
True if the task is in a project which is in the given portfolio
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/checkoff/tasks.rb', line 309 def in_portfolio_more_than_once?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name) portfolio_project_gids = portfolio_projects.map(&:gid) seen = T.let(false, T::Boolean) task.memberships.each do |membership| project_gid = membership.fetch('project').fetch('gid') next unless portfolio_project_gids.include?(project_gid) return true if seen seen = true end false end |
#in_portfolio_named?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) ⇒ Boolean
True if the task is in a project which is in the given portfolio
292 293 294 295 296 297 298 299 300 301 302 |
# File 'lib/checkoff/tasks.rb', line 292 def in_portfolio_named?(task, portfolio_name, workspace_name: @workspaces.default_workspace.name) portfolio_projects = @portfolios.projects_in_portfolio(workspace_name, portfolio_name) task.memberships.any? do |membership| project_gid = membership.fetch('project').fetch('gid') portfolio_projects.any? do |portfolio_project| portfolio_project.gid == project_gid end end end |
#incomplete_dependencies?(task) ⇒ Boolean
True if any of the task’s dependencies are marked incomplete
Include ‘dependencies.gid’ in extra_fields of task passed in.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/checkoff/tasks.rb', line 206 def incomplete_dependencies?(task) # Avoid a redundant fetch. Unfortunately, Ruby SDK allows # dependencies to be fetched along with other attributes--but # then doesn't use it and does another HTTP GET! At least this # way we can skip the extra HTTP GET in the common case when # there are no dependencies. # # https://github.com/Asana/ruby-asana/issues/125 # @sg-ignore # @type [Enumerable<Asana::Resources::Task>, nil] dependencies = task.instance_variable_get(:@dependencies) || [] dependencies.any? do |parent_task_info| # the real bummer though is that asana doesn't let you fetch # the completion status of dependencies, so we need to do this # regardless: parent_task_gid = parent_task_info.fetch('gid') parent_task = task_by_gid(parent_task_gid, only_uncompleted: false) T.must(parent_task).completed_at.nil? end end |
#task(workspace_name, project_name, task_name, section_name: :unspecified, only_uncompleted: true, extra_fields: []) ⇒ Asana::Resources::Task?
Pull a specific task by name
@sg-ignore
121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/checkoff/tasks.rb', line 121 def task(workspace_name, project_name, task_name, section_name: :unspecified, only_uncompleted: true, extra_fields: []) thread_local = Checkoff::Internal::ThreadLocal.new # @type [String] task_gid = thread_local.with_thread_local_variable(:suppress_asana_webhook_watch_creation, true) do gid_for_task(workspace_name, project_name, section_name, task_name) end return nil if task_gid.nil? task_by_gid(task_gid, only_uncompleted:, extra_fields:) end |
#task_by_gid(task_gid, extra_fields: [], only_uncompleted: true) ⇒ Asana::Resources::Task?
Pull a specific task by GID
162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/checkoff/tasks.rb', line 162 def task_by_gid(task_gid, extra_fields: [], only_uncompleted: true) = projects.(extra_fields:, only_uncompleted:) # @type [Hash] = .fetch(:options, {}) [:completed_since] = [:completed_since] unless [:completed_since].nil? client.tasks.find_by_id(task_gid, options:) rescue Asana::Errors::NotFound => e debug e nil end |
#task_ready?(task, period: :now_or_before, ignore_dependencies: false) ⇒ Boolean
Indicates a task is ready for a person to work on it. This is subtly different than what is used by Asana to mark a date as red/green! A task is ready if it is not dependent on an incomplete task and one of these is true:
-
start is null and due on is today
-
start is null and due at is before now
-
start on is today
-
start at is before now
82 83 84 85 86 |
# File 'lib/checkoff/tasks.rb', line 82 def task_ready?(task, period: :now_or_before, ignore_dependencies: false) return false if !ignore_dependencies && incomplete_dependencies?(task) in_period?(task, :ready, period) end |
#task_to_h(task) ⇒ Hash
Builds on the standard API representation of an Asana task with some convenience keys:
<regular keys from API response> + unwrapped:
membership_by_section_gid: Hash{String => Hash (membership)>
membership_by_project_gid: Hash{String => Hash (membership)>
membership_by_project_name: Hash{String => Hash (membership)>
task: String (name)
276 277 278 |
# File 'lib/checkoff/tasks.rb', line 276 def task_to_h(task) task_hashes.task_to_h(task) end |
#url_of_task(task) ⇒ String
Return user-accessible URL for a task
197 198 199 |
# File 'lib/checkoff/tasks.rb', line 197 def url_of_task(task) "https://app.asana.com/0/0/#{task.gid}/f" end |