TokiCLI 0.2.1 → 0.3.0
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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +9 -17
- data/.rspec +3 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +4 -0
- data/README.md +99 -163
- data/Rakefile +5 -0
- data/TokiCLI.gemspec +5 -4
- data/bin/toki +2 -2
- data/lib/API/helpers.rb +27 -170
- data/lib/API/toki_api.rb +227 -0
- data/lib/API/toki_db.rb +67 -0
- data/lib/TokiCLI.rb +270 -5
- data/lib/TokiCLI/adnimport.rb +202 -0
- data/lib/TokiCLI/fileops.rb +187 -0
- data/lib/TokiCLI/status.rb +49 -37
- data/lib/TokiCLI/version.rb +1 -1
- data/lib/TokiCLI/view.rb +133 -86
- data/lib/TokiServer/.bowerrc +3 -0
- data/lib/TokiServer/.gitignore +2 -0
- data/lib/TokiServer/Gemfile +1 -0
- data/lib/TokiServer/bower.json +0 -0
- data/lib/TokiServer/bower_components/fastclick/.bower.json +4 -4
- data/lib/TokiServer/bower_components/fastclick/bower.json +1 -1
- data/lib/TokiServer/bower_components/fastclick/lib/fastclick.js +33 -2
- data/lib/TokiServer/bower_components/foundation/.bower.json +5 -4
- data/lib/TokiServer/bower_components/foundation/bower.json +2 -1
- data/lib/TokiServer/bower_components/foundation/css/foundation.css +2173 -922
- data/lib/TokiServer/bower_components/foundation/css/foundation.css.map +7 -0
- data/lib/TokiServer/bower_components/foundation/css/normalize.css +53 -16
- data/lib/TokiServer/bower_components/foundation/css/normalize.css.map +7 -0
- data/lib/TokiServer/bower_components/foundation/js/foundation.js +782 -542
- data/lib/TokiServer/bower_components/foundation/js/foundation.min.js +4 -3
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.abide.js +45 -31
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.accordion.js +12 -6
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.alert.js +5 -5
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.clearing.js +34 -10
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.dropdown.js +83 -29
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.equalizer.js +3 -3
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.interchange.js +25 -12
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.joyride.js +112 -40
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.js +19 -5
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.magellan.js +22 -11
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.offcanvas.js +52 -8
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.orbit.js +133 -271
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.reveal.js +27 -20
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.slider.js +73 -33
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.tab.js +88 -31
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.tooltip.js +7 -5
- data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.topbar.js +44 -24
- data/lib/TokiServer/bower_components/foundation/js/vendor/fastclick.js +2 -2
- data/lib/TokiServer/bower_components/foundation/js/vendor/modernizr.js +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation.scss +38 -38
- data/lib/TokiServer/bower_components/foundation/scss/foundation/_functions.scss +3 -3
- data/lib/TokiServer/bower_components/foundation/scss/foundation/_settings.scss +417 -271
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_accordion.scss +110 -6
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_alert-boxes.scss +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_block-grid.scss +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_breadcrumbs.scss +8 -3
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_button-groups.scss +99 -9
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_buttons.scss +66 -28
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_clearing.scss +5 -5
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_dropdown-buttons.scss +4 -4
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_dropdown.scss +48 -35
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_forms.scss +104 -32
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_global.scss +48 -30
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_grid.scss +19 -4
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_icon-bar.scss +293 -0
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_joyride.scss +11 -9
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_keystrokes.scss +4 -4
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_labels.scss +4 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_magellan.scss +1 -1
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_offcanvas.scss +193 -35
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_orbit.scss +92 -147
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_pagination.scss +22 -10
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_panels.scss +10 -7
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_pricing-tables.scss +11 -11
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_progress-bars.scss +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_range-slider.scss +29 -9
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_reveal.scss +60 -56
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_side-nav.scss +3 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_split-buttons.scss +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_sub-nav.scss +2 -2
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_switches.scss +226 -0
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tables.scss +13 -7
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tabs.scss +22 -8
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_thumbs.scss +2 -4
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_toolbar.scss +70 -0
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tooltips.scss +9 -7
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_top-bar.scss +77 -44
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_type.scss +21 -9
- data/lib/TokiServer/bower_components/modernizr/.bower.json +4 -4
- data/lib/TokiServer/bower_components/modernizr/feature-detects/workers-blobworkers.js +2 -2
- data/lib/TokiServer/bower_components/modernizr/grunt.js +1 -1
- data/lib/TokiServer/bower_components/modernizr/modernizr.js +2 -2
- data/lib/TokiServer/config.rb +0 -0
- data/lib/TokiServer/humans.txt +0 -0
- data/lib/TokiServer/{itunesicon.rb → itunesicons.rb} +25 -23
- data/lib/TokiServer/js/app.js +0 -0
- data/lib/TokiServer/public/stylesheets/app.css +2636 -1610
- data/lib/TokiServer/robots.txt +0 -0
- data/lib/TokiServer/scss/_settings.scss +0 -0
- data/lib/TokiServer/scss/app.scss +7 -1
- data/lib/TokiServer/tokiserver.rb +239 -244
- data/lib/TokiServer/views/activity.erb +42 -0
- data/lib/TokiServer/views/apps_total.erb +8 -2
- data/lib/TokiServer/views/error.erb +13 -4
- data/lib/TokiServer/views/index.erb +36 -27
- data/lib/TokiServer/views/logs_total.erb +34 -0
- data/spec/TokiCLI_spec.rb +354 -0
- data/spec/mock/mock.sqlite3 +0 -0
- data/spec/spec_helper.rb +26 -0
- metadata +78 -23
- data/lib/API/dbapi.rb +0 -488
- data/lib/TokiCLI/app.rb +0 -389
- data/lib/TokiCLI/authorize.rb +0 -77
- data/lib/TokiCLI/export.rb +0 -81
- data/lib/TokiCLI/get_channels.rb +0 -22
- data/lib/TokiCLI/get_messages.rb +0 -32
- data/lib/TokiCLI/import.rb +0 -122
- data/lib/TokiCLI/scan.rb +0 -19
- data/lib/TokiCLI/search_messages.rb +0 -23
- data/lib/TokiServer/README.md +0 -37
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_reveal-new.scss +0 -0
- data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_switch.scss +0 -294
- data/lib/TokiServer/views/name_log.erb +0 -50
- data/lib/TokiServer/views/name_split.erb +0 -37
- data/lib/TokiServer/views/name_total.erb +0 -34
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module TokiCLI
|
|
3
|
+
class FileOps
|
|
4
|
+
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'CFPropertyList'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
|
|
9
|
+
attr_accessor :home_path, :toki_path, :db_path, :db_file, :bundles_file, :bundles, :config_file, :config_path, :config, :data_path, :files_path, :user_file, :log_path
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@home_path = Dir.home
|
|
13
|
+
@toki_path = "#{@home_path}/.TokiCLI"
|
|
14
|
+
@data_path = "#{@toki_path}/data"
|
|
15
|
+
@config_path = "#{@toki_path}/config"
|
|
16
|
+
@files_path = "#{@toki_path}/files"
|
|
17
|
+
@backup_path = "#{@toki_path}/backup"
|
|
18
|
+
@log_path = "#{@toki_path}/log"
|
|
19
|
+
@db_path = "#{@home_path}/Library/Containers/us.kkob.Toki/Data/Documents"
|
|
20
|
+
@db_file = "#{@db_path}/toki_data.sqlite3"
|
|
21
|
+
@bundles_file = "#{@files_path}/bundles.json"
|
|
22
|
+
@config_file = "#{@config_path}/config.yml"
|
|
23
|
+
@user_file = "#{@data_path}/user.json"
|
|
24
|
+
make_toki_dirs()
|
|
25
|
+
@bundles = load_bundles()
|
|
26
|
+
@config = create_config()
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def backup_db
|
|
30
|
+
FileUtils.copy(@db_file, "#{@backup_path}/toki_data.sqlite3.bak")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def load_bundles
|
|
34
|
+
if File.exist?(@bundles_file)
|
|
35
|
+
JSON.parse(File.read(@bundles_file))
|
|
36
|
+
else
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def save_bundles
|
|
42
|
+
@bundles = get_bundle_ids()
|
|
43
|
+
File.write(@bundles_file, @bundles.to_json)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def get_bundle_from_name(name)
|
|
47
|
+
app_name = name.map {|n| n.downcase}.join(' ')
|
|
48
|
+
candidates = []
|
|
49
|
+
@bundles.each do |bundle_id, bundle_name|
|
|
50
|
+
if bundle_name.downcase =~ /#{app_name}/
|
|
51
|
+
candidates << bundle_id
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
return candidates
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def export(toki, options, title = nil)
|
|
58
|
+
response = JSON.parse(toki.response)
|
|
59
|
+
type = response['meta']['request']['type']
|
|
60
|
+
prefix = response['meta']['request']['processed_at'][0..9]
|
|
61
|
+
path = if title.nil?
|
|
62
|
+
"#{@data_path}/#{prefix}_#{type}"
|
|
63
|
+
else
|
|
64
|
+
"#{@data_path}/#{prefix}_#{type}_#{title.tr_s(' ', '-')}"
|
|
65
|
+
end
|
|
66
|
+
if options[:json]
|
|
67
|
+
file = "#{path}.json"
|
|
68
|
+
File.write(file, toki.response)
|
|
69
|
+
elsif options[:csv]
|
|
70
|
+
file = "#{path}.csv"
|
|
71
|
+
if type == 'apps'
|
|
72
|
+
export_apps_csv(response, file)
|
|
73
|
+
elsif type == 'log'
|
|
74
|
+
export_log_csv(response, file)
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
abort(Status.wtf)
|
|
78
|
+
end
|
|
79
|
+
puts Status.file_saved(file)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def export_apps_csv(response, file)
|
|
85
|
+
CSV.open(file, "wb") do |csv|
|
|
86
|
+
csv << ['Bundle', 'Name', 'Total', 'Hours', 'Minutes', 'Seconds']
|
|
87
|
+
response['data'].each do |line|
|
|
88
|
+
csv << [line['bundle'], line['name'], line['total']['seconds'], line['total']['time']['hours'], line['total']['time']['minutes'], line['total']['time']['seconds']]
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def export_log_csv(response, file)
|
|
94
|
+
CSV.open(file, "wb") do |csv|
|
|
95
|
+
csv << ['Start', 'Duration (seconds)', 'Minutes', 'Seconds', 'Sync ID']
|
|
96
|
+
response['data'].each {|line| csv << [line[1]['start'], line[1]['duration']['seconds'], line[1]['duration']['time']['minutes'], line[1]['duration']['time']['seconds'], line[0]]}
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def make_toki_dirs
|
|
101
|
+
%w{backup data files config log}.each do |dir|
|
|
102
|
+
path = "#{@toki_path}/#{dir}"
|
|
103
|
+
FileUtils.mkdir_p(path) unless Dir.exist?(path)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def create_config
|
|
108
|
+
return YAML.load(File.read(@config_file)) if File.exist?(@config_file)
|
|
109
|
+
settings = {
|
|
110
|
+
'table' => {
|
|
111
|
+
'width' => 90
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
File.write(@config_file, settings.to_yaml)
|
|
115
|
+
return YAML.load(File.read(@config_file))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Scan for names from bundle ids
|
|
119
|
+
def get_bundle_ids
|
|
120
|
+
@names = {}
|
|
121
|
+
get_bundles(get_plists("/Applications/*/Contents/*"))
|
|
122
|
+
get_bundles(get_plists("/Applications/Utilities/*/Contents/*"))
|
|
123
|
+
get_bundles(get_plists("#{@home_path}/Applications/*/Contents/*"))
|
|
124
|
+
specials = {
|
|
125
|
+
'com.evernote.EvernoteHelper' => 'Evernote Helper',
|
|
126
|
+
'com.apple.finder' => 'Finder',
|
|
127
|
+
'com.apple.ReportPanic' => 'Kernel Panic',
|
|
128
|
+
'com.apple.coreservices.uiagent' => 'CoreServices UIAgent',
|
|
129
|
+
'com.apple.installer' => 'Apple Installer',
|
|
130
|
+
'com.apple.frameworks.diskimages.diuiagent' => 'Apple Disk Images Agent',
|
|
131
|
+
'com.mediaatelier.CheatSheet' => 'Cheat Sheet',
|
|
132
|
+
'com.vyprvpn.authorization' => 'VyprVPN',
|
|
133
|
+
'com.apple.WebKit.WebContent' => 'Safari module',
|
|
134
|
+
'com.apple.ProblemReporter' => 'Apple Problem Reporter',
|
|
135
|
+
'org.andymatuschak.sparkle.finish-installation' => 'Sparkle Install',
|
|
136
|
+
'com.noodlesoft.HazelHelper' => 'Hazel Helper',
|
|
137
|
+
'de.appsolute.MAMP' => 'MAMP',
|
|
138
|
+
'com.apple.KeyboardSetupAssistant' => 'Apple Keyboard Setup Assistant',
|
|
139
|
+
'com.apple.NetAuthAgent' => 'Apple Net Auth Agent',
|
|
140
|
+
'com.adobe.acc.AdobeCreativeCloud' => 'Adobe Creative Cloud',
|
|
141
|
+
'com.apple.iphonesimulator' => 'Apple iPhone Simulator',
|
|
142
|
+
'com.macromates.TextMate.preview' => 'TextMate',
|
|
143
|
+
'com.alfredapp.Alfred' => 'Alfred',
|
|
144
|
+
'com.runningwithcrayons.Alfred-Preferences' => 'Alfred Preferences',
|
|
145
|
+
'com.apple.WebKit.PluginProcess' => 'Safari Plugin',
|
|
146
|
+
'com.apple.ScreenSharing' => 'Apple Screen Sharing',
|
|
147
|
+
'org.virtualbox.app.VirtualBoxVM' => 'VirtualBox',
|
|
148
|
+
'2BUA8C4S2C.com.agilebits.onepassword-osx-helper' => '1Password Helper',
|
|
149
|
+
'com.apple.AirPlayUIAgent' => 'AirPlayUIAgent',
|
|
150
|
+
'com.apple.ScreenSaver.Engine' => 'ScreenSaver',
|
|
151
|
+
'com.adobe.PDApp' => 'Adobe Application Manager',
|
|
152
|
+
'com.screentime.flash.builder' => 'Screentime for Flash',
|
|
153
|
+
'com.apple.systemuiserver' => 'SystemUIServer',
|
|
154
|
+
'org.mozilla.plugincontainer' => 'Mozilla Plugin-container',
|
|
155
|
+
'com.adobe.ACCC.Uninstaller' => 'Adobe Creative Cloud Uninstaller',
|
|
156
|
+
'com.smileonmymac.textexpander' => 'TextExpander',
|
|
157
|
+
'org.pqrs.Karabiner-AXNotifier' => 'Karabiner Notifier',
|
|
158
|
+
'com.apple.DiskImageMounter' => 'Apple Disk Image Mounter'
|
|
159
|
+
}
|
|
160
|
+
specials.each { |k, v| @names[k] = v }
|
|
161
|
+
return @names
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def get_plists path
|
|
165
|
+
Dir.glob(path).select {|f| (File.split f).last == 'Info.plist'}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def get_bundles plists
|
|
169
|
+
plists.each do |obj|
|
|
170
|
+
puts Status.analysing(obj)
|
|
171
|
+
begin
|
|
172
|
+
pl = CFPropertyList::List.new(:file => obj)
|
|
173
|
+
rescue CFFormatError, NoMethodError
|
|
174
|
+
puts Status.no_plist
|
|
175
|
+
next
|
|
176
|
+
end
|
|
177
|
+
data = CFPropertyList.native_types(pl.value)
|
|
178
|
+
name = data['CFBundleName'] || data['CFBundleExecutable']
|
|
179
|
+
next if name.nil?
|
|
180
|
+
bundle_id = data['CFBundleIdentifier']
|
|
181
|
+
next if bundle_id.nil? || bundle_id.empty?
|
|
182
|
+
@names[bundle_id] = name
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
end
|
|
187
|
+
end
|
data/lib/TokiCLI/status.rb
CHANGED
|
@@ -1,41 +1,53 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
module TokiCLI
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
3
|
+
|
|
4
|
+
module Status
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
|
|
8
|
+
def canceled
|
|
9
|
+
"\nCanceled.\n\n"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def version(version)
|
|
13
|
+
"\n-- TokiCLI --\n\nVersion:\t#{version}\nUrl:\t\thttp://github.com/ericdke/TokiCLI\n\n"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def wtf
|
|
17
|
+
"\nAn error occurred! Goodbye, and thanks for all the fish.\n\n"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def no_plist
|
|
21
|
+
"Unable to read the file, skipping...\n"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def no_data
|
|
25
|
+
"\nNo data for this request.\n\n"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def analysing(obj)
|
|
29
|
+
"Analyzing #{obj} ...\n"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def scanning
|
|
33
|
+
"\nScanning applications bundles to find their names.\n\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def file_saved(path)
|
|
37
|
+
"\nFile saved in #{path}\n\n"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def next_launch_with_names
|
|
41
|
+
"Starting with next launch, TokiCLI will display apps names. Run `toki scan` again to update the apps list.\n\n"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def please_scan
|
|
45
|
+
"\nPlease run `toki scan` to populate/refresh the applications names database.\n\n"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
extend ClassMethods # This is a way to avoid having to declare self.xxx for each def
|
|
40
51
|
end
|
|
52
|
+
|
|
41
53
|
end
|
data/lib/TokiCLI/version.rb
CHANGED
data/lib/TokiCLI/view.rb
CHANGED
|
@@ -1,132 +1,179 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
|
|
2
3
|
module TokiCLI
|
|
4
|
+
|
|
3
5
|
class View
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if
|
|
9
|
-
if
|
|
10
|
-
table.title = "Your apps monitored by Toki: #{data['data']['date']}"
|
|
11
|
-
else
|
|
12
|
-
table.title = "Your apps monitored by Toki"
|
|
13
|
-
end
|
|
14
|
-
puts apps_3(data, table)
|
|
7
|
+
require 'terminal-table'
|
|
8
|
+
|
|
9
|
+
def initialize settings = {}
|
|
10
|
+
@settings = if settings.empty?
|
|
11
|
+
{"table"=>{"width"=>90}} # force default if no initialization
|
|
15
12
|
else
|
|
16
|
-
|
|
17
|
-
puts total_3(data, table)
|
|
13
|
+
settings
|
|
18
14
|
end
|
|
19
|
-
puts "\n"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def app_table(asked, app_data)
|
|
23
|
-
table = init_table
|
|
24
|
-
table.style = { :width => 100 }
|
|
25
|
-
name = name_of(app_data)
|
|
26
|
-
table.title = "Toki time tracking for #{name}"
|
|
27
|
-
table.headings = ['Start', 'Duration', 'Sync ID']
|
|
28
|
-
lines = create_lines app_data
|
|
29
|
-
total = calc_total lines
|
|
30
|
-
format_lines lines
|
|
31
|
-
|
|
32
|
-
#TODO: change this
|
|
33
|
-
display_limit = Time.now - (3600*24*7) # 7 days
|
|
34
|
-
lines.select! {|obj| Time.parse(obj[0]) > display_limit}
|
|
35
|
-
|
|
36
|
-
sort_lines lines
|
|
37
|
-
|
|
38
|
-
puts create_table(lines, table, total)
|
|
39
|
-
puts "\n"
|
|
40
15
|
end
|
|
41
16
|
|
|
42
|
-
def
|
|
43
|
-
|
|
17
|
+
def version
|
|
18
|
+
table = init_table("TokiCLI for Toki.app")
|
|
19
|
+
table << ['Version', VERSION]
|
|
20
|
+
table << :separator
|
|
21
|
+
table << ['Infos', 'http://github.com/ericdke/TokiCLI']
|
|
22
|
+
puts "\n#{table}\n"
|
|
44
23
|
end
|
|
45
24
|
|
|
46
|
-
def
|
|
47
|
-
|
|
25
|
+
def apps_total(api_response, title = "Your apps monitored by Toki")
|
|
26
|
+
table, apps = make_apps_table(api_response, title)
|
|
27
|
+
puts "\n"
|
|
28
|
+
display, total = populate_apps_table(table, apps)
|
|
29
|
+
display << :separator
|
|
30
|
+
display << insert_total_line(total)
|
|
31
|
+
puts "\e[H\e[2J"
|
|
32
|
+
puts display
|
|
48
33
|
end
|
|
49
34
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
def apps(api_response, title = "Your apps monitored by Toki")
|
|
36
|
+
table, apps = make_apps_table(api_response, title)
|
|
37
|
+
puts "\n"
|
|
38
|
+
display, _ = populate_apps_table(table, apps)
|
|
39
|
+
puts "\e[H\e[2J"
|
|
40
|
+
puts display
|
|
53
41
|
end
|
|
54
42
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
43
|
+
def log(api_response, title = "Your app monitored by Toki")
|
|
44
|
+
display, _ = make_log_table(api_response, title)
|
|
45
|
+
puts "\nRendering the view, please wait.\n\n"
|
|
46
|
+
lines = display.render
|
|
47
|
+
puts "\e[H\e[2J"
|
|
48
|
+
puts lines
|
|
60
49
|
end
|
|
61
50
|
|
|
62
|
-
def
|
|
63
|
-
|
|
51
|
+
def log_total(api_response, title = "Your app monitored by Toki")
|
|
52
|
+
display, total = make_log_table(api_response, title)
|
|
53
|
+
display << :separator
|
|
54
|
+
display << insert_total_line(total)
|
|
55
|
+
puts "\nRendering the view, please wait.\n\n"
|
|
56
|
+
lines = display.render
|
|
57
|
+
puts "\e[H\e[2J"
|
|
58
|
+
puts lines
|
|
64
59
|
end
|
|
65
60
|
|
|
66
|
-
def
|
|
67
|
-
|
|
61
|
+
def log_activity(api_response)
|
|
62
|
+
log = JSON.parse(api_response)['data']
|
|
63
|
+
table = init_table()
|
|
64
|
+
table.headings = ['Bundle', 'Start', 'Duration', 'Sync ID']
|
|
65
|
+
lines = make_log_lines(log)
|
|
66
|
+
display, _ = populate_log_table(lines, table, true)
|
|
67
|
+
puts display
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
private
|
|
71
71
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
table << obj
|
|
77
|
-
table << :separator unless index == length
|
|
78
|
-
index += 1
|
|
72
|
+
def init_table(title = 'TokiCLI')
|
|
73
|
+
Terminal::Table.new do |t|
|
|
74
|
+
t.style = { :width => @settings['table']['width'] }
|
|
75
|
+
t.title = title
|
|
79
76
|
end
|
|
80
|
-
table << :separator
|
|
81
|
-
table << [{ :value => "Total: #{humanized_date(total)}", :colspan => 3, :alignment => :center }]
|
|
82
|
-
table
|
|
83
77
|
end
|
|
84
78
|
|
|
85
|
-
def
|
|
86
|
-
|
|
79
|
+
def make_log_table(api_response, title)
|
|
80
|
+
log = JSON.parse(api_response)['data']
|
|
81
|
+
table = init_table(title)
|
|
82
|
+
table.headings = ['Start', 'Duration', 'Sync ID']
|
|
83
|
+
lines = make_log_lines(log)
|
|
84
|
+
return populate_log_table(lines, table)
|
|
87
85
|
end
|
|
88
86
|
|
|
89
|
-
def
|
|
90
|
-
|
|
87
|
+
def make_apps_table(api_response, title)
|
|
88
|
+
apps = JSON.parse(api_response)['data']
|
|
89
|
+
table = init_table(title)
|
|
90
|
+
table.headings = ['Bundle ID', 'Name', 'Total']
|
|
91
|
+
return table, apps
|
|
91
92
|
end
|
|
92
93
|
|
|
93
|
-
def
|
|
94
|
-
total
|
|
95
|
-
lines.each {|v| total += v[3]}
|
|
96
|
-
total
|
|
94
|
+
def insert_total_line(total, text = 'Total')
|
|
95
|
+
[{ :value => "#{text}: #{readable_time(sec_to_time(total))}", :colspan => 3, :alignment => :center }]
|
|
97
96
|
end
|
|
98
97
|
|
|
99
|
-
def
|
|
100
|
-
|
|
101
|
-
app_data['data']['log'].each {|k,v| lines << [v['start'], readable_time(v['duration']['time']), k, v['duration']['seconds']]}
|
|
102
|
-
lines
|
|
98
|
+
def make_log_lines(log)
|
|
99
|
+
log.map { |k, v| [v['start'], readable_time_log(v['duration']['time']), k, v['duration']['seconds'], v['bundle']] }
|
|
103
100
|
end
|
|
104
101
|
|
|
105
|
-
def
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
def populate_log_table(lines, table, with_bundle = false)
|
|
103
|
+
day = lines[0][0][0..9]
|
|
104
|
+
if with_bundle == false
|
|
105
|
+
table << [{ :value => "#{day}", :colspan => 3, :alignment => :center }]
|
|
108
106
|
else
|
|
109
|
-
|
|
107
|
+
table << [{ :value => "#{day}", :colspan => 4, :alignment => :center }]
|
|
108
|
+
table << :separator
|
|
110
109
|
end
|
|
110
|
+
total = 0
|
|
111
|
+
lines.each.with_index(1) do |line, index|
|
|
112
|
+
new_day = line[0][0..9]
|
|
113
|
+
unless day == new_day
|
|
114
|
+
table << :separator
|
|
115
|
+
if with_bundle == false
|
|
116
|
+
table << [{ :value => "#{new_day}", :colspan => 3, :alignment => :center }]
|
|
117
|
+
else
|
|
118
|
+
table << [{ :value => "#{new_day}", :colspan => 4, :alignment => :center }]
|
|
119
|
+
end
|
|
120
|
+
table << :separator
|
|
121
|
+
end
|
|
122
|
+
if with_bundle == false
|
|
123
|
+
table << [line[0][10..18], line[1], line[2]]
|
|
124
|
+
else
|
|
125
|
+
table << [line[4], line[0][10..18], line[1], line[2]]
|
|
126
|
+
# table << :separator unless index == lines.size
|
|
127
|
+
end
|
|
128
|
+
day = new_day
|
|
129
|
+
total += line[3]
|
|
130
|
+
end
|
|
131
|
+
return table, total
|
|
111
132
|
end
|
|
112
133
|
|
|
113
|
-
def
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
def populate_apps_table(table, apps)
|
|
135
|
+
total = 0
|
|
136
|
+
apps.each do |app|
|
|
137
|
+
total += app['total']['seconds']
|
|
138
|
+
if app['name']
|
|
139
|
+
table << app_row_with_name(app)
|
|
140
|
+
else
|
|
141
|
+
table << app_row(app)
|
|
142
|
+
end
|
|
116
143
|
end
|
|
144
|
+
return table, total
|
|
117
145
|
end
|
|
118
146
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
147
|
+
def app_row_with_name(obj)
|
|
148
|
+
max = @settings['table']['width'] / 3
|
|
149
|
+
[width(max + 5, obj['bundle']), width(max - 5, obj['name']), readable_time(obj['total']['time'])]
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def app_row(obj)
|
|
153
|
+
max = @settings['table']['width'] / 2
|
|
154
|
+
[width(max, obj['bundle']), '(unknown)', readable_time(obj['total']['time'])]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def width(width, text)
|
|
158
|
+
boundary = width - 3
|
|
159
|
+
text.length >= width ? "#{text[0..boundary]}..." : text
|
|
124
160
|
end
|
|
125
161
|
|
|
126
|
-
def
|
|
127
|
-
|
|
128
|
-
|
|
162
|
+
def readable_time(obj)
|
|
163
|
+
"#{obj['hours']}h #{'%.2d' % obj['minutes']}m #{'%.2d' % obj['seconds']}s"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def readable_time_log(obj)
|
|
167
|
+
"#{'%.2d' % obj['minutes']}m #{'%.2d' % obj['seconds']}s"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def sec_to_time(secs)
|
|
171
|
+
hours = secs / 3600
|
|
172
|
+
minutes = (secs / 60 - hours * 60)
|
|
173
|
+
seconds = (secs - (minutes * 60 + hours * 3600))
|
|
174
|
+
{'hours' => hours, 'minutes' => minutes, 'seconds' => seconds}
|
|
129
175
|
end
|
|
130
176
|
|
|
131
177
|
end
|
|
178
|
+
|
|
132
179
|
end
|