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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +9 -17
  4. data/.rspec +3 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +4 -0
  7. data/README.md +99 -163
  8. data/Rakefile +5 -0
  9. data/TokiCLI.gemspec +5 -4
  10. data/bin/toki +2 -2
  11. data/lib/API/helpers.rb +27 -170
  12. data/lib/API/toki_api.rb +227 -0
  13. data/lib/API/toki_db.rb +67 -0
  14. data/lib/TokiCLI.rb +270 -5
  15. data/lib/TokiCLI/adnimport.rb +202 -0
  16. data/lib/TokiCLI/fileops.rb +187 -0
  17. data/lib/TokiCLI/status.rb +49 -37
  18. data/lib/TokiCLI/version.rb +1 -1
  19. data/lib/TokiCLI/view.rb +133 -86
  20. data/lib/TokiServer/.bowerrc +3 -0
  21. data/lib/TokiServer/.gitignore +2 -0
  22. data/lib/TokiServer/Gemfile +1 -0
  23. data/lib/TokiServer/bower.json +0 -0
  24. data/lib/TokiServer/bower_components/fastclick/.bower.json +4 -4
  25. data/lib/TokiServer/bower_components/fastclick/bower.json +1 -1
  26. data/lib/TokiServer/bower_components/fastclick/lib/fastclick.js +33 -2
  27. data/lib/TokiServer/bower_components/foundation/.bower.json +5 -4
  28. data/lib/TokiServer/bower_components/foundation/bower.json +2 -1
  29. data/lib/TokiServer/bower_components/foundation/css/foundation.css +2173 -922
  30. data/lib/TokiServer/bower_components/foundation/css/foundation.css.map +7 -0
  31. data/lib/TokiServer/bower_components/foundation/css/normalize.css +53 -16
  32. data/lib/TokiServer/bower_components/foundation/css/normalize.css.map +7 -0
  33. data/lib/TokiServer/bower_components/foundation/js/foundation.js +782 -542
  34. data/lib/TokiServer/bower_components/foundation/js/foundation.min.js +4 -3
  35. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.abide.js +45 -31
  36. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.accordion.js +12 -6
  37. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.alert.js +5 -5
  38. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.clearing.js +34 -10
  39. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.dropdown.js +83 -29
  40. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.equalizer.js +3 -3
  41. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.interchange.js +25 -12
  42. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.joyride.js +112 -40
  43. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.js +19 -5
  44. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.magellan.js +22 -11
  45. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.offcanvas.js +52 -8
  46. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.orbit.js +133 -271
  47. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.reveal.js +27 -20
  48. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.slider.js +73 -33
  49. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.tab.js +88 -31
  50. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.tooltip.js +7 -5
  51. data/lib/TokiServer/bower_components/foundation/js/foundation/foundation.topbar.js +44 -24
  52. data/lib/TokiServer/bower_components/foundation/js/vendor/fastclick.js +2 -2
  53. data/lib/TokiServer/bower_components/foundation/js/vendor/modernizr.js +2 -2
  54. data/lib/TokiServer/bower_components/foundation/scss/foundation.scss +38 -38
  55. data/lib/TokiServer/bower_components/foundation/scss/foundation/_functions.scss +3 -3
  56. data/lib/TokiServer/bower_components/foundation/scss/foundation/_settings.scss +417 -271
  57. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_accordion.scss +110 -6
  58. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_alert-boxes.scss +2 -2
  59. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_block-grid.scss +2 -2
  60. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_breadcrumbs.scss +8 -3
  61. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_button-groups.scss +99 -9
  62. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_buttons.scss +66 -28
  63. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_clearing.scss +5 -5
  64. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_dropdown-buttons.scss +4 -4
  65. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_dropdown.scss +48 -35
  66. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_forms.scss +104 -32
  67. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_global.scss +48 -30
  68. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_grid.scss +19 -4
  69. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_icon-bar.scss +293 -0
  70. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_joyride.scss +11 -9
  71. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_keystrokes.scss +4 -4
  72. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_labels.scss +4 -2
  73. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_magellan.scss +1 -1
  74. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_offcanvas.scss +193 -35
  75. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_orbit.scss +92 -147
  76. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_pagination.scss +22 -10
  77. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_panels.scss +10 -7
  78. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_pricing-tables.scss +11 -11
  79. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_progress-bars.scss +2 -2
  80. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_range-slider.scss +29 -9
  81. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_reveal.scss +60 -56
  82. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_side-nav.scss +3 -2
  83. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_split-buttons.scss +2 -2
  84. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_sub-nav.scss +2 -2
  85. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_switches.scss +226 -0
  86. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tables.scss +13 -7
  87. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tabs.scss +22 -8
  88. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_thumbs.scss +2 -4
  89. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_toolbar.scss +70 -0
  90. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_tooltips.scss +9 -7
  91. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_top-bar.scss +77 -44
  92. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_type.scss +21 -9
  93. data/lib/TokiServer/bower_components/modernizr/.bower.json +4 -4
  94. data/lib/TokiServer/bower_components/modernizr/feature-detects/workers-blobworkers.js +2 -2
  95. data/lib/TokiServer/bower_components/modernizr/grunt.js +1 -1
  96. data/lib/TokiServer/bower_components/modernizr/modernizr.js +2 -2
  97. data/lib/TokiServer/config.rb +0 -0
  98. data/lib/TokiServer/humans.txt +0 -0
  99. data/lib/TokiServer/{itunesicon.rb → itunesicons.rb} +25 -23
  100. data/lib/TokiServer/js/app.js +0 -0
  101. data/lib/TokiServer/public/stylesheets/app.css +2636 -1610
  102. data/lib/TokiServer/robots.txt +0 -0
  103. data/lib/TokiServer/scss/_settings.scss +0 -0
  104. data/lib/TokiServer/scss/app.scss +7 -1
  105. data/lib/TokiServer/tokiserver.rb +239 -244
  106. data/lib/TokiServer/views/activity.erb +42 -0
  107. data/lib/TokiServer/views/apps_total.erb +8 -2
  108. data/lib/TokiServer/views/error.erb +13 -4
  109. data/lib/TokiServer/views/index.erb +36 -27
  110. data/lib/TokiServer/views/logs_total.erb +34 -0
  111. data/spec/TokiCLI_spec.rb +354 -0
  112. data/spec/mock/mock.sqlite3 +0 -0
  113. data/spec/spec_helper.rb +26 -0
  114. metadata +78 -23
  115. data/lib/API/dbapi.rb +0 -488
  116. data/lib/TokiCLI/app.rb +0 -389
  117. data/lib/TokiCLI/authorize.rb +0 -77
  118. data/lib/TokiCLI/export.rb +0 -81
  119. data/lib/TokiCLI/get_channels.rb +0 -22
  120. data/lib/TokiCLI/get_messages.rb +0 -32
  121. data/lib/TokiCLI/import.rb +0 -122
  122. data/lib/TokiCLI/scan.rb +0 -19
  123. data/lib/TokiCLI/search_messages.rb +0 -23
  124. data/lib/TokiServer/README.md +0 -37
  125. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_reveal-new.scss +0 -0
  126. data/lib/TokiServer/bower_components/foundation/scss/foundation/components/_switch.scss +0 -294
  127. data/lib/TokiServer/views/name_log.erb +0 -50
  128. data/lib/TokiServer/views/name_split.erb +0 -37
  129. 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
