Created
August 28, 2019 23:36
-
-
Save bsgreenb/e01bd470a7680b122fef85b40322f629 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# For the actual business logic around syncing new events to asana | |
# TODO: optimize asana api calls | |
# TODO: replace logs with logger. per https://guides.rubyonrails.org/debugging_rails_applications.html | |
class Syncer | |
def initialize | |
@asana = AsanaAPI.new | |
@tag_parser = TagParser.new | |
end | |
def sync_projects | |
Rails.logger.info 'Syncing projects' | |
personal_projects = @asana.personal_projects | |
personal_projects.each do |personal_project| | |
sync_project(personal_project) | |
end | |
end | |
def sync_project(project) | |
project_name = project.name | |
Rails.logger.info "Syncing project #{project_name}" | |
project.tasks.each do |task| | |
sync_task(task.gid) | |
end | |
end | |
# Loop through the events, and sync the top level tasks | |
def sync_events(events) | |
Rails.logger.info 'Syncing events' | |
top_level_task_ids = [] | |
events.each do |event| | |
sync_event(event) | |
end | |
end | |
def sync_event(event) | |
event = AsanaEvent.new(@asana, event) # this is ugly wrapper | |
return unless event.top_level_task_change? | |
sync_task(event.task_id) | |
end | |
# "Note that events are "skinny" - we expect consumers who desire syncing data to make | |
# additional calls to the API to retrieve the latest state. Because the data may have | |
# already changed by the time we send the event, it would be misleading to send a snapshot | |
# of the data along with the event." -- https://asana.com/developers/api-reference/webhooks | |
def sync_task(task_id) | |
task = @asana.find_task(task_id) | |
return unless valid_task?(task) | |
Rails.logger.info "Processing task #{task_id} with name '#{task.name}''" | |
task_updates, project_ids = get_task_updates_and_project_ids(task) | |
# TODO: move these to helpers on @asana, rather than in this class. | |
@asana.update_task(task, task_updates) if task_updates.any? | |
@asana.add_project_ids(task, project_ids) if project_ids.any? | |
end | |
def valid_task?(task) | |
if task.nil? # Missing Task Race condition handlin | |
Rails.logger.info "Skipping deleted task" | |
return false | |
end | |
unless task.parent.nil? # Gotta skip here after load despite earlier check | |
Rails.logger.info "Skipping subtask" | |
return false | |
end | |
if task.name.strip.empty? | |
Rails.logger.info "Skipping empty task #{task.gid}" | |
return false | |
end | |
true | |
end | |
# returns [task_updates, project_adds] | |
def get_task_updates_and_project_ids(task) | |
task_updates = {} | |
# Default to assigning | |
task_updates[:assignee] = @asana.user_id unless task.assignee | |
new_task_updates, project_ids = get_task_updates_and_project_ids_from_tags(task) | |
task_updates.merge!(new_task_updates) | |
[task_updates.compact, project_ids] | |
end | |
def get_task_updates_and_project_ids_from_tags(task) | |
task_updates = {} | |
project_ids = Set.new | |
processed_tags = Set.new | |
tags = @tag_parser.extract_unique_tags(task) | |
tags.each do |tag| | |
new_task_updates, project_id = get_task_updates_and_project_id_from_tag(task, tag) | |
task_updates.merge!(new_task_updates) | |
project_ids << project_id if project_id.present? | |
if task_updates.any? || project_id | |
processed_tags << tag | |
else | |
Rails.logger.warn "Could not process tag #{tag}" | |
end | |
end | |
# Remove processed_tags from | |
if processed_tags.any? | |
task_updates[:name] = @tag_parser.remove_tags(task.name, processed_tags) | |
# TODO: if it has no existing assignee_status but inbox or whatever, Then | |
# default to Upcoming | |
# TODO: if it has Upcoming but no due on, default to 2wks from now. When | |
# it has due date greater than today, but no assignee_status, set upcoming | |
end | |
[task_updates, project_ids.to_a] | |
end | |
def get_task_updates_and_project_id_from_tag(task, tag) | |
# Handle all tag processing with downcased. | |
tag = tag.downcase | |
task_updates = get_task_time_updates_from_tag(tag) | |
project_id = nil | |
if task_updates.empty? | |
# Could only be a project tag if not a time tag | |
project_id = find_matching_project_id(tag) | |
end | |
[task_updates, project_id] | |
end | |
# TODO: may want to add stuff like #May18 here later. | |
def get_task_time_updates_from_tag(tag) | |
task_updates = {} | |
if %w[today now].include?(tag) | |
task_updates[:due_on] = DateTimeUtils.date_format(Date.today) | |
task_updates[:assignee_status] = 'today' | |
elsif %w[1wk wk week 1week].include?(tag) | |
task_updates[:due_on] = DateTimeUtils.date_format(1.weeks.from_now.to_date) | |
task_updates[:assignee_status] = 'upcoming' | |
elsif %w[2wks 2weeks].include?(tag) | |
task_updates[:due_on] = DateTimeUtils.date_format(2.weeks.from_now.to_date) | |
task_updates[:assignee_status] = 'upcoming' | |
elsif %w[1month month 1mo].include?(tag) | |
task_updates[:due_on] = DatetimeUtils.date_format(1.month.from_now.to_date) | |
task_updates[:assignee_status] = 'upcoming' | |
elsif %w[tmrw tomorrow tommorrow tommorow].include?(tag) | |
task_updates[:due_on] = DateTimeUtils.date_format(Date.tomorrow) | |
task_updates[:assignee_status] = 'upcoming' | |
elsif tag == 'upcoming' | |
task_updates[:assignee_status] = 'upcoming' | |
elsif tag == 'later' | |
task_updates[:assignee_status] = 'later' | |
elsif DateTimeUtils.day_of_week?(tag) | |
task_updates[:due_on] = DateTimeUtils.date_of_next(tag) | |
task_updates[:assignee_status] = 'upcoming' | |
end | |
task_updates | |
end | |
# Takes a tag and finds first project with matching name | |
def find_matching_project_id(tag) | |
# TODO: implement this to match with contains. | |
project = @asana.personal_projects.find do |personal_project| | |
# Downcase and remove spaces for comparison to hash. | |
project_name = personal_project.name.downcase.delete(' ') | |
# Then check if it starts with the tag. | |
project_name.starts_with?(tag) | |
end | |
project ? project.gid : nil | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment