Skip to content
Browse files

initial commit

  • Loading branch information...
0 parents commit 86bdc46f090ddee08771f519eb8570cb4f9f4220 Zack Hobson committed Jul 16, 2009
Showing with 177 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +3 −0 .gitmodules
  3. +25 −0 README.markdown
  4. +9 −0 bin/hcl
  5. +1 −0 ext/harvest
  6. +3 −0 hcl_conf.yml.example
  7. +69 −0 lib/hcl.rb
  8. +66 −0 lib/hcl/day_entry.rb
1 .gitignore
@@ -0,0 +1 @@
+*.sw[nop]
3 .gitmodules
@@ -0,0 +1,3 @@
+[submodule "ext/harvest"]
+ path = ext/harvest
+ url = git://github.com/aiaio/harvest.git
25 README.markdown
@@ -0,0 +1,25 @@
+# hcl
+
+HCl is a command-line tool for interacting with Harvest time sheets using the
+[Harvest time tracking API][1].
+
+## Usage
+
+ hcl [opts] add <project> <task> <duration> [msg]
+ hcl [opts] rm [entry_id]
+ hcl [opts] start <project> <task> [msg]
+ hcl [opts] stop [msg]
+ hcl [opts] show [date]
+
+## TODO
+
+ * Implement time-tracking API methods:
+ - get daily time sheet
+ - get time sheet entry
+ - toggle a timer
+ - post a time sheet entry
+ - delete a time sheet entry
+ - update a time sheet entry
+
+[1]: http://www.getharvest.com/api/time_tracking
+
9 bin/hcl
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+
+$:.unshift File.dirname(__FILE__) + '/../lib'
+$:.unshift File.dirname(__FILE__) + '/../ext/harvest/lib'
+
+require 'hcl'
+
+HCl.command *ARGV
+
1 ext/harvest
@@ -0,0 +1 @@
+Subproject commit 485940b1205549c8bea1e0a9c5175c2a51ad7282
3 hcl_conf.yml.example
@@ -0,0 +1,3 @@
+subdomain: mycompany
+login: me@mycompany.com
+password: secret
69 lib/hcl.rb
@@ -0,0 +1,69 @@
+require 'yaml'
+
+require 'rubygems'
+require 'curb'
+
+require 'hcl/day_entry'
+
+class HCl
+ class UnknownCommand < RuntimeError; end
+
+ def self.command *args
+ command = args.shift
+ unless command
+ help
+ return
+ end
+ hcl = new.process_args *args
+ if hcl.respond_to? command
+ hcl.send command
+ else
+ raise UknownCommand, "unrecognized command `#{command}'"
+ end
+ end
+
+ def initialize
+ config = YAML::load(File.read('hcl_conf.yml'))
+ TimesheetResource.configure config
+ end
+
+ def self.help
+ puts <<-EOM
+ Usage:
+
+ hcl [opts] add <project> <task> <duration> [msg]
+ hcl [opts] rm [entry_id]
+ hcl [opts] start <project> <task> [msg]
+ hcl [opts] stop [msg]
+ hcl [opts] show [date]
+ EOM
+ end
+ def help; self.class.help; end
+
+ def process_args *args
+ # TODO process command-line args
+ self
+ end
+
+ def show
+ total_hours = 0.0
+ DayEntry.all.each do |day|
+ # TODO more information and formatting options
+ puts "#{day.task} / #{day.hours}"
+ total_hours = total_hours + day.hours.to_f
+ end
+ puts "Total #{total_hours} hours"
+ end
+
+ def not_implemented
+ puts "not yet implemented"
+ end
+
+ # TODO implement the following commands
+ alias start not_implemented
+ alias stop not_implemented
+ alias add not_implemented
+ alias rm not_implemented
+
+end
+
66 lib/hcl/day_entry.rb
@@ -0,0 +1,66 @@
+require 'rexml/document'
+
+class HCl
+ class TimesheetResource
+ def self.configure opts = nil
+ if opts
+ self.login = opts['login']
+ self.password = opts['password']
+ self.subdomain = opts['subdomain']
+ else
+ yield self
+ end
+ end
+
+ # configuration accessors
+ %w[ login password subdomain ].each do |config_var|
+ class_eval <<-EOC
+ def self.#{config_var}= arg
+ @@#{config_var} = arg
+ end
+ def self.#{config_var}
+ @@#{config_var}
+ end
+ EOC
+ end
+
+ def initialize params
+ @data = params
+ end
+
+ def self.perform action
+ client = Curl::Easy.new("https://#{subdomain}.harvestapp.com/#{action}")
+ client.headers['Accept'] = 'application/xml'
+ client.headers['Content-Type'] = 'application/xml'
+ client.http_auth_types = Curl::CURLAUTH_BASIC
+ client.userpwd = "#{login}:#{password}"
+ if client.http_get
+ client.body_str
+ else
+ raise "failed"
+ end
+ end
+
+ def method_missing method, *args
+ if @data.key? method.to_sym
+ @data[method]
+ else
+ super
+ end
+ end
+ end
+
+ class DayEntry < TimesheetResource
+ def self.all
+ doc = REXML::Document.new perform('daily')
+ doc.root.elements.collect('day_entries/day_entry') do |day|
+ new(
+ day.elements.map { |e| e.name }.inject({}) do |a, f|
+ a[f.to_sym] = day.elements[f].text if day.elements[f]
+ a
+ end
+ )
+ end
+ end
+ end
+end

0 comments on commit 86bdc46

Please sign in to comment.
Something went wrong with that request. Please try again.