#!/usr/bin/env ruby require 'json' require 'time' DAYS = 60 * 60 * 24 WEEKS = DAYS * 7 TASKWARRIOR_DATE_FORMAT = "%Y%m%dT060000Z" # Find the -final- task state that's passed to us. json_task = $stdin.map{ |line| line }.last task = JSON.parse(json_task) last_task_form = json_task # Only modify tasks that are done, but have a recur. # Emit all other tasks' final states unchanged. unless task.has_key?('status') && task.has_key?('recur_after') && (task['status'] == 'completed') puts(json_task) exit 0 end # # We have an recur-after recurring task -- modify it! # unless task.has_key? 'due' puts "ERROR: task #{task['id']} has an 'recur_after' recurrance, but no due date!" exit -1 end completed_date = Time.parse(task['end']) previous_due_date = Time.parse(task['due']) # Figure our our next recurrance based on the taskwarrior period/duration syntax. recur_date = case task['recur_after'] when /P(\d+)D/ then completed_date + ($1.to_i * DAYS) when /P(\d+)W/ then completed_date + ($1.to_i * WEEKS) when /P(\d+)M/ then Time.new(completed_date.year, completed_date.month + $1.to_i, completed_date.day) when /P(\d+)H/ then Time.new(completed_date.year + $1.to_i, completed_date.month, completed_date.day) else puts "ERROR: task #{task['id']} has unsupported 'recur_after' duration #{task['recur_after']}" exit -1 end # Mutate the task to be pending, still, but with new dates. task['status'] = 'pending' task['due'] = recur_date.strftime(TASKWARRIOR_DATE_FORMAT) # If this task has ancillary dates, maintain the offset between them and the due date. ['wait', 'start'].each do |field| if task.has_key?(field) field_offset = previous_due_date - Time.parse(task[field]) new_field_date = Time.parse(task['due']) - field_offset task[field] = new_field_date.strftime(TASKWARRIOR_DATE_FORMAT) end end # Emit the task back to TaskWarrior. puts task.to_json