Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial commit

  • Loading branch information...
commit 86bdc46f090ddee08771f519eb8570cb4f9f4220 0 parents
Zack Hobson authored
1  .gitignore
... ... @@ -0,0 +1 @@
  1 +*.sw[nop]
3  .gitmodules
... ... @@ -0,0 +1,3 @@
  1 +[submodule "ext/harvest"]
  2 + path = ext/harvest
  3 + url = git://github.com/aiaio/harvest.git
25 README.markdown
Source Rendered
... ... @@ -0,0 +1,25 @@
  1 +# hcl
  2 +
  3 +HCl is a command-line tool for interacting with Harvest time sheets using the
  4 +[Harvest time tracking API][1].
  5 +
  6 +## Usage
  7 +
  8 + hcl [opts] add <project> <task> <duration> [msg]
  9 + hcl [opts] rm [entry_id]
  10 + hcl [opts] start <project> <task> [msg]
  11 + hcl [opts] stop [msg]
  12 + hcl [opts] show [date]
  13 +
  14 +## TODO
  15 +
  16 + * Implement time-tracking API methods:
  17 + - get daily time sheet
  18 + - get time sheet entry
  19 + - toggle a timer
  20 + - post a time sheet entry
  21 + - delete a time sheet entry
  22 + - update a time sheet entry
  23 +
  24 +[1]: http://www.getharvest.com/api/time_tracking
  25 +
9 bin/hcl
... ... @@ -0,0 +1,9 @@
  1 +#!/usr/bin/env ruby
  2 +
  3 +$:.unshift File.dirname(__FILE__) + '/../lib'
  4 +$:.unshift File.dirname(__FILE__) + '/../ext/harvest/lib'
  5 +
  6 +require 'hcl'
  7 +
  8 +HCl.command *ARGV
  9 +
1  ext/harvest
... ... @@ -0,0 +1 @@
  1 +Subproject commit 485940b1205549c8bea1e0a9c5175c2a51ad7282
3  hcl_conf.yml.example
... ... @@ -0,0 +1,3 @@
  1 +subdomain: mycompany
  2 +login: me@mycompany.com
  3 +password: secret
69 lib/hcl.rb
... ... @@ -0,0 +1,69 @@
  1 +require 'yaml'
  2 +
  3 +require 'rubygems'
  4 +require 'curb'
  5 +
  6 +require 'hcl/day_entry'
  7 +
  8 +class HCl
  9 + class UnknownCommand < RuntimeError; end
  10 +
  11 + def self.command *args
  12 + command = args.shift
  13 + unless command
  14 + help
  15 + return
  16 + end
  17 + hcl = new.process_args *args
  18 + if hcl.respond_to? command
  19 + hcl.send command
  20 + else
  21 + raise UknownCommand, "unrecognized command `#{command}'"
  22 + end
  23 + end
  24 +
  25 + def initialize
  26 + config = YAML::load(File.read('hcl_conf.yml'))
  27 + TimesheetResource.configure config
  28 + end
  29 +
  30 + def self.help
  31 + puts <<-EOM
  32 + Usage:
  33 +
  34 + hcl [opts] add <project> <task> <duration> [msg]
  35 + hcl [opts] rm [entry_id]
  36 + hcl [opts] start <project> <task> [msg]
  37 + hcl [opts] stop [msg]
  38 + hcl [opts] show [date]
  39 + EOM
  40 + end
  41 + def help; self.class.help; end
  42 +
  43 + def process_args *args
  44 + # TODO process command-line args
  45 + self
  46 + end
  47 +
  48 + def show
  49 + total_hours = 0.0
  50 + DayEntry.all.each do |day|
  51 + # TODO more information and formatting options
  52 + puts "#{day.task} / #{day.hours}"
  53 + total_hours = total_hours + day.hours.to_f
  54 + end
  55 + puts "Total #{total_hours} hours"
  56 + end
  57 +
  58 + def not_implemented
  59 + puts "not yet implemented"
  60 + end
  61 +
  62 + # TODO implement the following commands
  63 + alias start not_implemented
  64 + alias stop not_implemented
  65 + alias add not_implemented
  66 + alias rm not_implemented
  67 +
  68 +end
  69 +
66 lib/hcl/day_entry.rb
... ... @@ -0,0 +1,66 @@
  1 +require 'rexml/document'
  2 +
  3 +class HCl
  4 + class TimesheetResource
  5 + def self.configure opts = nil
  6 + if opts
  7 + self.login = opts['login']
  8 + self.password = opts['password']
  9 + self.subdomain = opts['subdomain']
  10 + else
  11 + yield self
  12 + end
  13 + end
  14 +
  15 + # configuration accessors
  16 + %w[ login password subdomain ].each do |config_var|
  17 + class_eval <<-EOC
  18 + def self.#{config_var}= arg
  19 + @@#{config_var} = arg
  20 + end
  21 + def self.#{config_var}
  22 + @@#{config_var}
  23 + end
  24 + EOC
  25 + end
  26 +
  27 + def initialize params
  28 + @data = params
  29 + end
  30 +
  31 + def self.perform action
  32 + client = Curl::Easy.new("https://#{subdomain}.harvestapp.com/#{action}")
  33 + client.headers['Accept'] = 'application/xml'
  34 + client.headers['Content-Type'] = 'application/xml'
  35 + client.http_auth_types = Curl::CURLAUTH_BASIC
  36 + client.userpwd = "#{login}:#{password}"
  37 + if client.http_get
  38 + client.body_str
  39 + else
  40 + raise "failed"
  41 + end
  42 + end
  43 +
  44 + def method_missing method, *args
  45 + if @data.key? method.to_sym
  46 + @data[method]
  47 + else
  48 + super
  49 + end
  50 + end
  51 + end
  52 +
  53 + class DayEntry < TimesheetResource
  54 + def self.all
  55 + doc = REXML::Document.new perform('daily')
  56 + doc.root.elements.collect('day_entries/day_entry') do |day|
  57 + new(
  58 + day.elements.map { |e| e.name }.inject({}) do |a, f|
  59 + a[f.to_sym] = day.elements[f].text if day.elements[f]
  60 + a
  61 + end
  62 + )
  63 + end
  64 + end
  65 + end
  66 +end

0 comments on commit 86bdc46

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