Skip to content

Instantly share code, notes, and snippets.

@mvz mvz/gtg-export.rb
Created Jun 6, 2018

Embed
What would you like to do?
Export Getting Things Gnome tasks to Taskwarrior
#!/usr/bin/env ruby
require 'happymapper'
require 'json'
class Task
include HappyMapper
attribute :id, String
attribute :status, String
attribute :tags, String
attribute :uuid, String
element :title, String
element :startdate, String
element :duedate, String
element :modified, DateTime
element :donedate, String
has_many :subtasks, String, tag: 'subtask'
element :content, String
end
class TaskList
def initialize(tasks)
@tasks = tasks
@tasks_hash = {}
@tasks.each do |task|
@tasks_hash[task.id] = task
end
end
def each_task(&block)
@tasks.each &block
end
def find(task_id)
@tasks_hash[task_id]
end
def root_task(task)
parent = @tasks.find { |it| it.subtasks.include? task.id }
parent && root_task(parent) || task
end
end
class TaskProcessor
def initialize(task_list, handler)
@task_list = task_list
@handler = handler
@processed = {}
end
def process
@processed.clear
@task_list.each_task do |task|
next if @processed[task.id]
root = @task_list.root_task(task)
process_task root
end
@task_list.each_task do |task|
raise "Task #{task.id} not processed" unless @processed[task.id]
end
end
def self.process(tasks, handler)
new(tasks, handler).process
end
private
def process_task(task, level = 0)
@handler.handle(task, level)
@processed[task.id] = true
process_subtasks task.subtasks, level + 1
end
def process_subtasks(subtask_ids, level)
subtask_ids.each do |task_id|
raise "Task #{task_id} already processed" if @processed[task_id]
task = @task_list.find(task_id)
process_task task, level
end
end
end
class TaskWarriorExporter
def initialize(task_list)
@task_list = task_list
end
def handle(task, level)
status = case task.status
when 'Dismiss'
'deleted'
when 'Done'
'completed'
when 'Active'
'pending'
else
raise "Unknown: #{task.status}"
end
data = {
description: task.title,
status: status,
uuid: task.uuid,
}
if task.duedate
if task.duedate == 'soon'
data[:priority] = 'H'
else
data[:due] = task.duedate
end
end
data[:end] = task.donedate if task.donedate
data[:scheduled] = task.startdate if task.startdate
entry = guess_entry(task)
data[:entry] = entry
subtask_uuids = task.subtasks.map do |subtask_id|
@task_list.find(subtask_id).uuid
end
if subtask_uuids.any?
data[:depends] = subtask_uuids.join(',')
end
data[:tags] = task.tags unless task.tags.empty?
if task.content
data[:annotations] = [ { entry: entry, description: task.content } ]
end
puts data.to_json
end
private
def guess_entry(task)
dates = [task.duedate, task.donedate, task.startdate].compact.
reject { |it| %w(someday soon).include? it }.
sort
dates.first || task.modified.to_s
end
end
projects_file = File.expand_path '~/.local/share/gtg/projects.xml'
projects = HappyMapper.parse File.read projects_file
tasks_file = projects.backend.path
tasks = Task.parse File.read tasks_file
task_list = TaskList.new tasks
TaskProcessor.process(task_list, TaskWarriorExporter.new(task_list))
@mvz

This comment has been minimized.

Copy link
Owner Author

commented Jun 6, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.