88miles 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +81 -0
- data/LICENSE.txt +20 -0
- data/README.md +79 -0
- data/Rakefile +36 -0
- data/VERSION +1 -0
- data/bin/88miles +3 -0
- data/gigawatt.gemspec +89 -0
- data/lib/gigawatt/application.rb +8 -0
- data/lib/gigawatt/cache.rb +71 -0
- data/lib/gigawatt/commands/init.rb +51 -0
- data/lib/gigawatt/commands/log.rb +162 -0
- data/lib/gigawatt/commands/setup.rb +107 -0
- data/lib/gigawatt/commands/start.rb +75 -0
- data/lib/gigawatt/commands/status.rb +101 -0
- data/lib/gigawatt/commands/stop.rb +57 -0
- data/lib/gigawatt/commands/sync.rb +53 -0
- data/lib/gigawatt/oauth.rb +23 -0
- data/lib/gigawatt/options.rb +53 -0
- data/lib/gigawatt/project_file.rb +34 -0
- data/lib/gigawatt/settings.rb +41 -0
- data/lib/gigawatt.rb +29 -0
- data/test/helper.rb +18 -0
- data/test/test_gigawatt.rb +7 -0
- metadata +177 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
gem "trollop", "~> 2.0"
|
3
|
+
gem "highline", "~> 1.6.19"
|
4
|
+
gem "oauth2", "~> 0.9.2i"
|
5
|
+
gem "launchy", "~> 2.3.0"
|
6
|
+
gem "terminal-table", "~> 1.4.5"
|
7
|
+
|
8
|
+
group :development do
|
9
|
+
gem "shoulda", ">= 0"
|
10
|
+
gem "rdoc", "~> 3.12"
|
11
|
+
gem "bundler", "~> 1.0"
|
12
|
+
gem "jeweler", "~> 1.8.7"
|
13
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.0.0)
|
5
|
+
i18n (~> 0.6, >= 0.6.4)
|
6
|
+
minitest (~> 4.2)
|
7
|
+
multi_json (~> 1.3)
|
8
|
+
thread_safe (~> 0.1)
|
9
|
+
tzinfo (~> 0.3.37)
|
10
|
+
addressable (2.3.5)
|
11
|
+
atomic (1.1.14)
|
12
|
+
builder (3.2.2)
|
13
|
+
faraday (0.8.8)
|
14
|
+
multipart-post (~> 1.2.0)
|
15
|
+
git (1.2.6)
|
16
|
+
github_api (0.10.1)
|
17
|
+
addressable
|
18
|
+
faraday (~> 0.8.1)
|
19
|
+
hashie (>= 1.2)
|
20
|
+
multi_json (~> 1.4)
|
21
|
+
nokogiri (~> 1.5.2)
|
22
|
+
oauth2
|
23
|
+
hashie (2.0.5)
|
24
|
+
highline (1.6.19)
|
25
|
+
httpauth (0.2.0)
|
26
|
+
i18n (0.6.5)
|
27
|
+
jeweler (1.8.7)
|
28
|
+
builder
|
29
|
+
bundler (~> 1.0)
|
30
|
+
git (>= 1.2.5)
|
31
|
+
github_api (= 0.10.1)
|
32
|
+
highline (>= 1.6.15)
|
33
|
+
nokogiri (= 1.5.10)
|
34
|
+
rake
|
35
|
+
rdoc
|
36
|
+
json (1.8.0)
|
37
|
+
jwt (0.1.8)
|
38
|
+
multi_json (>= 1.5)
|
39
|
+
launchy (2.3.0)
|
40
|
+
addressable (~> 2.3)
|
41
|
+
minitest (4.7.5)
|
42
|
+
multi_json (1.8.1)
|
43
|
+
multi_xml (0.5.5)
|
44
|
+
multipart-post (1.2.0)
|
45
|
+
nokogiri (1.5.10)
|
46
|
+
oauth2 (0.9.2)
|
47
|
+
faraday (~> 0.8)
|
48
|
+
httpauth (~> 0.2)
|
49
|
+
jwt (~> 0.1.4)
|
50
|
+
multi_json (~> 1.0)
|
51
|
+
multi_xml (~> 0.5)
|
52
|
+
rack (~> 1.2)
|
53
|
+
rack (1.5.2)
|
54
|
+
rake (10.1.0)
|
55
|
+
rdoc (3.12.2)
|
56
|
+
json (~> 1.4)
|
57
|
+
shoulda (3.5.0)
|
58
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
59
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
60
|
+
shoulda-context (1.1.5)
|
61
|
+
shoulda-matchers (2.4.0)
|
62
|
+
activesupport (>= 3.0.0)
|
63
|
+
terminal-table (1.4.5)
|
64
|
+
thread_safe (0.1.3)
|
65
|
+
atomic
|
66
|
+
trollop (2.0)
|
67
|
+
tzinfo (0.3.37)
|
68
|
+
|
69
|
+
PLATFORMS
|
70
|
+
ruby
|
71
|
+
|
72
|
+
DEPENDENCIES
|
73
|
+
bundler (~> 1.0)
|
74
|
+
highline (~> 1.6.19)
|
75
|
+
jeweler (~> 1.8.7)
|
76
|
+
launchy (~> 2.3.0)
|
77
|
+
oauth2 (~> 0.9.2i)
|
78
|
+
rdoc (~> 3.12)
|
79
|
+
shoulda
|
80
|
+
terminal-table (~> 1.4.5)
|
81
|
+
trollop (~> 2.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 MadPilot Productions
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# 88 Miles Command Line Application
|
2
|
+
|
3
|
+
88 Miles is simple time tracking for freelance developers, designers and copywriters. This gem allows you to access you account from your command line.
|
4
|
+
|
5
|
+
To use this gem, you will need an 88 Miles account. You can [register here](http://88miles.net)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
gem install 88miles
|
10
|
+
|
11
|
+
## Get started
|
12
|
+
|
13
|
+
First, you will need to give the gem access to 88 Miles. This is done via OAuth, so you don't need to ever store your login or password.
|
14
|
+
|
15
|
+
To setup the link:
|
16
|
+
|
17
|
+
88miles setup
|
18
|
+
|
19
|
+
If you on a computer has a default browser, it will open a browser window asking you to login, and approve access to the application.
|
20
|
+
|
21
|
+
Once authentication has happened, you will be redirected to a confirmation window. Cut and paste the URL from this window in to the console that you started the process.
|
22
|
+
|
23
|
+
## Linking a Project to a directory
|
24
|
+
|
25
|
+
The command line application links directories with projects in 88 Miles. To setup the link, run
|
26
|
+
|
27
|
+
88miles init [path/to/directory]
|
28
|
+
|
29
|
+
You will be presented with a list of projects to select from. Select a project, and the link is complete.
|
30
|
+
|
31
|
+
## Punching in
|
32
|
+
|
33
|
+
Once you have linked a project, you can punch in by:
|
34
|
+
|
35
|
+
88miles start
|
36
|
+
|
37
|
+
If you want to select an activity:
|
38
|
+
|
39
|
+
88miles start -a
|
40
|
+
|
41
|
+
You will be presented with a list of activities from which you can select from
|
42
|
+
|
43
|
+
## Punching out
|
44
|
+
|
45
|
+
To punch out of the linked project
|
46
|
+
|
47
|
+
88miles stop -n "Any notes to associate with shift"
|
48
|
+
|
49
|
+
## View the current status of the project
|
50
|
+
|
51
|
+
You can see how much time you have clocked against a project by:
|
52
|
+
|
53
|
+
88miles status
|
54
|
+
|
55
|
+
If you want to have it automatically update, you can leave it in the foreground
|
56
|
+
|
57
|
+
88miles status -f
|
58
|
+
|
59
|
+
Hit Ctrl-C to exit foreground mode.
|
60
|
+
|
61
|
+
## List all shifts
|
62
|
+
|
63
|
+
If you want to view a list of all the shifts clocked against the linked project:
|
64
|
+
|
65
|
+
88miles log
|
66
|
+
|
67
|
+
To format it in a nice table view:
|
68
|
+
|
69
|
+
88miles log -t
|
70
|
+
|
71
|
+
## Update the local cache
|
72
|
+
|
73
|
+
To speed things up, your company, project and staff list is cached locally. If you modify any of these things on the website, you'll need to re-sync the cache by:
|
74
|
+
|
75
|
+
88miles sync
|
76
|
+
|
77
|
+
## Copyright
|
78
|
+
|
79
|
+
Copyright (c) 2013 [MadPilot Productions](http://www.madpilot.com.au/). See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "88miles"
|
18
|
+
gem.homepage = "http://github.com/madpilot/gigawatt"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Command line interface to 88 Miles}
|
21
|
+
gem.description = %Q{88 Miles (http://88miles.net) is simple time tracking for freelance developers, designers and copywriters. This gem allows you to access you account from your command line.}
|
22
|
+
gem.email = "myles@madpilot.com.au"
|
23
|
+
gem.authors = ["Myles Eftos"]
|
24
|
+
gem.executables = [ '88miles' ]
|
25
|
+
# dependencies defined in Gemfile
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
Rake::TestTask.new(:test) do |test|
|
31
|
+
test.libs << 'lib' << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :default => :test
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
data/bin/88miles
ADDED
data/gigawatt.gemspec
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "gigawatt"
|
8
|
+
s.version = "1.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Myles Eftos"]
|
12
|
+
s.date = "2013-10-06"
|
13
|
+
s.description = "88 Miles (http://88miles.net) is simple time tracking for freelance developers, designers and copywriters. This gem allows you to access you account from your command line."
|
14
|
+
s.email = "myles@madpilot.com.au"
|
15
|
+
s.executables = ["88miles"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.md"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".document",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"bin/88miles",
|
29
|
+
"gigawatt.gemspec",
|
30
|
+
"lib/gigawatt.rb",
|
31
|
+
"lib/gigawatt/application.rb",
|
32
|
+
"lib/gigawatt/cache.rb",
|
33
|
+
"lib/gigawatt/commands/init.rb",
|
34
|
+
"lib/gigawatt/commands/log.rb",
|
35
|
+
"lib/gigawatt/commands/setup.rb",
|
36
|
+
"lib/gigawatt/commands/start.rb",
|
37
|
+
"lib/gigawatt/commands/status.rb",
|
38
|
+
"lib/gigawatt/commands/stop.rb",
|
39
|
+
"lib/gigawatt/commands/sync.rb",
|
40
|
+
"lib/gigawatt/oauth.rb",
|
41
|
+
"lib/gigawatt/options.rb",
|
42
|
+
"lib/gigawatt/project_file.rb",
|
43
|
+
"lib/gigawatt/settings.rb",
|
44
|
+
"test/helper.rb",
|
45
|
+
"test/test_gigawatt.rb"
|
46
|
+
]
|
47
|
+
s.homepage = "http://github.com/madpilot/gigawatt"
|
48
|
+
s.licenses = ["MIT"]
|
49
|
+
s.require_paths = ["lib"]
|
50
|
+
s.rubygems_version = "1.8.15"
|
51
|
+
s.summary = "Command line interface to 88 Miles"
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_runtime_dependency(%q<trollop>, ["~> 2.0"])
|
58
|
+
s.add_runtime_dependency(%q<highline>, ["~> 1.6.19"])
|
59
|
+
s.add_runtime_dependency(%q<oauth2>, ["~> 0.9.2i"])
|
60
|
+
s.add_runtime_dependency(%q<launchy>, ["~> 2.3.0"])
|
61
|
+
s.add_runtime_dependency(%q<terminal-table>, ["~> 1.4.5"])
|
62
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
63
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
64
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
65
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.7"])
|
66
|
+
else
|
67
|
+
s.add_dependency(%q<trollop>, ["~> 2.0"])
|
68
|
+
s.add_dependency(%q<highline>, ["~> 1.6.19"])
|
69
|
+
s.add_dependency(%q<oauth2>, ["~> 0.9.2i"])
|
70
|
+
s.add_dependency(%q<launchy>, ["~> 2.3.0"])
|
71
|
+
s.add_dependency(%q<terminal-table>, ["~> 1.4.5"])
|
72
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
73
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
74
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
75
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
76
|
+
end
|
77
|
+
else
|
78
|
+
s.add_dependency(%q<trollop>, ["~> 2.0"])
|
79
|
+
s.add_dependency(%q<highline>, ["~> 1.6.19"])
|
80
|
+
s.add_dependency(%q<oauth2>, ["~> 0.9.2i"])
|
81
|
+
s.add_dependency(%q<launchy>, ["~> 2.3.0"])
|
82
|
+
s.add_dependency(%q<terminal-table>, ["~> 1.4.5"])
|
83
|
+
s.add_dependency(%q<shoulda>, [">= 0"])
|
84
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
85
|
+
s.add_dependency(%q<bundler>, ["~> 1.0"])
|
86
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.7"])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Gigawatt
|
2
|
+
class Cache
|
3
|
+
def initialize(settings, access_key)
|
4
|
+
@access_key = access_key
|
5
|
+
@settings = settings
|
6
|
+
end
|
7
|
+
|
8
|
+
def fetch_companies
|
9
|
+
companies = JSON.parse(@access_key.get('/api/1/companies.json').body)
|
10
|
+
companies["response"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def fetch_projects
|
14
|
+
projects = JSON.parse(@access_key.get("/api/1/projects.json?where=#{URI::encode('active="true"')}").body)
|
15
|
+
projects["response"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch_staff
|
19
|
+
staff = JSON.parse(@access_key.get("/api/1/staff.json").body)
|
20
|
+
staff["response"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def refresh!
|
24
|
+
@settings.companies = nil
|
25
|
+
@settings.projects = nil
|
26
|
+
@settings.staff = nil
|
27
|
+
companies
|
28
|
+
projects
|
29
|
+
staff
|
30
|
+
end
|
31
|
+
|
32
|
+
def companies(indexed = false)
|
33
|
+
if @settings.companies.nil?
|
34
|
+
@settings.companies = fetch_companies
|
35
|
+
@settings.write(:companies)
|
36
|
+
end
|
37
|
+
return @settings.companies unless indexed
|
38
|
+
companies = {}
|
39
|
+
@settings.companies.each do |c|
|
40
|
+
companies[c["uuid"]] = c
|
41
|
+
end
|
42
|
+
companies
|
43
|
+
end
|
44
|
+
|
45
|
+
def projects(indexed = false)
|
46
|
+
if @settings.projects.nil?
|
47
|
+
@settings.projects = fetch_projects
|
48
|
+
@settings.write(:projects)
|
49
|
+
end
|
50
|
+
return @settings.projects unless indexed
|
51
|
+
projects = {}
|
52
|
+
@settings.projects.each do |p|
|
53
|
+
projects[p["uuid"]] = p
|
54
|
+
end
|
55
|
+
projects
|
56
|
+
end
|
57
|
+
|
58
|
+
def staff(indexed = false)
|
59
|
+
if @settings.staff.nil?
|
60
|
+
@settings.staff = fetch_staff
|
61
|
+
@settings.write(:staff)
|
62
|
+
end
|
63
|
+
return @settings.staff unless indexed
|
64
|
+
staff = {}
|
65
|
+
@settings.staff.each do |s|
|
66
|
+
staff[s["uuid"]] = s
|
67
|
+
end
|
68
|
+
staff
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Gigawatt
|
2
|
+
module Commands
|
3
|
+
class Init
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def self.run!(settings)
|
7
|
+
directory = nil
|
8
|
+
p = Trollop::Parser.new
|
9
|
+
options = p.parse
|
10
|
+
directory = p.leftovers.first
|
11
|
+
|
12
|
+
Trollop::die "Please supply a directory" unless directory
|
13
|
+
Trollop::die "Directory does not exist" unless File.exists?(directory)
|
14
|
+
Trollop::die "#{directory} is not a directory" unless File.directory?(directory)
|
15
|
+
|
16
|
+
instance = self.new(settings, options, directory)
|
17
|
+
begin
|
18
|
+
instance.list_projects
|
19
|
+
rescue OAuth2::Error => e
|
20
|
+
say "Access to your 88 Miles may have been revoked. Please run <%= color('88miles setup', BOLD) %> again."
|
21
|
+
return INVALID_OAUTH_TOKEN_EXIT_CODE
|
22
|
+
end
|
23
|
+
|
24
|
+
return OK_EXIT_CODE
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(settings, options, directory)
|
28
|
+
@settings = settings
|
29
|
+
@options = options
|
30
|
+
@directory = directory
|
31
|
+
@access_key = OAuth.token(settings.access_key)
|
32
|
+
@cache = Cache.new(settings, @access_key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def list_projects
|
36
|
+
companies = @cache.companies(true)
|
37
|
+
|
38
|
+
selected = nil
|
39
|
+
choose do |menu|
|
40
|
+
menu.prompt = "Pick a project"
|
41
|
+
@cache.projects.each do |project|
|
42
|
+
menu.choice("#{companies[project["company_uuid"]]["name"]}: #{project["name"]}") { selected = project }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ProjectFile.write(selected)
|
47
|
+
say("<%= color('#{companies[selected["company_uuid"]]["name"]}: #{selected["name"]}', GREEN) %> selected. Run <%= color('88miles start', BOLD) %> to punch in")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
module Gigawatt
|
2
|
+
module Commands
|
3
|
+
class Log
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def self.run!(settings)
|
7
|
+
options = Trollop::options do
|
8
|
+
banner <<-EOS
|
9
|
+
88 Miles Command line application - http://88miles.net
|
10
|
+
|
11
|
+
Print out the shifts for the linked project
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
88miles log [options]
|
15
|
+
|
16
|
+
options
|
17
|
+
EOS
|
18
|
+
opt :page, "Page the output", :type => :flag, :default => true
|
19
|
+
opt :table, "Outputs a formatted table", :type => :flag, :default => false
|
20
|
+
end
|
21
|
+
|
22
|
+
instance = self.new(settings, options)
|
23
|
+
begin
|
24
|
+
return instance.log
|
25
|
+
rescue OAuth2::Error => e
|
26
|
+
say "Access to your 88 Miles may have been revoked. Please run <%= color('88miles setup', BOLD) %> again."
|
27
|
+
return INVALID_OAUTH_TOKEN_EXIT_CODE
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(settings, options)
|
32
|
+
@settings = settings
|
33
|
+
@options = options
|
34
|
+
|
35
|
+
@access_key = OAuth.token(@settings.access_key)
|
36
|
+
@cache = Cache.new(settings, @access_key)
|
37
|
+
@project = Gigawatt::ProjectFile.new.project
|
38
|
+
end
|
39
|
+
|
40
|
+
def log
|
41
|
+
unless @project
|
42
|
+
say("No project found.")
|
43
|
+
return NO_PROJECT_EXIT_CODE
|
44
|
+
end
|
45
|
+
|
46
|
+
$terminal.page_at = :auto if @options[:page]
|
47
|
+
|
48
|
+
if @options[:table]
|
49
|
+
log_table
|
50
|
+
else
|
51
|
+
log_blob
|
52
|
+
end
|
53
|
+
return OK_EXIT_CODE
|
54
|
+
end
|
55
|
+
|
56
|
+
def log_blob
|
57
|
+
buffer = ''
|
58
|
+
shifts = JSON.parse(@access_key.get("/api/1/projects/#{@project["uuid"]}/shifts.json").body)
|
59
|
+
|
60
|
+
company = @cache.companies(true)[@project["company_uuid"]]
|
61
|
+
staff = @cache.staff(true)
|
62
|
+
buffer += "#{company["name"]}: #{@project["name"]} shifts:\n\n"
|
63
|
+
|
64
|
+
total = 0
|
65
|
+
rows = []
|
66
|
+
str = ""
|
67
|
+
shifts["response"].each do |shift|
|
68
|
+
staff_member = staff[shift["user_uuid"]]
|
69
|
+
start = Time.parse(shift["start"])
|
70
|
+
stop = Time.parse(shift["stop"] || Time.now.to_s)
|
71
|
+
total += (stop - start)
|
72
|
+
|
73
|
+
str += "<%= color('uuid #{shift["uuid"]}', YELLOW) %>\n"
|
74
|
+
|
75
|
+
if staff_member
|
76
|
+
str += "Staff: #{staff_member["first_name"]} #{staff_member["last_name"]} <#{staff_member["email_address"]}>\n"
|
77
|
+
else
|
78
|
+
str += "Staff: [Deleted user]\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
str += "Start: #{start.getlocal.strftime('%c %:z')}\n"
|
82
|
+
str += "Stop: #{stop.getlocal.strftime('%c %:z')}\n"
|
83
|
+
str += "Total: #{to_clock_s(stop - start)}\n"
|
84
|
+
str += "\n\t"
|
85
|
+
if shift["notes"].to_s == ""
|
86
|
+
str += "No notes\n"
|
87
|
+
else
|
88
|
+
str += "#{shift["notes"]}\n"
|
89
|
+
end
|
90
|
+
str += "\n"
|
91
|
+
end
|
92
|
+
|
93
|
+
overdue = @project["grand_total"] > @project["time_limit"] if @project["time_limit"]
|
94
|
+
if overdue
|
95
|
+
str += "Total: #{HighLine::String.new(to_clock_s(total)).red}"
|
96
|
+
else
|
97
|
+
str += "Total: #{to_clock_s(total)}"
|
98
|
+
end
|
99
|
+
|
100
|
+
say(str)
|
101
|
+
end
|
102
|
+
|
103
|
+
def log_table
|
104
|
+
buffer = ''
|
105
|
+
shifts = JSON.parse(@access_key.get("/api/1/projects/#{@project["uuid"]}/shifts.json").body)
|
106
|
+
|
107
|
+
company = @cache.companies(true)[@project["company_uuid"]]
|
108
|
+
staff = @cache.staff(true)
|
109
|
+
buffer += "#{company["name"]}: #{@project["name"]} shifts:\n\n"
|
110
|
+
|
111
|
+
total = 0
|
112
|
+
rows = []
|
113
|
+
shifts["response"].each do |shift|
|
114
|
+
row = []
|
115
|
+
|
116
|
+
staff_member = staff[shift["user_uuid"]]
|
117
|
+
start = Time.parse(shift["start"])
|
118
|
+
stop = Time.parse(shift["stop"] || Time.now.to_s)
|
119
|
+
total += (stop - start)
|
120
|
+
|
121
|
+
if staff_member
|
122
|
+
row << "#{staff_member["first_name"]} #{staff_member["last_name"]}"
|
123
|
+
else
|
124
|
+
row << "[Deleted user]"
|
125
|
+
end
|
126
|
+
row << "#{start.getlocal.strftime("%d/%M/%Y %H:%M:%S")} - #{stop.getlocal.strftime("%H:%M:%S")}"
|
127
|
+
row << "#{to_clock_s(stop - start)}"
|
128
|
+
|
129
|
+
if shift["notes"].to_s == ""
|
130
|
+
row << " No notes"
|
131
|
+
else
|
132
|
+
row << " #{shift["notes"]}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if shift["stop"].nil?
|
136
|
+
row = row.map{ |col| HighLine::String.new(col).green }
|
137
|
+
end
|
138
|
+
rows << row
|
139
|
+
end
|
140
|
+
|
141
|
+
rows << :separator
|
142
|
+
overdue = @project["grand_total"] > @project["time_limit"] if @project["time_limit"]
|
143
|
+
if overdue
|
144
|
+
rows << [ 'Total', '', HighLine::String.new(to_clock_s(total)).red, '' ]
|
145
|
+
else
|
146
|
+
rows << [ 'Total', '', to_clock_s(total), '' ]
|
147
|
+
end
|
148
|
+
|
149
|
+
say(Terminal::Table.new(:rows => rows).to_s) if @options[:table]
|
150
|
+
end
|
151
|
+
|
152
|
+
def to_clock_s(time, show_seconds = false)
|
153
|
+
hour = (time.abs / 3600).floor
|
154
|
+
minute = (time.abs / 60 % 60).floor
|
155
|
+
seconds = (time.abs % 60).floor if show_seconds
|
156
|
+
|
157
|
+
return (time != 0 && (time / time.abs) == -1 ? "-" : "") + hour.to_s.rjust(2, '0') + ":" + minute.to_s.rjust(2, '0') + (show_seconds ? ":" + seconds.to_s.rjust(2, '0') : '')
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|