TokiCLI 0.0.9 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,12 @@
1
1
  require 'json'
2
2
  require 'rest_client'
3
- module ADNAuthorize
3
+ module TokiCLI
4
+ CLIENT_ID = 'm6AccJFM56ENCn58Vde9cSg3uSpbvAAs'
5
+ CALLBACK_URL = 'http://aya.io/toki_cli/auth.html'
4
6
  class Authorize
5
- def initialize
6
- @auth_url = "https://account.app.net/oauth/authenticate?client_id=#{TokiCLI::Toki::CLIENT_ID}&response_type=token&redirect_uri=#{TokiCLI::Toki::CALLBACK_URL}&scope=basic,messages&include_marker=1"
7
- @home = Dir.home + "/.TokiCLI"
7
+ def initialize home
8
+ @auth_url = "https://account.app.net/oauth/authenticate?client_id=#{TokiCLI::CLIENT_ID}&response_type=token&redirect_uri=#{TokiCLI::CALLBACK_URL}&scope=basic,messages&include_marker=1"
9
+ @home = home
8
10
  end
9
11
 
10
12
  def authorize
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class Export
4
+
5
+ def initialize toki, view
6
+ @toki = toki
7
+ @view = view
8
+ end
9
+
10
+ def apps_total options, apps, type
11
+ arr = []
12
+ apps['data']['apps'].each do |obj|
13
+ arr << {
14
+ bundle: obj['bundle'],
15
+ name: obj['name'],
16
+ total: {
17
+ seconds: obj['total']['seconds'],
18
+ time: @view.readable_time(obj['total']['time'])
19
+ }
20
+ }
21
+ end
22
+ save options, arr, "toki-#{type}__#{Time.now.to_s[0..9]}"
23
+ end
24
+
25
+ def log options, log, type
26
+ arr = []
27
+ temp = log['data']['log'].map do |obj|
28
+ [obj[1]['start'], obj[1]['duration']['seconds'], @view.readable_time(obj[1]['duration']['time']), obj[0]]
29
+ end
30
+ temp.sort_by! {|obj| obj[0]}
31
+ temp.each do |obj|
32
+ arr << {
33
+ start: obj[0],
34
+ duration: {
35
+ seconds: obj[1],
36
+ time: obj[2]
37
+ },
38
+ sync_id: obj[3]
39
+ }
40
+ end
41
+ save_log options, arr, "toki-#{type}__#{Time.now.to_s[0..9]}"
42
+ end
43
+
44
+ private
45
+
46
+ def save options, data, filename
47
+ case
48
+ when options[:json]
49
+ name = @toki.helpers.toki_path + "/#{filename}.json"
50
+ File.write(name, data.to_json)
51
+ when options[:csv]
52
+ name = @toki.helpers.toki_path + "/#{filename}.csv"
53
+ CSV.open(name, "wb") do |csv|
54
+ csv << ['Bundle Identifier', 'Name', 'Total seconds', 'Total time']
55
+ data.each {|line| csv << [line[:bundle], line[:name], line[:total][:seconds], line[:total][:time]]}
56
+ end
57
+ else
58
+ exit
59
+ end
60
+ puts "File converted and exported to #{name}\n\n"
61
+ end
62
+
63
+ def save_log options, data, filename
64
+ case
65
+ when options[:json]
66
+ name = @toki.helpers.toki_path + "/#{filename}.json"
67
+ File.write(name, data.to_json)
68
+ when options[:csv]
69
+ name = @toki.helpers.toki_path + "/#{filename}.csv"
70
+ CSV.open(name, "wb") do |csv|
71
+ csv << ['Start', 'Duration (seconds)', 'Duration (time)', 'Sync ID']
72
+ data.each {|line| csv << [line[:start], line[:duration][:seconds], line[:duration][:time], line[:sync_id]]}
73
+ end
74
+ else
75
+ exit
76
+ end
77
+ puts "File converted and exported to #{name}\n\n"
78
+ end
79
+
80
+ end
81
+ end
@@ -0,0 +1,122 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class Import
4
+
5
+ def initialize toki, view
6
+ @toki_api = toki
7
+ @view = view
8
+ end
9
+
10
+ def restore token, channel_id
11
+ please_quit
12
+ backup_db
13
+ adn_data = get_adn_data token, channel_id
14
+ db = create_db
15
+ create_table db
16
+ lines = decode adn_data
17
+ populate db, lines
18
+ replace
19
+ end
20
+
21
+
22
+ def replace
23
+ if File.exist? @toki_api.helpers.db_path
24
+ puts "\nAre you sure you want to replace the current Toki.app database?\n\nHit SPACE to continue (any other key to cancel).\n\n"
25
+ abort("Canceled.\n\n") unless STDIN.getch == ' '
26
+ FileUtils.mv @toki_api.helpers.db_path, "#{Dir.home}/.Trash/"
27
+ end
28
+ FileUtils.mv @new_db_path, @toki_api.helpers.db_path
29
+ puts "\n\nDone. You may relaunch Toki.app now.\n\n"
30
+ end
31
+
32
+ def populate db, lines
33
+ puts "Populating new database with App.net data...\n\n"
34
+ before = Time.now
35
+ idx = 0
36
+ db.transaction do |db_in_transaction|
37
+ lines.each do |obj|
38
+ insert_data = {}
39
+ insert_data[':id'] = obj[:id]
40
+ insert_data[':bundleIdentifier'] = obj[:bundleIdentifier]
41
+ insert_data[':activeTo'] = obj[:activeTo]
42
+ insert_data[':activeFrom'] = obj[:activeFrom]
43
+ insert_data[':totalSeconds'] = obj[:totalSeconds]
44
+ insert_data[':UUID'] = obj[:uuid]
45
+ insert_data[':synced'] = 1
46
+ insert_data[':availableToSync'] = 1
47
+
48
+ db_in_transaction.prepare("INSERT INTO plain(id, bundleIdentifier, activeTo, activeFrom, totalSeconds, UUID, synced, availableToSync) VALUES(:id, :bundleIdentifier, :activeTo, :activeFrom, :totalSeconds, :UUID, :synced, :availableToSync);") do |insert|
49
+ insert.execute insert_data
50
+ end
51
+
52
+ idx += 1
53
+ print "Processed #{idx} of #{lines.size}\r"
54
+ $stdout.flush
55
+ end
56
+ end
57
+ puts "\n\n\nFinished inserting #{idx} lines in #{Time.now - before}.\n\n"
58
+ end
59
+
60
+ def decode adn_data
61
+ puts "Decoding App.net data...\n\n"
62
+ lines = []
63
+ adn_data.each do |obj|
64
+ adn = obj['annotations'][0]
65
+ lines <<
66
+ {
67
+ type: adn['type'],
68
+ table: adn['value']['c'],
69
+ uuid: adn['value']['d']['UUID'],
70
+ bundleIdentifier: adn['value']['d']['bundleIdentifier'],
71
+ activeTo: adn['value']['d']['activeTo'],
72
+ activeFrom: adn['value']['d']['activeFrom'],
73
+ totalSeconds: adn['value']['d']['totalSeconds'],
74
+ id: adn['value']['d']['id']
75
+ }
76
+ end
77
+ lines
78
+ end
79
+
80
+ def create_table db
81
+ puts "Creating table...\n\n"
82
+ db.execute_batch <<-SQL
83
+ CREATE TABLE KKAppActivity (
84
+ id INTEGER,
85
+ bundleIdentifier VARCHAR(256),
86
+ activeTo INTEGER,
87
+ activeFrom INTEGER,
88
+ totalSeconds INTEGER,
89
+ UUID VARCHAR(256),
90
+ synced INTEGER,
91
+ availableToSync INTEGER
92
+ );
93
+ SQL
94
+ db.reload_schema!
95
+ end
96
+
97
+ def create_db
98
+ puts "\n\nCreating a new SQLite3 database...\n\n"
99
+ @new_db_path = "#{@toki_api.helpers.toki_path}/db_from_adn.sqlite3"
100
+ Amalgalite::Database.new @new_db_path
101
+ end
102
+
103
+ def get_adn_data token, channel_id
104
+ puts "Connecting to App.net...\n\n"
105
+ channels = ADNChannels::GetMessages.new(token)
106
+ channels.get_messages(channel_id)
107
+ end
108
+
109
+ def backup_db
110
+ if File.exist? @toki_api.helpers.db_path
111
+ FileUtils.copy(@toki_api.helpers.db_path, "#{@toki_api.helpers.toki_path}/toki_data.sqlite3.bak")
112
+ puts "Current database has been backed up in #{@toki_api.helpers.toki_path}\n\n"
113
+ end
114
+ end
115
+
116
+ def please_quit
117
+ puts "\n\nYou need to quit Toki.app before restoring data from App.net.\n\nPlease quit Toki.app then hit SPACE to continue (any other key to cancel).\n\n"
118
+ abort("Canceled.\n\n") unless STDIN.getch == ' '
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module TokiCLI
3
+ class Scan
4
+
5
+ def initialize toki
6
+ @toki_api = toki
7
+ end
8
+
9
+ def scan params
10
+ bundles = @toki_api.helpers.scan params
11
+ if bundles.empty? || bundles.nil?
12
+ puts "\nError.\n\n"
13
+ else
14
+ puts "\nDone.\n\n"
15
+ end
16
+ end
17
+
18
+ end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module TokiCLI
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,70 +1,127 @@
1
1
  # encoding: utf-8
2
2
  module TokiCLI
3
3
  class View
4
- def total_table(list)
4
+
5
+ def total_table(data)
5
6
  table = init_table
6
- table.title = "Your apps monitored by Toki"
7
- puts table_2(list, table)
7
+ clear
8
+ if data['data']['apps']
9
+ table.title = "Your apps monitored by Toki"
10
+ puts apps_3(data, table)
11
+ else
12
+ table.title = "Your app monitored by Toki"
13
+ puts total_3(data, table)
14
+ end
8
15
  puts "\n"
9
16
  end
10
- def day_table(day, list)
17
+
18
+ def app_table(asked, app_data)
11
19
  table = init_table
12
- table.title = "Your apps monitored by Toki on #{day}"
13
- puts table_2(list, table)
20
+ table.style = { :width => 100 }
21
+ name = name_of app_data
22
+ table.title = "Toki time tracking for #{name}"
23
+ table.headings = ['Start', 'Duration', 'Sync ID']
24
+ lines = create_lines app_data
25
+ total = calc_total lines
26
+ format_lines lines
27
+
28
+ #TODO: change this
29
+ display_limit = Time.now - (3600*24*7) # 7 days
30
+ lines.select! {|obj| Time.parse(obj[0]) > display_limit}
31
+
32
+ sort_lines lines
33
+ puts create_table(lines, table)
14
34
  puts "\n"
