TokiCLI 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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