logtime 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.md +21 -0
- data/VERSION +1 -0
- data/bin/logtime +3 -0
- data/certs/paperback.pem +21 -0
- data/config/application.rb +6 -0
- data/config/database.rb +4 -0
- data/config/initialisation.rb +8 -0
- data/config/schema.rb +41 -0
- data/lib/app/commands/add.rb +41 -0
- data/lib/app/commands/list.rb +56 -0
- data/lib/app/commands/start.rb +28 -0
- data/lib/app/commands/stop.rb +29 -0
- data/lib/app/helpers/estimate.rb +13 -0
- data/lib/app/helpers/statistics.rb +45 -0
- data/lib/app/helpers/time_to_words.rb +33 -0
- data/lib/app/logtime.rb +67 -0
- data/lib/app/models/log.rb +42 -0
- data/lib/app/models/series.rb +35 -0
- data/lib/app/models/tag.rb +9 -0
- data/lib/app/subcommands/keyword.rb +214 -0
- data/lib/app/subcommands/series.rb +58 -0
- data/lib/app/subcommands/tags.rb +79 -0
- data/lib/commands/add.rb +41 -0
- data/lib/commands/list.rb +67 -0
- data/lib/commands/start.rb +28 -0
- data/lib/commands/stop.rb +29 -0
- data/lib/commands/version.rb +12 -0
- data/lib/helpers/estimate.rb +13 -0
- data/lib/helpers/statistics.rb +48 -0
- data/lib/helpers/time_difference.rb +20 -0
- data/lib/helpers/time_display.rb +5 -0
- data/lib/helpers/time_to_words.rb +33 -0
- data/lib/logtime.rb +68 -0
- data/lib/models/log.rb +47 -0
- data/lib/models/series.rb +35 -0
- data/lib/models/tag.rb +9 -0
- data/lib/subcommands/keyword.rb +214 -0
- data/lib/subcommands/series.rb +59 -0
- data/lib/subcommands/tags.rb +37 -0
- data/logtime.gemspec +23 -0
- data.tar.gz.sig +2 -0
- metadata +167 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,214 @@
|
|
1
|
+
class Keywords < Thor
|
2
|
+
# desc "add <account> <keyword>", "Add a new <keyword> to an <account>"
|
3
|
+
# option :interactive, :type => :boolean, :alias => :i
|
4
|
+
# def add(account, keyword=nil)
|
5
|
+
# account = Account.find_by(:name => account)
|
6
|
+
|
7
|
+
# if account.blank?
|
8
|
+
# say 'no account found', :red
|
9
|
+
# else
|
10
|
+
# if options[:interactive]
|
11
|
+
# say "To stop adding keywords type exit", :cyan
|
12
|
+
# while true
|
13
|
+
# keyword = ask ':'
|
14
|
+
# break if keyword == 'exit'
|
15
|
+
# if Keyword.new(:keyword => keyword, :account_id => account.id).save
|
16
|
+
# say 'added', :green
|
17
|
+
# else
|
18
|
+
# say 'failed', :red
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# else
|
22
|
+
# keyword.split(',').each do |k|
|
23
|
+
# k = Keyword.new(:keyword => k, :account_id => account.id)
|
24
|
+
|
25
|
+
# if k.save
|
26
|
+
# say k.keyword + ' keyword added', :green
|
27
|
+
# else
|
28
|
+
# say 'could not add keyword!', :red
|
29
|
+
# error k.errors.first[0].to_s + ' ' + k.errors.first[1]
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
|
36
|
+
# desc "delete <account> <keyword>", "Removes <keyword> on an <account>"
|
37
|
+
# def delete(account, keyword)
|
38
|
+
# account = Account.search(account)
|
39
|
+
|
40
|
+
# if account.blank?
|
41
|
+
# say 'no account found', :red
|
42
|
+
# else
|
43
|
+
# keyword.split(',').each do |k|
|
44
|
+
# k = Keyword.search(k)
|
45
|
+
|
46
|
+
# if k.blank?
|
47
|
+
# say 'could not find keyword!', :red
|
48
|
+
# else
|
49
|
+
# say k.keyword + ' removed', :green if k.destroy
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
|
55
|
+
# desc "list", "Lists keywords belonging to an <account>"
|
56
|
+
# option :account
|
57
|
+
# def list(account=nil)
|
58
|
+
# if account.nil?
|
59
|
+
# keywords = Keyword.all
|
60
|
+
|
61
|
+
# table = [['# Keyword', '# Position', '']] # header
|
62
|
+
# keywords.each do |k|
|
63
|
+
# stat = k.keyword_statistics.last unless k.keyword_statistics.empty?
|
64
|
+
# table << [k.keyword, stat.position || '', stat.created_at.pretty ] # row
|
65
|
+
# end
|
66
|
+
|
67
|
+
# say ''
|
68
|
+
# print_table table
|
69
|
+
# say ''
|
70
|
+
# else
|
71
|
+
# account = Account.search(account)
|
72
|
+
# if account.blank?
|
73
|
+
# say 'no account found', :red
|
74
|
+
# elsif account.keywords.empty?
|
75
|
+
# say 'no keywords exist for that account yet', :red
|
76
|
+
# else
|
77
|
+
# table = [['# Keyword', '# Position', '']] # header
|
78
|
+
# account.keywords.each do |k|
|
79
|
+
# stat = k.keyword_statistics.last unless k.keyword_statistics.empty?
|
80
|
+
# table << [k.keyword, stat.position || '', stat.created_at.pretty] # row
|
81
|
+
# end
|
82
|
+
|
83
|
+
# say ''
|
84
|
+
# print_table table
|
85
|
+
# say ''
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
|
90
|
+
# desc "stats", "Compare keywords over time"
|
91
|
+
# option :search, :default => 'year', :banner => 'year,month,week,day'
|
92
|
+
# option :length, :type => :numeric, :default => 1
|
93
|
+
# option :engine, :default => :google
|
94
|
+
# def stats(account)
|
95
|
+
# account = Account.search(account)
|
96
|
+
|
97
|
+
# if account.blank?
|
98
|
+
# say 'no account found', :red
|
99
|
+
# elsif account.keywords.empty?
|
100
|
+
# say 'no keywords exist for that account yet', :red
|
101
|
+
# else
|
102
|
+
|
103
|
+
# table = [['# Keyword', 'n', 'High', 'Low', 'Current', 'Delta']] # header
|
104
|
+
# searched = 0;
|
105
|
+
# account.keywords.each do |k|
|
106
|
+
# stats = KeywordStatistic.send options[:search], k.id, options[:length], options[:engine]
|
107
|
+
# stats.order(:position)
|
108
|
+
|
109
|
+
# if !stats.empty?
|
110
|
+
# delta = stats.last.position - stats.first.position
|
111
|
+
# delta = '' if delta == 0
|
112
|
+
# table << [k.keyword, stats.size, stats.last.position, stats.first.position, stats.last.position, delta] # row
|
113
|
+
# searched = searched + stats.size
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
|
117
|
+
# if searched == 0
|
118
|
+
# say 'these keywords have not been indexed yet', :red
|
119
|
+
# else
|
120
|
+
# say ''
|
121
|
+
# say account.name + ' has ' + searched.to_s + ' recorded positions for ' + account.keywords.size.to_s + ' keywords', :green
|
122
|
+
# say ''
|
123
|
+
# print_table table
|
124
|
+
# say ''
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
# end
|
128
|
+
|
129
|
+
# #desc "import", "imports csv file"
|
130
|
+
# #options :file
|
131
|
+
# #def import(file)
|
132
|
+
|
133
|
+
# #end
|
134
|
+
|
135
|
+
# desc "index", "gets keyword position and saves it for later use"
|
136
|
+
# def index(account)
|
137
|
+
# account = Account.search(account)
|
138
|
+
# if account.blank?
|
139
|
+
# say 'no account found', :red
|
140
|
+
# else
|
141
|
+
# table = [['# Keyword', '# Google', '# GoogleUS']] # header
|
142
|
+
# account.keywords.each do |k|
|
143
|
+
# # Google
|
144
|
+
# google = ::Ranking.new(:keyword => k.keyword, :url => account.domain, :limit =>100).from_googleAU
|
145
|
+
# if !google.blank?
|
146
|
+
# KeywordStatistic.new(:keyword_id => k.id, :engine => :google, :position => google).save
|
147
|
+
# say '.', :green, false
|
148
|
+
# else
|
149
|
+
# say '.', :red, false
|
150
|
+
# end
|
151
|
+
|
152
|
+
# # GoogleUS
|
153
|
+
# googleUS = ::Ranking.new(:keyword => k.keyword, :url => account.domain, :limit =>100).from_googleUS
|
154
|
+
# if !googleUS.blank?
|
155
|
+
# KeywordStatistic.new(:keyword_id => k.id, :engine => :googleUS, :position => googleUS).save
|
156
|
+
# say '.', :green, false
|
157
|
+
# else
|
158
|
+
# say '.', :red, false
|
159
|
+
# end
|
160
|
+
|
161
|
+
# # Bing
|
162
|
+
# # bing = ::Ranking.new(:keyword => k.keyword, :url => account.domain, :limit =>100).from_bingAU
|
163
|
+
# # if !bing.blank?
|
164
|
+
# # KeywordStatistic.new(:keyword_id => k.id, :engine => :bing, :position => bing).save
|
165
|
+
# # say '.', :green, false
|
166
|
+
# # else
|
167
|
+
# # say '.', :red, false
|
168
|
+
# # end
|
169
|
+
|
170
|
+
|
171
|
+
# # Yahoo
|
172
|
+
# # yahoo = ::Ranking.new(:keyword => k.keyword, :url => account.domain, :limit =>100).from_yahooAU
|
173
|
+
# # if !yahoo.blank?
|
174
|
+
# # KeywordStatistic.new(:keyword_id => k.id, :engine => :yahoo, :position => yahoo).save
|
175
|
+
# # say '.', :green, false
|
176
|
+
# # else
|
177
|
+
# # say '.', :red, false
|
178
|
+
# # end
|
179
|
+
|
180
|
+
# table << [k.keyword, google, googleUS]
|
181
|
+
# end
|
182
|
+
|
183
|
+
# say ''
|
184
|
+
# print_table table
|
185
|
+
# say ''
|
186
|
+
# end
|
187
|
+
# end
|
188
|
+
|
189
|
+
# desc "export", "export keywords with positions"
|
190
|
+
# option :file
|
191
|
+
# def export(account)
|
192
|
+
# account = Account.search(account)
|
193
|
+
# if account.blank?
|
194
|
+
# say 'no account found', :red
|
195
|
+
# else
|
196
|
+
# table = [['# Date', '# Keyword', '# Position']] # header
|
197
|
+
|
198
|
+
# options[:file] = 'export.csv' unless options.includes? :file
|
199
|
+
# CSV.open(options[:file], 'w') do |csv|
|
200
|
+
# account.keywords.each do |keyword|
|
201
|
+
# keyword.keyword_statistics.each do |stat|
|
202
|
+
# row = [stat.created_at, keyword.keyword, stat.position]
|
203
|
+
# table << row
|
204
|
+
# csv << row
|
205
|
+
# end
|
206
|
+
# end
|
207
|
+
# end
|
208
|
+
|
209
|
+
# say ''
|
210
|
+
# print_table table
|
211
|
+
# say ''
|
212
|
+
# end
|
213
|
+
# end
|
214
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module SubCommands
|
2
|
+
class Series < Thor
|
3
|
+
desc "ls", "list series"
|
4
|
+
option :order, :type => :string, :default => "updated_at"
|
5
|
+
def ls
|
6
|
+
series = ::Series.all.order(options[:order].to_sym => :desc)
|
7
|
+
|
8
|
+
series_stats = Statistics.new
|
9
|
+
table = [['#', 'log', 'start', 'end', 'hours']] # header
|
10
|
+
series.each do |s|
|
11
|
+
next if s.log_id.blank?
|
12
|
+
|
13
|
+
start = s.start
|
14
|
+
finish = s.end
|
15
|
+
|
16
|
+
# total time
|
17
|
+
series_stats << timer = start.difference(finish || Time.now)
|
18
|
+
|
19
|
+
start = start.display if start
|
20
|
+
finish = finish.display if finish
|
21
|
+
|
22
|
+
table << [s.id,
|
23
|
+
Log.find(s.log_id).name,
|
24
|
+
start, finish || "active",
|
25
|
+
(timer/3600).round(2).to_s] # row
|
26
|
+
end
|
27
|
+
puts ''
|
28
|
+
print_table table
|
29
|
+
puts ''
|
30
|
+
|
31
|
+
say [series.count.to_s, "series out of", ::Series.count].join(' '), :cyan
|
32
|
+
say [(series_stats.mean/3600).round(2), " hours avg"].join(' '), :cyan
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "rm [ID]", "remove series"
|
36
|
+
option :confirm, :type => :boolean, :default => false, :alias => '-y'
|
37
|
+
def rm(id)
|
38
|
+
if ::Series.where(id: id).count == 0
|
39
|
+
say "Series #" + id.to_s + " not found", :red
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
series = ::Series.find(id)
|
44
|
+
|
45
|
+
if !options[:confirm]
|
46
|
+
say "You must --confirm before removing this series", :red
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
if !series.finished?
|
51
|
+
series.log.deactivate
|
52
|
+
end
|
53
|
+
|
54
|
+
destroyed = series.destroy
|
55
|
+
say "Series #" + id.to_s + " has been destroyed", :green
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module SubCommands
|
2
|
+
class Tags < Thor
|
3
|
+
desc "add", "create new tag"
|
4
|
+
def add(tag)
|
5
|
+
tag = ::Tag.new(tag: tag.strip)
|
6
|
+
|
7
|
+
if !tag.save
|
8
|
+
tag.errors.full_messages.each do |error|
|
9
|
+
say error, :red
|
10
|
+
end
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
say tag.tag + " added!", :green
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "rm", "remove tag"
|
18
|
+
# option :prune, :type => :boolean, :alias => '-p', :default => false not implemented yet
|
19
|
+
def rm(tag)
|
20
|
+
tag = ::Tag.where(tag: tag)
|
21
|
+
p tag.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "ls", "list tags"
|
25
|
+
option :order, :type => :string, :default => "created_at"
|
26
|
+
def ls
|
27
|
+
tags = ::Tag.order(options[:order].to_sym => :desc)
|
28
|
+
|
29
|
+
table = [['#', '']] # header
|
30
|
+
tags.each do |tag|
|
31
|
+
table << [tag.id, tag.tag] # row
|
32
|
+
end
|
33
|
+
print_table table
|
34
|
+
end
|
35
|
+
|
36
|
+
# desc "add <name> <domain>", "Adds an account that owns a <domain>"
|
37
|
+
# def add(name, domain)
|
38
|
+
# account = Account.new({
|
39
|
+
# :name => name,
|
40
|
+
# :domain => domain
|
41
|
+
# })
|
42
|
+
|
43
|
+
# if account.save
|
44
|
+
# say name + ' added', :green
|
45
|
+
# else
|
46
|
+
# say 'could not add account!', :red
|
47
|
+
# error account.errors.first[0].to_s + ' ' + account.errors.first[1]
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
|
51
|
+
# desc "delete <account>", "Deletes <account>"
|
52
|
+
# #option :force, :type => :boolean, :alias => '-f', :default => false
|
53
|
+
# def delete(account)
|
54
|
+
# account = Account.search(account)
|
55
|
+
|
56
|
+
# if account.blank?
|
57
|
+
# say 'no account found', :red
|
58
|
+
# else
|
59
|
+
# say 'WARNING ', :red, false
|
60
|
+
# should_destroy = ask "Are you sure you want to delete " + account.name + "?", :limited_to => ["y", "n"]
|
61
|
+
# account.destroy and say account.name + ' destroyed!', :red if should_destroy == 'y'
|
62
|
+
# say 'no action was taken', :green if should_destroy == 'n'
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
|
66
|
+
# desc "list", "Lists all accounts"
|
67
|
+
# def list
|
68
|
+
# accounts = Account.all
|
69
|
+
|
70
|
+
# table = [['# Name', '# Domain', '# Keywords']] # header
|
71
|
+
# accounts.each do |a|
|
72
|
+
# table << [a.name, a.domain, a.keywords.size] # row
|
73
|
+
# end
|
74
|
+
# say ''
|
75
|
+
# print_table table
|
76
|
+
# say ''
|
77
|
+
# end
|
78
|
+
end
|
79
|
+
end
|
data/lib/commands/add.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commands
|
2
|
+
module Add
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
option :tag, :type => :string, :alias => '-t', default: ""
|
6
|
+
option :estimate, :type => :string, :alias => '-e', default: false
|
7
|
+
option :start, :type => :boolean, default: true
|
8
|
+
desc "add [NAME]", "create new log"
|
9
|
+
def add(name)
|
10
|
+
log = Log.new(name: name).tag(options[:tag])
|
11
|
+
|
12
|
+
# estimate option
|
13
|
+
if options[:estimate]
|
14
|
+
estimation = ChronicDuration.parse(options[:estimate]) if !estimation
|
15
|
+
if !estimation
|
16
|
+
say "could not parse estimation time: " + options[:estimate], :red
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
log.estimation = estimation
|
20
|
+
end
|
21
|
+
|
22
|
+
# save log
|
23
|
+
if !log.save
|
24
|
+
log.errors.full_messages.each do |error|
|
25
|
+
say error, :red
|
26
|
+
end
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
# start option
|
31
|
+
if options[:start]
|
32
|
+
log.activate if Series.begin(log_id: log.id)
|
33
|
+
say name.to_s + " added and started", :green
|
34
|
+
else
|
35
|
+
say name.to_s + " added", :green
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Commands
|
2
|
+
module List
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
option :active, :type => :boolean, :default => true
|
6
|
+
#option :tag, :type => :string, :alias => '-t'
|
7
|
+
desc "ls", "list time series"
|
8
|
+
def ls(tag='')
|
9
|
+
logs = Log.where(active: options[:active]).includes(:series).includes(:tags)
|
10
|
+
logs = logs.tagged(tag) if !tag.blank?
|
11
|
+
|
12
|
+
creep_stats = Statistics.new
|
13
|
+
table = [['#', 'name', 'active', 'tags', 'series', 'time', 'estimate', 'creep %']] # header
|
14
|
+
logs.each do |log|
|
15
|
+
|
16
|
+
# tags
|
17
|
+
tags = log.tags.map do |t|
|
18
|
+
t.tag
|
19
|
+
end
|
20
|
+
|
21
|
+
# only show logs with tag, if selected
|
22
|
+
# if !tag.blank?
|
23
|
+
# next unless tags.include? tag
|
24
|
+
# end
|
25
|
+
|
26
|
+
# active text
|
27
|
+
active = "ACTIVE" if log.active == "t"
|
28
|
+
|
29
|
+
# total counter
|
30
|
+
total_time = (log.total/3600).round(2)
|
31
|
+
|
32
|
+
# estimation creep
|
33
|
+
if log.estimation
|
34
|
+
creep = (total_time/log.estimation)*100
|
35
|
+
creep_stats << creep
|
36
|
+
creep = creep.round(3)
|
37
|
+
estimation = (log.estimation/3600).round(3) # log estimation to hours
|
38
|
+
end
|
39
|
+
|
40
|
+
table << [log.id,
|
41
|
+
log.name,
|
42
|
+
active || '',
|
43
|
+
tags,
|
44
|
+
log.series.count || '',
|
45
|
+
total_time || '',
|
46
|
+
estimation || '',
|
47
|
+
creep || '']
|
48
|
+
end
|
49
|
+
|
50
|
+
if logs.count == 0
|
51
|
+
puts ""
|
52
|
+
say "No logs found", :red
|
53
|
+
puts ""
|
54
|
+
exit
|
55
|
+
else
|
56
|
+
puts ""
|
57
|
+
print_table table
|
58
|
+
puts ""
|
59
|
+
say [logs.count.to_s, "logs out of", Log.count.to_s].join(' '), :cyan
|
60
|
+
say [creep_stats.count, "estimations"].join(' '), :cyan
|
61
|
+
say [creep_stats.mean.round(2), "percent mean creep"].join(' '), :cyan
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Commands
|
2
|
+
module Start
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
desc "start [NAME]", "start new timer"
|
6
|
+
def start(name)
|
7
|
+
|
8
|
+
# not found
|
9
|
+
if Log.where(name: name).count == 0
|
10
|
+
say name.to_s + " not found", :red
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
log = Log.find_by(name: name)
|
15
|
+
|
16
|
+
# already active?
|
17
|
+
if log.active == "t"
|
18
|
+
say name.to_s + " already active", :red
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
|
22
|
+
# begin new series, activate log
|
23
|
+
series = Series.begin(log_id: log.id) and log.activate
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Commands
|
2
|
+
module Stop
|
3
|
+
def self.included(thor)
|
4
|
+
thor.class_eval do
|
5
|
+
desc "stop [NAME]", "stop active timer"
|
6
|
+
def stop(name)
|
7
|
+
if Log.where(name: name).count == 0
|
8
|
+
say name.to_s + " not found", :red
|
9
|
+
exit
|
10
|
+
end
|
11
|
+
|
12
|
+
log = Log.find_by(name: name)
|
13
|
+
if log.active == "f"
|
14
|
+
say name.to_s + " not active", :red
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
series = log.series.last
|
19
|
+
if series.stop and log.deactivate
|
20
|
+
say name + " stopped!", :green
|
21
|
+
|
22
|
+
# this_series = log.series.last
|
23
|
+
say (series.total/3600).round(3).to_s + " hours out of " + (log.total/3600).round(3).to_s, :cyan
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module StatisticsHelper
|
2
|
+
class Series < Array
|
3
|
+
def sum
|
4
|
+
inject(0.0) { |result, el| result + el }
|
5
|
+
end
|
6
|
+
|
7
|
+
def mean
|
8
|
+
return 0 if size == 0
|
9
|
+
avg = sum / size
|
10
|
+
end
|
11
|
+
|
12
|
+
alias :average :mean
|
13
|
+
alias :avg :mean
|
14
|
+
end
|
15
|
+
|
16
|
+
class Statistics
|
17
|
+
def initialize
|
18
|
+
@statistics = Series.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(statistic)
|
22
|
+
@statistics << statistic
|
23
|
+
end
|
24
|
+
|
25
|
+
def count
|
26
|
+
@statistics.count
|
27
|
+
end
|
28
|
+
|
29
|
+
def mean
|
30
|
+
@statistics.mean
|
31
|
+
end
|
32
|
+
|
33
|
+
def <<(statistic)
|
34
|
+
add(statistic)
|
35
|
+
end
|
36
|
+
|
37
|
+
def +(statistics)
|
38
|
+
@statistics + statistics.data
|
39
|
+
end
|
40
|
+
|
41
|
+
def data
|
42
|
+
@statistics
|
43
|
+
end
|
44
|
+
|
45
|
+
alias :average :mean
|
46
|
+
alias :avg :mean
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module TimeDifferenceHelper
|
2
|
+
def difference(relative)
|
3
|
+
if relative > self
|
4
|
+
difference = relative - self
|
5
|
+
else
|
6
|
+
difference = self - relative
|
7
|
+
end
|
8
|
+
|
9
|
+
# hours = difference / 3600
|
10
|
+
# difference -= hours * 3600
|
11
|
+
# minutes = difference / 60
|
12
|
+
# difference -= minutes * 60
|
13
|
+
# seconds = difference
|
14
|
+
# "#{hours.to_s.rjust(2, '0')}:#{minutes.to_s.rjust(2, '0')}:#{seconds.to_s.rjust(2, '0')}"
|
15
|
+
|
16
|
+
# hours = difference / 3600
|
17
|
+
return difference
|
18
|
+
end
|
19
|
+
Time.send :include, self
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module TimeToWordsHelper
|
2
|
+
def time_to_words_ago(time)
|
3
|
+
a = (Time.now-time).to_i
|
4
|
+
case a
|
5
|
+
when 0 then 'just now'
|
6
|
+
when 1 then 'a second ago'
|
7
|
+
when 2..59 then a.to_s+' seconds ago'
|
8
|
+
when 60..119 then 'a minute ago' # 120 = 2 minutes
|
9
|
+
when 120..3540 then (a/60).to_i.to_s+' minutes ago'
|
10
|
+
when 3541..7100 then 'an hour ago' # 3600 = 1 hour
|
11
|
+
when 7101..82800 then ((a+99)/3600).to_i.to_s+' hours ago'
|
12
|
+
when 82801..172000 then 'a day ago' # 86400 = 1 day
|
13
|
+
when 172001..518400 then ((a+800)/(60*60*24)).to_i.to_s+' days ago'
|
14
|
+
when 518400..1036800 then 'a week ago'
|
15
|
+
else ((a+180000)/(60*60*24*7)).to_i.to_s+' weeks ago'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def time_to_words(seconds)
|
20
|
+
seconds
|
21
|
+
case a
|
22
|
+
when 0..60 then 'seconds'
|
23
|
+
when 60..69 then 'minute'
|
24
|
+
when 70..3600 then 'minutes'
|
25
|
+
when 3600..7199 then 'day'
|
26
|
+
when 7200..107999 then 'days' # 3600 = 1 hour
|
27
|
+
when 108000..215999 then 'month'
|
28
|
+
when 216000..1296000 then 'months'
|
29
|
+
when 1296000..1296000 then 'year'
|
30
|
+
else 'years'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|