15
35
  end
16
- def range_table(day1, day2, list)
17
- table = init_table
18
- table.title = "Your apps monitored by Toki between #{day1} and #{day2}"
19
- puts table_2(list, table)
20
- puts "\n"
36
+
37
+ def max_30 text
38
+ text.length >= 30 ? "#{text[0..27]}..." : text
21
39
  end
22
- def hits_table(list)
23
- table = init_table
24
- table.title = "Your top apps monitored by Toki"
25
- puts table2_index(list, table)
26
- puts "\n"
40
+
41
+ def force_name name
42
+ name.nil? ? ' ' : name
27
43
  end
28
- def app_table(asked, app_data)
29
- table = init_table
30
- table.style = { :width => 100 }
31
- table.title = "Toki time tracking for '#{asked}' => #{app_data.first[1][:name]}"
32
- table.headings = ['From', 'To', 'Duration', 'Sync id']
33
- @index = 0
34
- @length = app_data.length - 1
35
- @total = app_data.select {|k,v| k == 'total'}
36
- app_data.delete('total')
37
- data = app_data.to_a
38
- data.sort_by! {|k,v| v[:raw_from] }
39
- data.each do |id, log|
40
- @index += 1
41
- table << ["#{log[:from]}", "#{log[:to]}", "#{log[:duration]}", "#{id}"]
42
- table << :separator unless @index == @length
44
+
45
+ def humanized_date epoch
46
+ human = seconds_to_time epoch
47
+ "#{human[:hours]}h #{human[:minutes]}m #{human[:seconds]}s"
48
+ end
49
+
50
+ def seconds_to_time epoch
51
+ hours = epoch / 3600
52
+ minutes = (epoch / 60 - hours * 60)
53
+ seconds = (epoch - (minutes * 60 + hours * 3600))
54
+ {hours: hours, minutes: minutes, seconds: seconds}
55
+ end
56
+
57
+ def readable_time time
58
+ "#{'%.2d' % time['hours']}h #{'%.2d' % time['minutes']}m #{'%.2d' % time['seconds']}s"
59
+ end
60
+
61
+ def clear
62
+ puts "\e[H\e[2J"
63
+ end
64
+
65
+ private
66
+
67
+ def create_table lines, table
68
+ index = 0
69
+ length = lines.length - 1
70
+ lines.each do |obj|
71
+ table << obj
72
+ table << :separator unless index == length
73
+ index += 1
43
74
  end