@@ -1,41 +1,53 @@
1
1
  # encoding: utf-8
2
2
  module TokiCLI
3
- class Status
4
- def self.get_all
5
- "Toki_CLI is crawling your Toki App.net channel to get your data.\nIt could take a while, please be patient.\n\n"
6
- end
7
- def self.canceled
8
- "\n\nCanceled.\n\n"
9
- end
10
- def self.error(e)
11
- "\n\nStopped. Error: \n#{e}\n\n"
12
- end
13
- def self.no_channel
14
- "\n\nCan't find your Toki App.net channel, sorry.\n\n"
15
- end
16
- def self.paste_token
17
- "\nPlease paste your App.net token:\n\n"
18
- end
19
- def self.no_token
20
- "\nOops, I didn't get the token. Please try again.\n"
21
- end
22
- def self.no_db
23
- "\nOops, I can't find the Toki database. Please install Toki.app by http://app.net/keita.\n\n"
24
- end
25
- def self.done
26
- "\nDone.\n"
27
- end
28
- def self.specify_name
29
- "\nPlease specify the name of the app (or part of it).\n\n"
30
- end
31
- def self.specify_day
32
- "\nPlease specify the date with this format: 2014-04-19\n\n"
33
- end
34
- def self.specify_range
35
- "\nPlease specify the range with this format: 2014-04-17 2014-04-19\n\n"
36
- end
37
- def self.run_auth
38
- "\nPlease run `toki auth` first.\n\n"
39
- end
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
@@ -1,3 +1,3 @@
1
1
  module TokiCLI
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,132 +1,179 @@
1
1
  # encoding: utf-8
2
+
2
3
  module TokiCLI
4
+
3
5
  class View
4
6
 
5
- def total_table(data)
6
- table = init_table
7
- clear
8
- if data['data']['apps']
9
- if data['data']['date']
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
- table.title = "Your app monitored by Toki"
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 max_30 text
43
- text.length >= 30 ? "#{text[0..27]}..." : text
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 force_name name
47
- name.nil? ? ' ' : name
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 humanized_date epoch
51
- human = seconds_to_time epoch
52
- "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
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 seconds_to_time epoch
56
- hours = epoch / 3600
57
- minutes = (epoch / 60 - hours * 60)
58
- seconds = (epoch - (minutes * 60 + hours * 3600))
59
- {hours: hours, minutes: minutes, seconds: seconds}
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 readable_time time
63
- "#{'%.2d' % time['hours']}h #{'%.2d' % time['minutes']}m #{'%.2d' % time['seconds']}s"
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 clear
67
- puts "\e[H\e[2J"
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 create_table lines, table, total = 0
73
- index = 0
74
- length = lines.length - 1
75
- lines.each do |obj|
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 sort_lines lines
86
- lines.sort_by! {|obj| obj[0]}
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 format_lines lines
90
- lines.map! {|obj| [obj[0], obj[1], obj[2]]}
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 calc_total lines
94
- total = 0
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 create_lines app_data
100
- lines = []
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 name_of app_data
106
- if app_data['data']['name']
107
- app_data['data']['name']
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
- app_data['data']['bundle']
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 init_table
114
- Terminal::Table.new do |t|
115
- t.style = { :width => 100 }
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 apps_3 resp, table
120
- resp['data']['apps'].each do |obj|
121
- table << [max_30(obj['bundle']), max_30(force_name(obj['name'])), readable_time(obj['total']['time'])]
122
- end
123
- table
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 total_3 resp, table
127
- table << [max_30(resp['data']['bundle']), max_30(force_name(resp['data']['name'])), readable_time(resp['data']['total']['time'])]
128
- table
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