mirror of
https://github.com/TracksApp/tracks.git
synced 2025-09-21 21:40:48 +02:00
refactor the cli examples in /doc and fix a small bug in todos_controller
This commit is contained in:
parent
bbf6cdf702
commit
871e18922b
4 changed files with 384 additions and 385 deletions
|
@ -85,7 +85,8 @@ class TodosController < ApplicationController
|
||||||
p.parse_dates() unless mobile?
|
p.parse_dates() unless mobile?
|
||||||
tag_list = p.tag_list
|
tag_list = p.tag_list
|
||||||
|
|
||||||
@todo = current_user.todos.build(p.attributes)
|
@todo = current_user.todos.build
|
||||||
|
@todo.assign_attributes(p.attributes)
|
||||||
p.add_errors(@todo)
|
p.add_errors(@todo)
|
||||||
|
|
||||||
if @todo.errors.empty?
|
if @todo.errors.empty?
|
||||||
|
|
94
doc/tracks_cli/tracks_api.rb
Normal file
94
doc/tracks_cli/tracks_api.rb
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
require 'time'
|
||||||
|
require 'net/https'
|
||||||
|
|
||||||
|
module TracksCli
|
||||||
|
|
||||||
|
class TracksAPI
|
||||||
|
def initialize(options)
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_http(uri)
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
|
||||||
|
if uri.scheme == "https" # enable SSL/TLS
|
||||||
|
http.use_ssl = true
|
||||||
|
http.ca_path = "/etc/ssl/certs/" # Debian based path
|
||||||
|
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
||||||
|
http.verify_depth = 5
|
||||||
|
end
|
||||||
|
|
||||||
|
http
|
||||||
|
end
|
||||||
|
|
||||||
|
def context_uri_for(context_id)
|
||||||
|
URI.parse(@options[:context_prefix] + context_id.to_s + ".xml")
|
||||||
|
end
|
||||||
|
|
||||||
|
def todo_uri
|
||||||
|
URI.parse(@options[:uri])
|
||||||
|
end
|
||||||
|
|
||||||
|
def project_uri
|
||||||
|
URI.parse(@options[:projects_uri])
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_todo(todo)
|
||||||
|
req = Net::HTTP::Post.new(todo_uri.path, "Content-Type" => "text/xml")
|
||||||
|
req.basic_auth @options[:login], @options[:password]
|
||||||
|
req.body = build_todo_body(todo)
|
||||||
|
get_http(todo_uri).request(req)
|
||||||
|
end
|
||||||
|
|
||||||
|
def post_project(project)
|
||||||
|
req = Net::HTTP::Post.new(project_uri.path, "Content-Type" => "text/xml")
|
||||||
|
req.basic_auth @options[:login], @options[:password]
|
||||||
|
req.body = build_project_body(project)
|
||||||
|
get_http(project_uri).request(req)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_context(context_id)
|
||||||
|
req = Net::HTTP::Get.new(context_uri_for(context_id).path)
|
||||||
|
req.basic_auth @options[:login], @options[:password]
|
||||||
|
get_http(context_uri_for(context_id)).request(req)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_todo_body(todo)
|
||||||
|
props = "<description>#{todo[:description]}</description><project_id>#{todo[:project_id]}</project_id>"
|
||||||
|
|
||||||
|
unless todo[:show_from].nil?
|
||||||
|
props << "<show-from type=\"datetime\">#{Time.at(todo[:show_from]).xmlschema}</show-from>"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless todo[:notes].nil?
|
||||||
|
props << "<notes>#{todo[:notes]}</notes>"
|
||||||
|
end
|
||||||
|
|
||||||
|
unless todo[:taglist].nil?
|
||||||
|
tags = todo[:taglist].split(",")
|
||||||
|
if tags.length() > 0
|
||||||
|
tags = tags.collect { |tag| "<tag><name>#{tag.strip}</name></tag>" unless tag.strip.empty?}.join('')
|
||||||
|
props << "<tags>#{tags}</tags>"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if todo[:context_name] && !todo[:context_name].empty?
|
||||||
|
props << "<context><name>#{todo[:context_name]}</name></context>"
|
||||||
|
else
|
||||||
|
props << "<context_id>#{todo[:context_id]}</context_id>"
|
||||||
|
end
|
||||||
|
|
||||||
|
if todo[:is_dependend]
|
||||||
|
props << "<predecessor_dependencies><predecessor>#{todo[:predecessor]}</predecessor></predecessor_dependencies>"
|
||||||
|
end
|
||||||
|
|
||||||
|
"<todo>#{props}</todo>"
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_project_body(project)
|
||||||
|
"<project><name>#{project[:description]}</name><default-context-id>#{project[:default_context_id]}</default-context-id></project>"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,138 +1,109 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
#
|
# CLI ruby client for Tracks: rails application for GTD methodology (First author: Vitalie Lazu <vitalie.lazu@gmail.com>)
|
||||||
# Author: Vitalie Lazu <vitalie.lazu@gmail.com>
|
|
||||||
# Date: Sat, 10 Jan 2009 19:12:43 +0200
|
|
||||||
#
|
|
||||||
|
|
||||||
# CLI ruby client for Tracks: rails application for GTD methodology
|
|
||||||
# http://www.getontracks.org/development/
|
# http://www.getontracks.org/development/
|
||||||
# You need to set ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
# You need to set ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
||||||
# and set GTD_TODOS_URL to your tracks install. It defaults to 'http://localhost:3000/todos.xml'
|
# and set GTD_TODOS_URL to your tracks install. It defaults to 'http://localhost:3000/todos.xml'
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# $ echo "todo 1\ntodo2" | GTD_LOGIN=username GTD_PASSWORD=secret ruby tracks_cli_client.rb -c 123 -p 456
|
||||||
|
# This will post todo 1 and todo 2 to localhost:3000 using username and secret als credentials
|
||||||
|
|
||||||
require 'net/https'
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require 'cgi'
|
require 'cgi'
|
||||||
require 'time'
|
require File.expand_path(File.dirname(__FILE__) + '/tracks_cli/tracks_api')
|
||||||
|
|
||||||
class Hash
|
class PostLineAsTodo
|
||||||
def to_query_string
|
|
||||||
map { |k, v|
|
def initialize(options)
|
||||||
if v.instance_of?(Hash)
|
@options = options
|
||||||
v.map { |sk, sv|
|
@tracks = TracksCli::TracksAPI.new(
|
||||||
"#{k}[#{sk}]=#{sv}"
|
uri: ENV['GTD_TODOS_URL'] || 'http://localhost:3000/todos.xml',
|
||||||
}.join('&')
|
login: ENV['GTD_LOGIN'],
|
||||||
else
|
password: ENV['GTD_PASSWORD'])
|
||||||
"#{k}=#{v}"
|
@context_id = options[:context_id] ? options[:context_id].to_i : 1
|
||||||
end
|
@project_id = options[:project_id] ? options[:project_id].to_i : 1
|
||||||
}.join('&')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def post(lines)
|
||||||
|
lines.each_line do |l|
|
||||||
|
l.chomp!
|
||||||
|
next if l.strip.empty?
|
||||||
|
|
||||||
|
resp = @tracks.post_todo(
|
||||||
|
description: CGI.escapeHTML(l),
|
||||||
|
context_id: @context_id,
|
||||||
|
project_id: @project_id,
|
||||||
|
show_from: @options[:show_from])
|
||||||
|
|
||||||
|
if resp.code == '302' || resp.code == '201'
|
||||||
|
puts resp['location']
|
||||||
|
else
|
||||||
|
p resp.body
|
||||||
|
raise Error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module Gtd
|
class Error < StandardError; end
|
||||||
class API
|
class InvalidParser < StandardError; end
|
||||||
GTD_URI = ENV['GTD_TODOS_URL'] || 'http://localhost:3000/todos.xml'
|
|
||||||
|
|
||||||
def post(lines, options = {})
|
class ConsoleOptions
|
||||||
uri = URI.parse(GTD_URI)
|
attr_reader :parser, :options
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
|
|
||||||
if uri.scheme == "https" # enable SSL/TLS
|
def initialize
|
||||||
http.use_ssl = true
|
@options = {}
|
||||||
http.ca_path = "/etc/ssl/certs/" # Debian based path
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
@parser = OptionParser.new do |cmd|
|
||||||
http.verify_depth = 5
|
cmd.banner = "Ruby Gtd CLI - takes todos input from STDIN"
|
||||||
|
|
||||||
|
cmd.separator ''
|
||||||
|
|
||||||
|
cmd.on('-h', '--help', 'Displays this help message') do
|
||||||
|
puts @parser
|
||||||
|
exit
|
||||||
end
|
end
|
||||||
|
|
||||||
lines.each_line do |l|
|
cmd.on('-p [N]', Integer, "project id to set for new todo") do |v|
|
||||||
l.chomp!
|
@options[:project_id] = v
|
||||||
next if l.strip.empty?
|
end
|
||||||
|
|
||||||
description = CGI.escapeHTML(l)
|
cmd.on('-c [N]', Integer, 'context id to set') do |v|
|
||||||
context_id = options[:context_id] ? options[:context_id].to_i : 1
|
@options[:context_id] = v
|
||||||
project_id = options[:project_id] ? options[:project_id].to_i : 1
|
end
|
||||||
props = "<description>#{description}</description><project_id>#{project_id}</project_id><context_id>#{context_id}</context_id>"
|
|
||||||
|
|
||||||
if options[:show_from]
|
cmd.on('-w [N]', Integer, 'Postpone task for N weeks') do |v|
|
||||||
props << "<show-from type=\"datetime\">#{Time.at(options[:show_from]).xmlschema}</show-from>"
|
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * (v || 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
req = Net::HTTP::Post.new(uri.path, "Content-Type" => "text/xml")
|
cmd.on('-m [N]', Integer, 'Postpone task for N months') do |v|
|
||||||
req.basic_auth ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * 4 * (v || 1)
|
||||||
req.body = "<todo>#{props}</todo>"
|
|
||||||
|
|
||||||
resp = http.request(req)
|
|
||||||
|
|
||||||
if resp.code == '302' || resp.code == '201'
|
|
||||||
puts resp['location']
|
|
||||||
else
|
|
||||||
p resp.body
|
|
||||||
raise Gtd::Error
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Error < StandardError; end
|
def run(args)
|
||||||
class InvalidParser < StandardError; end
|
@parser.parse!(args)
|
||||||
|
lines = STDIN.read
|
||||||
|
|
||||||
class ConsoleOptions
|
if lines.strip.empty?
|
||||||
attr_reader :parser, :options
|
puts "Please pipe in some content to tracks on STDIN."
|
||||||
|
|
||||||
def initialize
|
|
||||||
@options = {}
|
|
||||||
|
|
||||||
@parser = OptionParser.new do |cmd|
|
|
||||||
cmd.banner = "Ruby Gtd CLI - takes todos input from STDIN"
|
|
||||||
|
|
||||||
cmd.separator ''
|
|
||||||
|
|
||||||
cmd.on('-h', '--help', 'Displays this help message') do
|
|
||||||
puts @parser
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-p [N]', Integer, "project id to set for new todo") do |v|
|
|
||||||
@options[:project_id] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-c [N]', Integer, 'context id to set') do |v|
|
|
||||||
@options[:context_id] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-w [N]', Integer, 'Postpone task for N weeks') do |v|
|
|
||||||
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * (v || 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-m [N]', Integer, 'Postpone task for N months') do |v|
|
|
||||||
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * 4 * (v || 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def run(args)
|
|
||||||
@parser.parse!(args)
|
|
||||||
lines = STDIN.read
|
|
||||||
|
|
||||||
if lines.strip.empty?
|
|
||||||
puts "Please pipe in some content to tracks on STDIN."
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
gtd = API.new
|
|
||||||
gtd.post(lines, @options)
|
|
||||||
exit 0
|
|
||||||
rescue InvalidParser
|
|
||||||
puts "Please specify a valid format parser."
|
|
||||||
exit 1
|
|
||||||
rescue Error
|
|
||||||
puts "An unknown error occurred"
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
PostLineAsTodo.new(@options).post(lines)
|
||||||
|
exit 0
|
||||||
|
rescue InvalidParser
|
||||||
|
puts "Please specify a valid format parser."
|
||||||
|
exit 1
|
||||||
|
rescue Error
|
||||||
|
puts "An unknown error occurred"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if $0 == __FILE__
|
if $0 == __FILE__
|
||||||
app = Gtd::ConsoleOptions.new
|
ConsoleOptions.new.run(ARGV)
|
||||||
app.run(ARGV)
|
|
||||||
end
|
end
|
||||||
|
|
487
doc/tracks_template_cli.rb
Executable file → Normal file
487
doc/tracks_template_cli.rb
Executable file → Normal file
|
@ -1,7 +1,5 @@
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
# Version 0.4 (Dec 17, 2011)
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Based on the tracks_cli by Vitalie Lazu (https://gist.github.com/45537)
|
# Based on the tracks_cli by Vitalie Lazu (https://gist.github.com/45537)
|
||||||
#
|
#
|
||||||
|
@ -67,313 +65,248 @@
|
||||||
# .Set a reminder to check for reimbursement for [location]
|
# .Set a reminder to check for reimbursement for [location]
|
||||||
# .Mail folder to secretary
|
# .Mail folder to secretary
|
||||||
|
|
||||||
# Instantiate this template: ./tracks_template_cli -c 1 -f template_file.txt
|
# Instantiate this template: ruby tracks_template_cli -c 1 -f template_file.txt
|
||||||
|
|
||||||
require 'net/https'
|
|
||||||
require 'optparse'
|
require 'optparse'
|
||||||
require 'cgi'
|
require 'cgi'
|
||||||
require 'time'
|
|
||||||
require 'readline'
|
require 'readline'
|
||||||
|
require File.expand_path(File.dirname(__FILE__) + '/tracks_cli/tracks_api')
|
||||||
|
|
||||||
class Hash
|
class TemplateParser
|
||||||
def to_query_string
|
|
||||||
map { |k, v|
|
def initialize
|
||||||
if v.instance_of?(Hash)
|
@keywords = {}
|
||||||
v.map { |sk, sv|
|
|
||||||
"#{k}[#{sk}]=#{sv}"
|
|
||||||
}.join('&')
|
|
||||||
else
|
|
||||||
"#{k}=#{v}"
|
|
||||||
end
|
|
||||||
}.join('&')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_keyword(token)
|
||||||
|
print "Input required for "+token+": "
|
||||||
|
@keywords[token]=gets.chomp
|
||||||
|
end
|
||||||
|
|
||||||
|
def replace_tokens_in(line)
|
||||||
|
@keywords.each{ |key,val| line=line.sub(key,val) }
|
||||||
|
line
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_todo(line)
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
# first char is . or ^ the latter meaning this todo is dependent on the previous one
|
||||||
|
options[:depend]= line[0].chr == "^" ? true : false;
|
||||||
|
line = line[1..line.length] # remove first char
|
||||||
|
|
||||||
|
# find notes
|
||||||
|
tmp= line.split("|")
|
||||||
|
if tmp.length > 5
|
||||||
|
puts "Formatting error: found too many |"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
tmp[0].chomp!
|
||||||
|
options[:description]=tmp[0]
|
||||||
|
|
||||||
|
tmp.each_with_index do |t,idx|
|
||||||
|
t=t.strip.chomp
|
||||||
|
t=nil if t.empty?
|
||||||
|
tmp[idx]=t
|
||||||
|
end
|
||||||
|
|
||||||
|
options[:context]=tmp[1]
|
||||||
|
options[:taglist]=tmp[2]
|
||||||
|
options[:notes]=tmp[3]
|
||||||
|
options
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse(file, poster)
|
||||||
|
while line = file.gets
|
||||||
|
line = line.strip
|
||||||
|
|
||||||
|
# skip line if empty or comment
|
||||||
|
next if (line.empty? || line[0].chr == "#")
|
||||||
|
|
||||||
|
# check if line defines a token
|
||||||
|
if (line.split(' ')[0] == "token")
|
||||||
|
parse_keyword line.split(' ')[1]
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
# replace defined tokes in current line
|
||||||
|
line = replace_tokens_in line
|
||||||
|
|
||||||
|
# line is either todo/dependency or project
|
||||||
|
if (line[0].chr == "." ) || (line[0].chr == "^")
|
||||||
|
if @last_project_id.nil?
|
||||||
|
puts "Warning: no project specified for task \"#{line}\". Using default project."
|
||||||
|
end
|
||||||
|
poster.postTodo(parse_todo(line), @last_project_id)
|
||||||
|
else
|
||||||
|
@last_project_id = poster.postProject(line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module Gtd
|
class TemplatePoster
|
||||||
class API
|
|
||||||
GTD_URI_TODOS = ENV['GTD_TODOS_URL'] || 'http://localhost:3000/todos.xml'
|
|
||||||
GTD_URI_PROJECTS = ENV['GTD_PROJECTS_URL'] || 'http://localhost:3000/projects.xml'
|
|
||||||
GTD_URI_CONTEXTS_PREFIX = ENV['GTD_CONTEXT_URL_PREFIX'] || 'http://localhost:3000/contexts/'
|
|
||||||
GTD_URI_CONTEXTS = ENV['GTD_CONTEXT_URL'] || 'http://localhost:3000/contexts.xml'
|
|
||||||
|
|
||||||
def postTodo(l, options = {})
|
|
||||||
uri = URI.parse(GTD_URI_TODOS)
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
|
|
||||||
if uri.scheme == "https" # enable SSL/TLS
|
|
||||||
http.use_ssl = true
|
|
||||||
http.ca_path = "/etc/ssl/certs/" # Debian based path
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
||||||
http.verify_depth = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
l.chomp!
|
|
||||||
|
|
||||||
description = CGI.escapeHTML(l)
|
|
||||||
context_id = options[:context_id] ? options[:context_id].to_i : 1
|
|
||||||
project_id = options[:project_id] ? options[:project_id].to_i : 1
|
|
||||||
props = "<description>#{description}</description><project_id>#{project_id}</project_id>"
|
|
||||||
|
|
||||||
if options[:show_from]
|
|
||||||
props << "<show-from type=\"datetime\">#{Time.at(options[:show_from]).xmlschema}</show-from>"
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:note]
|
|
||||||
props << "<notes>#{options[:note]}</notes>"
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:taglist]
|
|
||||||
tags = options[:taglist].split(",")
|
|
||||||
if tags.length() > 0
|
|
||||||
tags = tags.collect { |tag| "<tag><name>#{tag.strip}</name></tag>" unless tag.strip.empty?}.join('')
|
|
||||||
props << "<tags>#{tags}</tags>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not (options[:context].nil? || options[:context].empty?)
|
|
||||||
props << "<context><name>#{options[:context]}</name></context>"
|
|
||||||
else
|
|
||||||
## use the default context
|
|
||||||
props << "<context_id>#{context_id}</context_id>"
|
|
||||||
end
|
|
||||||
|
|
||||||
if options[:depend]
|
|
||||||
props << "<predecessor_dependencies><predecessor>#{options[:last_todo_id]}</predecessor></predecessor_dependencies>"
|
|
||||||
end
|
|
||||||
|
|
||||||
req = Net::HTTP::Post.new(uri.path, "Content-Type" => "text/xml")
|
|
||||||
req.basic_auth ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
|
||||||
req.body = "<todo>#{props}</todo>"
|
|
||||||
|
|
||||||
puts req.body if options[:verbose]
|
|
||||||
|
|
||||||
resp = http.request(req)
|
|
||||||
|
|
||||||
if resp.code == '302' || resp.code == '201'
|
|
||||||
puts resp['location'] if options[:verbose]
|
|
||||||
|
|
||||||
# return the todo id
|
|
||||||
return resp['location'].split("/").last
|
|
||||||
else
|
|
||||||
p resp.body
|
|
||||||
raise Gtd::Error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def postProject(l, options = {})
|
|
||||||
uri = URI.parse(GTD_URI_PROJECTS)
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
|
|
||||||
if uri.scheme == "https" # enable SSL/TLS
|
|
||||||
http.use_ssl = true
|
|
||||||
http.ca_path = "/etc/ssl/certs/" # Debian based path
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
||||||
http.verify_depth = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
l.chomp!
|
|
||||||
|
|
||||||
description = CGI.escapeHTML(l)
|
|
||||||
props = "<name>#{l}</name><default-context-id>#{options[:context_id]}</default-context-id>"
|
|
||||||
|
|
||||||
req = Net::HTTP::Post.new(uri.path, "Content-Type" => "text/xml")
|
|
||||||
req.basic_auth ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
|
||||||
req.body = "<project>#{props}</project>"
|
|
||||||
|
|
||||||
resp = http.request(req)
|
|
||||||
|
|
||||||
if resp.code == '302' || resp.code == '201'
|
|
||||||
puts resp['location'] if options[:verbose]
|
|
||||||
|
|
||||||
# return the project id
|
|
||||||
return resp['location'].split("/").last
|
|
||||||
else
|
|
||||||
p resp.body
|
|
||||||
raise Gtd::Error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def queryContext(contextID)
|
|
||||||
return false unless contextID.is_a? Integer
|
|
||||||
|
|
||||||
uri = URI.parse(GTD_URI_CONTEXTS_PREFIX + contextID.to_s + ".xml")
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
|
|
||||||
if uri.scheme == "https" # enable SSL/TLS
|
|
||||||
http.use_ssl = true
|
|
||||||
http.ca_path = "/etc/ssl/certs/" # Debian based path
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
||||||
http.verify_depth = 5
|
|
||||||
end
|
|
||||||
|
|
||||||
req = Net::HTTP::Get.new(uri.path)
|
|
||||||
req.basic_auth ENV['GTD_LOGIN'], ENV['GTD_PASSWORD']
|
|
||||||
resp = http.request(req)
|
|
||||||
|
|
||||||
case resp
|
|
||||||
when Net::HTTPSuccess
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
def initialize(options)
|
||||||
|
@options = options
|
||||||
|
@tracks = TracksCli::TracksAPI.new({
|
||||||
|
uri: ENV['GTD_TODOS_URL'] || 'http://localhost:3000/todos.xml',
|
||||||
|
login: ENV['GTD_LOGIN'],
|
||||||
|
password: ENV['GTD_PASSWORD'],
|
||||||
|
projects_uri: ENV['GTD_PROJECTS_URL'] || 'http://localhost:3000/projects.xml',
|
||||||
|
contexts_uri: ENV['GTD_CONTEXT_URL'] || 'http://localhost:3000/contexts.xml',
|
||||||
|
context_prefix: ENV['GTD_CONTEXT_URL_PREFIX'] || 'http://localhost:3000/contexts/'})
|
||||||
|
@context_id = options[:context_id] ? options[:context_id].to_i : 1
|
||||||
|
@project_id = options[:project_id] ? options[:project_id].to_i : 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def postTodo(parsed_todo, project_id)
|
||||||
|
resp = @tracks.post_todo(
|
||||||
|
description: CGI.escapeHTML(parsed_todo[:description]),
|
||||||
|
context_name: parsed_todo[:context],
|
||||||
|
context_id: @context_id,
|
||||||
|
project_id: project_id || @project_id,
|
||||||
|
show_from: parsed_todo[:show_from],
|
||||||
|
notes: parsed_todo[:notes],
|
||||||
|
is_dependend: parsed_todo[:depend],
|
||||||
|
predecessor: @last_posted_todo_id)
|
||||||
|
|
||||||
class Error < StandardError; end
|
if resp.code == '302' || resp.code == '201'
|
||||||
class InvalidParser < StandardError; end
|
puts resp['location'] if @options[:verbose]
|
||||||
|
|
||||||
|
# return the todo id
|
||||||
|
@last_posted_todo_id = resp['location'].split("/").last
|
||||||
|
return @last_posted_todo_id
|
||||||
|
else
|
||||||
|
p resp.body
|
||||||
|
raise Error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class ConsoleOptions
|
def postProject(project_description)
|
||||||
attr_reader :parser, :options, :keywords
|
project_description.chomp!
|
||||||
|
|
||||||
def initialize
|
resp = @tracks.post_project(
|
||||||
@options = {}
|
description: CGI.escapeHTML(project_description),
|
||||||
@keywords = {}
|
default_context_id: @context_id)
|
||||||
|
|
||||||
@parser = OptionParser.new do |cmd|
|
if resp.code == '302' || resp.code == '201'
|
||||||
cmd.banner = "Ruby Gtd Templates CLI"
|
puts resp['location'] if @options[:verbose]
|
||||||
|
|
||||||
cmd.separator ''
|
# return the project id
|
||||||
|
return resp['location'].split("/").last
|
||||||
|
else
|
||||||
|
p resp.body
|
||||||
|
raise Error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
cmd.on('-h', '--help', 'Displays this help message') do
|
def queryContext(context_id)
|
||||||
puts @parser
|
return false unless context_id.is_a? Integer
|
||||||
exit
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-p [N]', Integer, "project id to set for new todo") do |v|
|
resp = @tracks.get_context(context_id)
|
||||||
@options[:project_id] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-k [S]', "keyword to be replaced") do |v|
|
return resp.code == '200'
|
||||||
@keywords[v.split("=")[0]] = v.split("=")[1]
|
end
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-v', "verbose on") do |v|
|
end
|
||||||
@options[:verbose] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-f [S]', "filename of the template") do |v|
|
class Error < StandardError; end
|
||||||
@filename = v
|
class InvalidParser < StandardError; end
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-c [N]', Integer, 'default context id to set for new projects') do |v|
|
class ConsoleOptions
|
||||||
@options[:context_id] = v
|
attr_reader :parser, :options, :keywords
|
||||||
end
|
|
||||||
|
|
||||||
cmd.on('-w [N]', Integer, 'Postpone task for N weeks') do |v|
|
def initialize
|
||||||
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * (v || 1)
|
@options = {}
|
||||||
end
|
@keywords = {}
|
||||||
|
|
||||||
cmd.on('-m [N]', Integer, 'Postpone task for N months') do |v|
|
@parser = OptionParser.new do |cmd|
|
||||||
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * 4 * (v || 1)
|
cmd.banner = "Ruby Gtd Templates CLI"
|
||||||
end
|
|
||||||
|
cmd.separator ''
|
||||||
|
|
||||||
|
cmd.on('-h', '--help', 'Displays this help message') do
|
||||||
|
puts @parser
|
||||||
|
exit
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-p [N]', Integer, "project id to set for new todo") do |v|
|
||||||
|
@options[:project_id] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-k [S]', "keyword to be replaced") do |v|
|
||||||
|
@keywords[v.split("=")[0]] = v.split("=")[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-v', "verbose on") do |v|
|
||||||
|
@options[:verbose] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-f [S]', "filename of the template") do |v|
|
||||||
|
@filename = v
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-c [N]', Integer, 'default context id to set for new projects') do |v|
|
||||||
|
@options[:context_id] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-w [N]', Integer, 'Postpone task for N weeks') do |v|
|
||||||
|
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * (v || 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd.on('-m [N]', Integer, 'Postpone task for N months') do |v|
|
||||||
|
@options[:show_from] = Time.now.to_i + 24 * 3600 * 7 * 4 * (v || 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def run(args)
|
def run(args)
|
||||||
@parser.parse!(args)
|
@parser.parse!(args)
|
||||||
# lines = STDIN.read
|
@poster = TemplatePoster.new(@options)
|
||||||
gtd = API.new
|
|
||||||
|
|
||||||
if @filename != nil and not File.exist?(@filename)
|
if !@filename.nil? and not File.exist?(@filename)
|
||||||
puts "ERROR: file #{@filename} doesn't exist"
|
puts "ERROR: file #{@filename} doesn't exist"
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if ENV['GTD_LOGIN'] == nil
|
|
||||||
puts "ERROR: no GTD_LOGIN environment variable set"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if ENV['GTD_PASSWORD'] == nil
|
|
||||||
puts "ERROR: no GTD_PASSWORD environment variable set"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if @filename == nil
|
|
||||||
file = STDIN
|
|
||||||
else
|
|
||||||
file = File.open(@filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
## check for existence of the context
|
|
||||||
if !@options[:context_id]
|
|
||||||
puts "ERROR: need to specify a context_id with -c option. Go here to find one: #{API::GTD_URI_CONTEXTS}"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if !gtd.queryContext(@options[:context_id])
|
|
||||||
puts "Error: context_id #{options[:context_id]} doesn't exist"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
#lines.each_line do |line|
|
|
||||||
while line = file.gets
|
|
||||||
line = line.strip
|
|
||||||
next if (line.empty? || line[0].chr == "#")
|
|
||||||
|
|
||||||
if (line.split(' ')[0] == "token")
|
|
||||||
## defining a new token; ask for input
|
|
||||||
|
|
||||||
newtok=line.split(' ')[1]
|
|
||||||
|
|
||||||
print "Input required for "+newtok+": "
|
|
||||||
@keywords[newtok]=gets.chomp
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
# replace tokens
|
|
||||||
@keywords.each do |key,val|
|
|
||||||
line=line.sub(key,val)
|
|
||||||
end
|
|
||||||
|
|
||||||
# decide whether project or task
|
|
||||||
if (line[0].chr == "." ) || (line[0].chr == "^")
|
|
||||||
@options[:depend]= line[0].chr == "^" ? true : false;
|
|
||||||
line = line[1..line.length]
|
|
||||||
|
|
||||||
# find notes
|
|
||||||
tmp= line.split("|")
|
|
||||||
if tmp.length > 5
|
|
||||||
puts "Formatting error: found too many |"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
line=tmp[0]
|
|
||||||
|
|
||||||
tmp.each_with_index do |t,idx|
|
|
||||||
t=t.strip.chomp
|
|
||||||
t=nil if t.empty?
|
|
||||||
tmp[idx]=t
|
|
||||||
end
|
|
||||||
|
|
||||||
@options[:context]=tmp[1]
|
|
||||||
@options[:taglist]=tmp[2]
|
|
||||||
@options[:note]=tmp[3]
|
|
||||||
|
|
||||||
if !@options[:project_id]
|
|
||||||
puts "Warning: no project specified for task \"#{line}\". Using default project."
|
|
||||||
end
|
|
||||||
|
|
||||||
@options[:last_todo_id]=gtd.postTodo(line, @options)
|
|
||||||
else
|
|
||||||
@options[:project_id]=gtd.postProject(line, @options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
rescue InvalidParser
|
|
||||||
puts "Please specify a valid format parser."
|
|
||||||
exit 1
|
|
||||||
rescue Error
|
|
||||||
puts "An unknown error occurred"
|
|
||||||
exit 1
|
exit 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if ENV['GTD_LOGIN'] == nil
|
||||||
|
puts "ERROR: no GTD_LOGIN environment variable set"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if ENV['GTD_PASSWORD'] == nil
|
||||||
|
puts "ERROR: no GTD_PASSWORD environment variable set"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
file = @filename.nil? ? STDIN : File.open(@filename)
|
||||||
|
|
||||||
|
## check for existence of the context
|
||||||
|
if @options[:context_id].nil?
|
||||||
|
puts "ERROR: need to specify a context_id with -c option."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if !@poster.queryContext(@options[:context_id])
|
||||||
|
puts "Error: context_id #{options[:context_id]} doesn't exist"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
TemplateParser.new.parse(file, @poster)
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
rescue InvalidParser
|
||||||
|
puts "Please specify a valid format parser."
|
||||||
|
exit 1
|
||||||
|
rescue Error
|
||||||
|
puts "An unknown error occurred"
|
||||||
|
exit 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if $0 == __FILE__
|
if $0 == __FILE__
|
||||||
app = Gtd::ConsoleOptions.new
|
ConsoleOptions.new.run(ARGV)
|
||||||
app.run(ARGV)
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue