freshbookstimestats 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bba95bba59822e0df7d3f6187da642a167363ba
4
+ data.tar.gz: 8112cbdf0b675d6a6cdfdbe808333b378f1f2906
5
+ SHA512:
6
+ metadata.gz: c305ede55d96feebdc120b74d16a982ccc7e2aab1e4e74840535b600bdaa676ab39db66729ca666906d42d4c7d03fa8da9377a8d63c13b4a5324b2384cbb9999
7
+ data.tar.gz: cad6a12999783a34ec83221dd88124bd915158c9fd71055aba742456a9fcf4b0b6dabb20f4867ee5d1bfbe4bee6dd837207ac776487ce50248703d8b88ad9253
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Amiel Martin
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Project Stats
2
+
3
+ Connect to Freshbooks API to enumerate time spent on one project.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ gem install freshbookstimestats
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Create a `~/.freshbooks` with the following:
14
+
15
+ ```shell
16
+ FESHBOOKS_DOMAIN="<subdomain>.freshbooks.com"
17
+ FRESHBOOKS_AUTH_TOKEN="<auth_token>"
18
+ ```
19
+
20
+ Fill in `<subdomain>` and `<auth_token>`. Your `auth_token` can be found on the
21
+ profile page under API Access -> Authentication Token.
22
+
23
+ ## Usage
24
+
25
+ Run
26
+
27
+ ```shell
28
+ freshbookstimestats
29
+ ```
30
+
31
+ and follow the instructions.
32
+
33
+
34
+ That's it!
35
+
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'freshbookstimestats'
4
+ Freshbookstimestats.load '~/.freshbooks'
5
+ Freshbookstimestats.run
@@ -0,0 +1,156 @@
1
+ require 'freshbooks.rb'
2
+ require 'dotenv'
3
+ require 'terminal-table'
4
+
5
+
6
+ class Task
7
+ def self.find(id)
8
+ memoize :task, id do
9
+ FreshBooks::Task.get id
10
+ end
11
+ end
12
+
13
+ def self.memoize(*keys)
14
+ @_cache ||= {}
15
+ @_cache[keys.join('-')] ||= yield
16
+ end
17
+ end
18
+
19
+ class Projects
20
+ Project = Struct.new(:number, :id, :name)
21
+
22
+ def self.pick_project
23
+ new.select_project
24
+ end
25
+
26
+ def select_project
27
+ puts project_table
28
+ print "\nChoose a project number: "
29
+ project = ask_project_select
30
+ FreshBooks::Project.get project.id if project
31
+ end
32
+
33
+ private
34
+
35
+ def project_table
36
+ Terminal::Table.new do |t|
37
+ t << ['Number', 'Project']
38
+ t << :separator
39
+
40
+ puts "Loading projects..."
41
+ projects.each do |project|
42
+ t << [project.number, project.name]
43
+ end
44
+ end
45
+ end
46
+
47
+ def ask_project_select
48
+ project_number = gets.chomp
49
+ project = find_project_by_number project_number.to_i
50
+ puts "Project not found" if project.nil?
51
+ project
52
+ end
53
+
54
+ def projects
55
+ @_projects ||= FreshBooks::Project.list.map { |project|
56
+ Project.new next_number, project.project_id, project.name
57
+ }
58
+ end
59
+
60
+ def find_project_by_number(number)
61
+ projects.find { |project| project.number == number }
62
+ end
63
+
64
+ def next_number
65
+ @n ||= 0
66
+ @n += 1 # always increment. This way we'll start with 1
67
+ end
68
+ end
69
+
70
+
71
+ class TimeEntries
72
+ def self.render_project(project)
73
+ new(project).render
74
+ end
75
+
76
+ def initialize(project)
77
+ @project = project
78
+ @total_hours = 0
79
+ end
80
+
81
+ def render
82
+ puts time_entries_table
83
+ end
84
+
85
+ private
86
+
87
+ attr_reader :project
88
+
89
+ def time_entries_table
90
+ @_time_entries_table ||= Terminal::Table.new title: title do |t|
91
+ add_header t
92
+ t.add_separator
93
+ add_entries t
94
+ t.add_separator
95
+ add_totals t
96
+ end
97
+ end
98
+
99
+ def title
100
+ project.name
101
+ end
102
+
103
+ def add_header(t)
104
+ t << ['Task', 'Date', 'Hours', 'Notes']
105
+ end
106
+
107
+ def add_entries(t)
108
+ entries.each do |entry|
109
+ task = Task.find entry.task_id
110
+ @total_hours += entry.hours
111
+ t << [task.name, entry.date.inspect, render_hours(entry.hours), entry.notes]
112
+ end
113
+ end
114
+
115
+ def add_totals(t)
116
+ t << [{ value: 'Project Budget', colspan: 2 }, '%7.2f' % project_budget, '']
117
+ t << [{ value: 'Total Hours', colspan: 2 }, '%7.2f' % @total_hours, '']
118
+
119
+ if !project_budget.zero?
120
+ remaining = project_budget - @total_hours
121
+ t << [{ value: 'Time Remaining', colspan: 2 }, '%7.2f' % remaining, '']
122
+ end
123
+ end
124
+
125
+ def render_hours(hours)
126
+ '%7.2f' % hours
127
+ end
128
+
129
+ def project_budget
130
+ @_project_budget ||= project.budget.hours.to_f
131
+ end
132
+
133
+ def entries
134
+ puts "\nLoading...\n\n"
135
+ FreshBooks::TimeEntry.list project_id: project_id
136
+ end
137
+
138
+ def project_id
139
+ project.project_id
140
+ end
141
+ end
142
+
143
+
144
+ module Freshbookstimestats
145
+ def self.load(dir = nil)
146
+ Dotenv.load(*Array(dir))
147
+ FreshBooks::Base.establish_connection(ENV['FRESHBOOKS_DOMAIN'], ENV['FRESHBOOKS_AUTH_TOKEN'])
148
+ end
149
+
150
+ def self.run
151
+ project = Projects.pick_project
152
+ TimeEntries.render_project project if project
153
+ end
154
+ end
155
+
156
+
@@ -0,0 +1,3 @@
1
+ module Freshbookstimestats
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: freshbookstimestats
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Amiel Martin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: freshbooks.rb
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dotenv
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: terminal-table
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: "\n Freshbookstimestats is a simple command line script that asks
84
+ shows a nice table\n of your time entries for a given project.\n "
85
+ email:
86
+ - amiel@carnesmedia.com
87
+ executables:
88
+ - freshbookstimestats
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - LICENSE.txt
93
+ - README.md
94
+ - bin/freshbookstimestats
95
+ - lib/freshbookstimestats.rb
96
+ - lib/freshbookstimestats/version.rb
97
+ homepage: https://github.com/carnesmedia/freshbookstimestats
98
+ licenses:
99
+ - MIT
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.2.0
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: A simple command-line utility to breakdown project hours
121
+ test_files: []