TokiCLI 0.0.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +166 -20
- data/TokiCLI.gemspec +2 -2
- data/lib/API/dbapi.rb +451 -0
- data/lib/API/helpers.rb +151 -0
- data/lib/TokiCLI/app.rb +225 -106
- data/lib/TokiCLI/authorize.rb +6 -4
- data/lib/TokiCLI/export.rb +81 -0
- data/lib/TokiCLI/import.rb +122 -0
- data/lib/TokiCLI/scan.rb +19 -0
- data/lib/TokiCLI/version.rb +1 -1
- data/lib/TokiCLI/view.rb +100 -43
- metadata +10 -7
- data/lib/TokiCLI/module.rb +0 -224
data/lib/TokiCLI/authorize.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'rest_client'
|
3
|
-
module
|
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::
|
7
|
-
@home =
|
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
|
data/lib/TokiCLI/scan.rb
ADDED
@@ -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
|
data/lib/TokiCLI/version.rb
CHANGED
data/lib/TokiCLI/view.rb
CHANGED
@@ -1,70 +1,127 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module TokiCLI
|
3
3
|
class View
|
4
|
-
|
4
|
+
|
5
|
+
def total_table(data)
|
5
6
|
table = init_table
|
6
|
-
|
7
|
-
|
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
|
-
|
17
|
+
|
18
|
+
def app_table(asked, app_data)
|
11
19
|
table = init_table
|
12
|
-
table.
|
13
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
23
|
-
|
24
|
-
|
25
|
-
puts table2_index(list, table)
|
26
|
-
puts "\n"
|
40
|
+
|
41
|
+
def force_name name
|
42
|
+
name.nil? ? ' ' : name
|
27
43
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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: #{
|
46
|
-
|
47
|
-
puts "\n"
|
76
|
+
table << [{ :value => "Total: #{humanized_date(total)}", :colspan => 3, :alignment => :center }]
|
77
|
+
table
|
48
78
|
end
|
49
79
|
|
50
|
-
|
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 =>
|
110
|
+
t.style = { :width => 100 }
|
55
111
|
end
|
56
112
|
end
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
|
64
|
-
|
65
|
-
|
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.
|
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-
|
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
|
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
|
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/
|
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:
|
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:
|