44
75
  table << :separator
45
- table << [{ :value => "Total: #{@total['total']}", :colspan => 4, :alignment => :center }]
46
- puts table
47
- puts "\n"
76
+ table << [{ :value => "Total: #{humanized_date(total)}", :colspan => 3, :alignment => :center }]
77
+ table
48
78
  end
49
79
 
50
- private
80
+ def sort_lines lines
81
+ lines.sort_by! {|obj| obj[0]}
82
+ end
83
+
84
+ def format_lines lines
85
+ lines.map! {|obj| [obj[0], obj[1], obj[2]]}
86
+ end
87
+
88
+ def calc_total lines
89
+ total = 0
90
+ lines.each {|v| total += v[3]}
91
+ total
92
+ end
93
+
94
+ def create_lines app_data
95
+ lines = []
96
+ app_data['data']['log'].each {|k,v| lines << [v['start'], readable_time(v['duration']['time']), k, v['duration']['seconds']]}
97
+ lines
98
+ end
99
+
100
+ def name_of app_data
101
+ if app_data['data']['name']
102
+ app_data['data']['name']
103
+ else
104
+ app_data['data']['bundle']
105
+ end
106
+ end
51
107
 
52
108
  def init_table
53
109
  Terminal::Table.new do |t|
54
- t.style = { :width => 75 }
110
+ t.style = { :width => 100 }
55
111
  end
56
112
  end
57
- def table_2(list, table)
58
- list.each do |k,v|
59
- table << ["#{k}", "#{v}"]
113
+
114
+ def apps_3 resp, table
115
+ resp['data']['apps'].each do |obj|
116
+ table << [max_30(obj['bundle']), max_30(force_name(obj['name'])), readable_time(obj['total']['time'])]
60
117
  end
61
118
  table
62
119
  end
63
- def table2_index(list, table)
64
- list.each.with_index(1) do |obj,index|
65
- table << ["#{index}", "#{obj[0]}", "#{obj[1]}"]
66
- end
120
+
121
+ def total_3 resp, table
122
+ table << [max_30(resp['data']['bundle']), max_30(force_name(resp['data']['name'])), readable_time(resp['data']['total']['time'])]
67
123
  table
68
124
  end
125
+
69
126
  end
70
127
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: TokiCLI
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Dejonckheere
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-29 00:00:00.000000000 Z
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 2.2.8
75
+ version: '2.2'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 2.2.8
82
+ version: '2.2'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: bundler
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -126,12 +126,16 @@ files:
126
126
  - Rakefile
127
127
  - TokiCLI.gemspec
128
128
  - bin/toki
129
+ - lib/API/dbapi.rb
130
+ - lib/API/helpers.rb
129
131
  - lib/TokiCLI.rb
130
132
  - lib/TokiCLI/app.rb
131
133
  - lib/TokiCLI/authorize.rb
134
+ - lib/TokiCLI/export.rb
132
135
  - lib/TokiCLI/get_channels.rb
133
136
  - lib/TokiCLI/get_messages.rb
134
- - lib/TokiCLI/module.rb
137
+ - lib/TokiCLI/import.rb
138
+ - lib/TokiCLI/scan.rb
135
139
  - lib/TokiCLI/search_messages.rb
136
140
  - lib/TokiCLI/status.rb
137
141
  - lib/TokiCLI/version.rb
@@ -148,7 +152,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
152
  requirements:
149
153
  - - ">="
150
154
  - !ruby/object:Gem::Version
151
- version: 1.9.3
155
+ version: 2.0.0
152
156
  required_rubygems_version: !ruby/object:Gem::Requirement
153
157
  requirements:
154
158
  - - ">="
@@ -161,4 +165,3 @@ signing_key:
161
165
  specification_version: 4
162
166
  summary: Toki.app command-line client
163
167
  test_files: []
164
- has_rdoc: