appstats 0.10.0 → 0.11.2
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.
- data/Gemfile.lock +1 -1
- data/db/migrations/20110222215437_create_appstats_jobs.rb +16 -0
- data/db/schema.rb +11 -1
- data/lib/appstats.rb +1 -0
- data/lib/appstats/date_range.rb +13 -6
- data/lib/appstats/entry_date.rb +15 -0
- data/lib/appstats/log_collector.rb +35 -1
- data/lib/appstats/query.rb +4 -2
- data/lib/appstats/result_job.rb +54 -0
- data/lib/appstats/version.rb +1 -1
- data/lib/daemons/appstats_log_collector.rb +10 -2
- data/spec/date_range_spec.rb +24 -4
- data/spec/entry_date_spec.rb +34 -0
- data/spec/entry_spec.rb +4 -4
- data/spec/log_collector_spec.rb +77 -0
- data/spec/logger_spec.rb +18 -18
- data/spec/query_spec.rb +355 -348
- data/spec/result_job_spec.rb +333 -0
- metadata +8 -4
data/Gemfile.lock
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateAppstatsJobs < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :appstats_result_jobs do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :frequency
|
6
|
+
t.string :status
|
7
|
+
t.text :query
|
8
|
+
t.datetime :last_run_at
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :appstats_result_jobs
|
15
|
+
end
|
16
|
+
end
|
data/db/schema.rb
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended to check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema.define(:version =>
|
13
|
+
ActiveRecord::Schema.define(:version => 20110222215437) do
|
14
14
|
|
15
15
|
create_table "appstats_actions", :force => true do |t|
|
16
16
|
t.string "name"
|
@@ -89,6 +89,16 @@ ActiveRecord::Schema.define(:version => 20110217234136) do
|
|
89
89
|
|
90
90
|
add_index "appstats_log_collectors", ["host"], :name => "index_appstats_log_collectors_on_host"
|
91
91
|
|
92
|
+
create_table "appstats_result_jobs", :force => true do |t|
|
93
|
+
t.string "name"
|
94
|
+
t.string "frequency"
|
95
|
+
t.string "status"
|
96
|
+
t.text "query"
|
97
|
+
t.datetime "last_run_at"
|
98
|
+
t.datetime "created_at"
|
99
|
+
t.datetime "updated_at"
|
100
|
+
end
|
101
|
+
|
92
102
|
create_table "appstats_results", :force => true do |t|
|
93
103
|
t.string "name"
|
94
104
|
t.string "result_type"
|
data/lib/appstats.rb
CHANGED
@@ -13,6 +13,7 @@ require "#{File.dirname(__FILE__)}/appstats/log_collector"
|
|
13
13
|
require "#{File.dirname(__FILE__)}/appstats/parser"
|
14
14
|
require "#{File.dirname(__FILE__)}/appstats/query"
|
15
15
|
require "#{File.dirname(__FILE__)}/appstats/result"
|
16
|
+
require "#{File.dirname(__FILE__)}/appstats/result_job"
|
16
17
|
require "#{File.dirname(__FILE__)}/appstats/host"
|
17
18
|
require "#{File.dirname(__FILE__)}/appstats/context_key"
|
18
19
|
require "#{File.dirname(__FILE__)}/appstats/context_value"
|
data/lib/appstats/date_range.rb
CHANGED
@@ -115,37 +115,44 @@ module Appstats
|
|
115
115
|
end
|
116
116
|
|
117
117
|
|
118
|
-
m = input.match(/^this\s*(year|month|week|day)$/)
|
118
|
+
m = input.match(/^this\s*(year|quarter|month|week|day)$/)
|
119
119
|
unless m.nil?
|
120
120
|
range.from = EntryDate.parse(input)
|
121
|
-
range.format = m[1]
|
121
|
+
range.format = ["week","quarter"].include?(m[1]) ? :inclusive : :fixed_point
|
122
122
|
return range
|
123
123
|
end
|
124
124
|
|
125
|
-
m = input.match(/^(last|previous)\s*(year|month|week|day)$/)
|
125
|
+
m = input.match(/^(last|previous)\s*(year|quarter|month|week|day)$/)
|
126
126
|
unless m.nil?
|
127
127
|
range.from = EntryDate.parse(input)
|
128
128
|
if m[2] == "week"
|
129
129
|
range.to = range.from.end_of_week
|
130
130
|
range.format = :inclusive
|
131
|
+
elsif m[2] == "quarter"
|
132
|
+
range.to = range.from.end_of_quarter
|
133
|
+
range.format = :inclusive
|
131
134
|
else
|
132
135
|
range.format = :fixed_point
|
133
136
|
end
|
134
137
|
return range
|
135
138
|
end
|
136
139
|
|
137
|
-
m = input.match(/^last\s*(.+)\s*(year|years|month|months|week|weeks|day|days)$/)
|
140
|
+
m = input.match(/^last\s*(.+)\s*(year|years|quarter|quarters|month|months|week|weeks|day|days)$/)
|
138
141
|
unless m.nil?
|
139
142
|
range.from = EntryDate.parse(input)
|
140
143
|
range.format = :inclusive
|
141
144
|
return range
|
142
145
|
end
|
143
146
|
|
144
|
-
m = input.match(/^previous\s*(.+)\s*(year|month|week|day)s?$/)
|
147
|
+
m = input.match(/^previous\s*(.+)\s*(year|quarter|month|week|day)s?$/)
|
145
148
|
unless m.nil?
|
146
149
|
range.from = EntryDate.parse(input)
|
147
150
|
to = EntryDate.parse("last #{m[2]}")
|
148
|
-
|
151
|
+
if m[2] == "week"
|
152
|
+
to = to.end_of_week
|
153
|
+
elsif m[2] == "quarter"
|
154
|
+
to = to.end_of_quarter
|
155
|
+
end
|
149
156
|
range.to = to
|
150
157
|
range.format = :inclusive
|
151
158
|
return range
|
data/lib/appstats/entry_date.rb
CHANGED
@@ -22,6 +22,11 @@ module Appstats
|
|
22
22
|
week
|
23
23
|
end
|
24
24
|
|
25
|
+
def end_of_quarter
|
26
|
+
t = to_time.end_of_quarter
|
27
|
+
EntryDate.new(:year => t.year, :month => t.month)
|
28
|
+
end
|
29
|
+
|
25
30
|
def to_time(mode = :start)
|
26
31
|
return Time.now if @year.nil?
|
27
32
|
t = Time.parse("#{@year}-#{@month||'01'}-#{@day||'01'} #{@hour||'00'}:#{@min||'00'}:#{@sec||'00'}")
|
@@ -79,6 +84,9 @@ module Appstats
|
|
79
84
|
t_parts = [:year,:month,:day]
|
80
85
|
elsif input.match(/^this year$/)
|
81
86
|
t_parts = [:year]
|
87
|
+
elsif input.match(/^this quarter$/)
|
88
|
+
t = t.beginning_of_quarter
|
89
|
+
t_parts = [:year,:month]
|
82
90
|
elsif input.match(/^this month$/)
|
83
91
|
t_parts = [:year,:month]
|
84
92
|
elsif input.match(/^this week$/)
|
@@ -99,6 +107,13 @@ module Appstats
|
|
99
107
|
t -= last_date_offset(m).year
|
100
108
|
t_parts = [:year]
|
101
109
|
end
|
110
|
+
|
111
|
+
m = input.match(/^(last|previous)\s*(\d*)\s*quarters?$/)
|
112
|
+
if m
|
113
|
+
t = t.beginning_of_quarter
|
114
|
+
last_date_offset(m).times { t = (t - 1.day).beginning_of_quarter }
|
115
|
+
t_parts = [:year,:month]
|
116
|
+
end
|
102
117
|
|
103
118
|
m = input.match(/^(last|previous)\s*(\d*)\s*months?$/)
|
104
119
|
if m
|
@@ -11,6 +11,19 @@ module Appstats
|
|
11
11
|
File.expand_path("#{File.dirname(__FILE__)}/../../log/appstats_remote_log_#{id}.log")
|
12
12
|
end
|
13
13
|
|
14
|
+
def processed_filename
|
15
|
+
return filename if filename.nil? || filename == ''
|
16
|
+
m = filename.match(/(.*\/)(.*)/)
|
17
|
+
prefix = "__processed__"
|
18
|
+
return "#{prefix}#{filename}" if m.nil?
|
19
|
+
"#{m[1]}#{prefix}#{m[2]}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.should_process(last_time)
|
23
|
+
return true if last_time.nil?
|
24
|
+
Time.now.day > last_time.day
|
25
|
+
end
|
26
|
+
|
14
27
|
def self.find_remote_files(remote_login,path,log_template)
|
15
28
|
begin
|
16
29
|
Appstats.log(:info,"Looking for logs in [#{remote_login[:user]}@#{remote_login[:host]}:#{path}] labelled [#{log_template}]")
|
@@ -120,7 +133,28 @@ module Appstats
|
|
120
133
|
Appstats.log(:info,"Processed #{count} file(s) with #{total_entries} entr(ies).")
|
121
134
|
count
|
122
135
|
end
|
136
|
+
|
137
|
+
def self.remove_remote_files(remote_login)
|
138
|
+
all = LogCollector.where("status = 'processed'").all
|
139
|
+
if all.empty?
|
140
|
+
Appstats.log(:info,"No remote logs to remove.")
|
141
|
+
return 0
|
142
|
+
end
|
143
|
+
|
144
|
+
count = 0
|
145
|
+
Appstats.log(:info,"About to remove #{all.size} remote file(s) from the processing queue.")
|
146
|
+
all.each do |log_collector|
|
147
|
+
Net::SSH.start(remote_login[:host], remote_login[:user], :password => remote_login[:password] ) do |ssh|
|
148
|
+
ssh.exec!("mv #{log_collector.filename} #{log_collector.processed_filename}")
|
149
|
+
Appstats.log(:info," - #{remote_login[:user]}@#{remote_login[:host]}:#{log_collector.processed_filename}")
|
150
|
+
log_collector.status = "destroyed"
|
151
|
+
log_collector.save
|
152
|
+
count += 1
|
153
|
+
end
|
154
|
+
end
|
155
|
+
Appstats.log(:info,"Removed #{count} remote file(s).")
|
156
|
+
count
|
157
|
+
end
|
123
158
|
|
124
159
|
end
|
125
|
-
|
126
160
|
end
|
data/lib/appstats/query.rb
CHANGED
@@ -7,9 +7,11 @@ module Appstats
|
|
7
7
|
|
8
8
|
@@nill_query = "select 0 from appstats_entries LIMIT 1"
|
9
9
|
@@default = "1=1"
|
10
|
-
attr_accessor :query, :action, :host, :date_range, :query_to_sql, :contexts
|
10
|
+
attr_accessor :name, :query, :action, :host, :date_range, :query_to_sql, :contexts
|
11
11
|
|
12
12
|
def initialize(data = {})
|
13
|
+
@name = data[:name]
|
14
|
+
@result_type = data[:result_type] || "on_demand"
|
13
15
|
self.query=(data[:query])
|
14
16
|
end
|
15
17
|
|
@@ -19,7 +21,7 @@ module Appstats
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def run
|
22
|
-
result = Appstats::Result.new(:result_type =>
|
24
|
+
result = Appstats::Result.new(:name => @name, :result_type => @result_type, :query => @query, :query_as_sql => @query_to_sql, :action => @action, :host => @host, :from_date => @date_range.from_date, :to_date => @date_range.to_date, :contexts => @contexts)
|
23
25
|
result.count = ActiveRecord::Base.connection.select_one(@query_to_sql)["count(*)"].to_i
|
24
26
|
result.save
|
25
27
|
result
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Appstats
|
2
|
+
class ResultJob < ActiveRecord::Base
|
3
|
+
set_table_name "appstats_result_jobs"
|
4
|
+
|
5
|
+
attr_accessible :name, :frequency, :status, :query, :last_run_at
|
6
|
+
|
7
|
+
@@frequency_methods =
|
8
|
+
|
9
|
+
def should_run
|
10
|
+
return true if frequency == "once" && last_run_at.nil?
|
11
|
+
period = { "daily" => :beginning_of_day, "weekly" => :beginning_of_week, "monthly" => :beginning_of_month, "quarterly" => :beginning_of_quarter, "yearly" => :beginning_of_year }[frequency]
|
12
|
+
return false if period.nil?
|
13
|
+
return true if last_run_at.nil?
|
14
|
+
last_run_at.send(period) <= (Time.now.send(period) - 1.day).send(period)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(o)
|
18
|
+
o.class == self.class && o.send(:state) == state
|
19
|
+
end
|
20
|
+
alias_method :eql?, :==
|
21
|
+
|
22
|
+
def self.run
|
23
|
+
count = 0
|
24
|
+
all = ResultJob.where("frequency <> 'once' or last_run_at IS NULL").all
|
25
|
+
if all.size == 0
|
26
|
+
Appstats.log(:info, "No result jobs to run.")
|
27
|
+
return count
|
28
|
+
end
|
29
|
+
Appstats.log(:info, "About to analyze #{all.size} result job(s).")
|
30
|
+
all.each do |job|
|
31
|
+
if job.should_run
|
32
|
+
Appstats.log(:info, " - Job #{job.name} run [ID #{job.id}, FREQUENCY #{job.frequency}, QUERY #{job.query}]")
|
33
|
+
query = Appstats::Query.new(:name => job.name, :result_type => "result_job", :query => job.query)
|
34
|
+
query.run
|
35
|
+
job.last_run_at = Time.now
|
36
|
+
job.save
|
37
|
+
count += 1
|
38
|
+
else
|
39
|
+
Appstats.log(:info, " - Job #{job.name} NOT run [ID #{job.id}, FREQUENCY #{job.frequency}, QUERY #{job.query}]")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
Appstats.log(:info, "Ran #{count} query(ies).")
|
43
|
+
count
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def state
|
49
|
+
[name, frequency, status, query, last_run_at]
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
data/lib/appstats/version.rb
CHANGED
@@ -30,19 +30,27 @@ appstats_config = YAML::load(File.open(options[:config]))
|
|
30
30
|
ActiveRecord::Base.establish_connection(appstats_config['database'])
|
31
31
|
require File.join(File.dirname(__FILE__),"..","appstats")
|
32
32
|
|
33
|
+
last_processed_at = nil
|
33
34
|
Appstats.log(:info,"Started Appstats Log Collector")
|
34
35
|
while($running) do
|
36
|
+
unless Appstats::LogCollector.should_process(last_processed_at)
|
37
|
+
an_hour = 60*60
|
38
|
+
sleep an_hour
|
39
|
+
next
|
40
|
+
end
|
41
|
+
last_processed_at = Time.now
|
35
42
|
ActiveRecord::Base.connection.reconnect!
|
36
43
|
appstats_config["remote_servers"].each do |remote_server|
|
37
44
|
Appstats::LogCollector.find_remote_files(remote_server,remote_server[:path],remote_server[:template])
|
38
45
|
end
|
39
46
|
Appstats::LogCollector.download_remote_files(appstats_config["remote_servers"])
|
40
47
|
Appstats::LogCollector.process_local_files
|
48
|
+
Appstats::ResultJob.run
|
49
|
+
|
41
50
|
Appstats::Action.update_actions
|
42
51
|
Appstats::Host.update_hosts
|
43
52
|
Appstats::ContextKey.update_context_keys
|
44
53
|
Appstats::ContextValue.update_context_values
|
54
|
+
Appstats::LogCollector.remove_remote_files(appstats_config["remote_servers"])
|
45
55
|
ActiveRecord::Base.connection.disconnect!
|
46
|
-
a_day_in_seconds = 60*60*24
|
47
|
-
sleep a_day_in_seconds
|
48
56
|
end
|
data/spec/date_range_spec.rb
CHANGED
@@ -82,12 +82,16 @@ module Appstats
|
|
82
82
|
DateRange.parse(" yesterday ").should == DateRange.new(:from => EntryDate.new(:year => 2010, :month => 1, :day => 14), :to => nil, :format => :fixed_point )
|
83
83
|
end
|
84
84
|
|
85
|
-
describe "this (year|month|week|day)" do
|
85
|
+
describe "this (year|quarter|month|week|day)" do
|
86
86
|
|
87
87
|
it "should understand this year" do
|
88
88
|
DateRange.parse(" this year ").should == DateRange.new(:from => EntryDate.new(:year => 2010), :to => nil, :format => :fixed_point )
|
89
89
|
end
|
90
90
|
|
91
|
+
it "should understand this quarter" do
|
92
|
+
DateRange.parse(" this quarter ").should == DateRange.new(:from => EntryDate.new(:year => 2010, :month => 1), :to => nil, :format => :inclusive )
|
93
|
+
end
|
94
|
+
|
91
95
|
it "should understand this month" do
|
92
96
|
DateRange.parse(" this month ").should == DateRange.new(:from => EntryDate.new(:year => 2010, :month => 1), :to => nil, :format => :fixed_point )
|
93
97
|
end
|
@@ -102,12 +106,16 @@ module Appstats
|
|
102
106
|
|
103
107
|
end
|
104
108
|
|
105
|
-
describe "(last|previous) (year|month|week|day)" do
|
109
|
+
describe "(last|previous) (year|quarter|month|week|day)" do
|
106
110
|
|
107
111
|
it "should understand last year" do
|
108
112
|
DateRange.parse(" last year ").should == DateRange.new(:from => EntryDate.new(:year => 2009), :to => nil, :format => :fixed_point )
|
109
113
|
end
|
110
114
|
|
115
|
+
it "should understand last quarter" do
|
116
|
+
DateRange.parse(" last quarter ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 10), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
117
|
+
end
|
118
|
+
|
111
119
|
it "should understand last month" do
|
112
120
|
DateRange.parse(" last month ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 12), :to => nil, :format => :fixed_point )
|
113
121
|
end
|
@@ -138,7 +146,7 @@ module Appstats
|
|
138
146
|
|
139
147
|
end
|
140
148
|
|
141
|
-
describe "last X (year|month|week|day)s" do
|
149
|
+
describe "last X (year|quarter|month|week|day)s" do
|
142
150
|
|
143
151
|
it "should understand last X years" do
|
144
152
|
DateRange.parse(" last 1 year ").should == DateRange.new(:from => EntryDate.new(:year => 2010), :to => nil, :format => :inclusive )
|
@@ -146,6 +154,12 @@ module Appstats
|
|
146
154
|
DateRange.parse(" last 3 years ").should == DateRange.new(:from => EntryDate.new(:year => 2008), :to => nil, :format => :inclusive )
|
147
155
|
end
|
148
156
|
|
157
|
+
it "should understand last X quarters" do
|
158
|
+
DateRange.parse(" last 1 quarter ").should == DateRange.new(:from => EntryDate.new(:year => 2010, :month => 1), :to => nil, :format => :inclusive )
|
159
|
+
DateRange.parse(" last 2 quarters ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 10), :to => nil, :format => :inclusive )
|
160
|
+
DateRange.parse(" last 3 quarters ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 7), :to => nil, :format => :inclusive )
|
161
|
+
end
|
162
|
+
|
149
163
|
it "should understand last X months" do
|
150
164
|
DateRange.parse(" last 1 month ").should == DateRange.new(:from => EntryDate.new(:year => 2010, :month => 1), :to => nil, :format => :inclusive )
|
151
165
|
DateRange.parse(" last 2 months ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 12), :to => nil, :format => :inclusive )
|
@@ -166,7 +180,7 @@ module Appstats
|
|
166
180
|
|
167
181
|
end
|
168
182
|
|
169
|
-
describe "previous X (year|month|week|day)s" do
|
183
|
+
describe "previous X (year|quarter|month|week|day)s" do
|
170
184
|
|
171
185
|
it "should understand previous X years" do
|
172
186
|
DateRange.parse(" previous 1 year ").should == DateRange.new(:from => EntryDate.new(:year => 2009), :to => EntryDate.new(:year => 2009), :format => :inclusive )
|
@@ -174,6 +188,12 @@ module Appstats
|
|
174
188
|
DateRange.parse(" previous 3 years ").should == DateRange.new(:from => EntryDate.new(:year => 2007), :to => EntryDate.new(:year => 2009), :format => :inclusive )
|
175
189
|
end
|
176
190
|
|
191
|
+
it "should understand previous Y quarters" do
|
192
|
+
DateRange.parse(" previous 1 quarter ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 10), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
193
|
+
DateRange.parse(" previous 2 quarters ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 7), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
194
|
+
DateRange.parse(" previous 3 quarters ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 4), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
195
|
+
end
|
196
|
+
|
177
197
|
it "should understand previous Y months" do
|
178
198
|
DateRange.parse(" previous 1 month ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 12), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
179
199
|
DateRange.parse(" previous 2 months ").should == DateRange.new(:from => EntryDate.new(:year => 2009, :month => 11), :to => EntryDate.new(:year => 2009, :month => 12), :format => :inclusive )
|
data/spec/entry_date_spec.rb
CHANGED
@@ -75,6 +75,15 @@ module Appstats
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
describe "#end_of_quarter" do
|
79
|
+
it "should return the same 'level' data points" do
|
80
|
+
now = EntryDate.new(:year => 2010, :month => 1, :day => 5, :hour => 10)
|
81
|
+
expected = EntryDate.new(:year => 2010, :month => 3)
|
82
|
+
now.end_of_quarter.should == expected
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
78
87
|
describe "#parse" do
|
79
88
|
|
80
89
|
it "should deal with nil" do
|
@@ -118,6 +127,10 @@ module Appstats
|
|
118
127
|
EntryDate.parse("last year").should == EntryDate.new(:year => 2009)
|
119
128
|
end
|
120
129
|
|
130
|
+
it "should understand last quarter" do
|
131
|
+
EntryDate.parse("last quarter").should == EntryDate.new(:year => 2009, :month => 10)
|
132
|
+
end
|
133
|
+
|
121
134
|
it "should understand last month" do
|
122
135
|
EntryDate.parse("last month").should == EntryDate.new(:year => 2009, :month => 12)
|
123
136
|
end
|
@@ -134,6 +147,10 @@ module Appstats
|
|
134
147
|
EntryDate.parse("previous year").should == EntryDate.new(:year => 2009)
|
135
148
|
end
|
136
149
|
|
150
|
+
it "should understand previous quarter" do
|
151
|
+
EntryDate.parse("previous quarter").should == EntryDate.new(:year => 2009, :month => 10)
|
152
|
+
end
|
153
|
+
|
137
154
|
it "should understand previous month" do
|
138
155
|
EntryDate.parse("previous month").should == EntryDate.new(:year => 2009, :month => 12)
|
139
156
|
end
|
@@ -150,6 +167,10 @@ module Appstats
|
|
150
167
|
EntryDate.parse("this year").should == EntryDate.new(:year => 2010)
|
151
168
|
end
|
152
169
|
|
170
|
+
it "should understand this quarter" do
|
171
|
+
EntryDate.parse("this quarter").should == EntryDate.new(:year => 2010, :month => 1)
|
172
|
+
end
|
173
|
+
|
153
174
|
it "should understand this month" do
|
154
175
|
EntryDate.parse("this month").should == EntryDate.new(:year => 2010, :month => 1)
|
155
176
|
end
|
@@ -168,6 +189,13 @@ module Appstats
|
|
168
189
|
EntryDate.parse("last 3 years").should == EntryDate.new(:year => 2008)
|
169
190
|
end
|
170
191
|
|
192
|
+
it "should understand last X quarters" do
|
193
|
+
EntryDate.parse("last 1 quarter").should == EntryDate.new(:year => 2010, :month => 1)
|
194
|
+
EntryDate.parse("last 2 quarters").should == EntryDate.new(:year => 2009, :month => 10)
|
195
|
+
EntryDate.parse("last 3 quarters").should == EntryDate.new(:year => 2009, :month => 7)
|
196
|
+
end
|
197
|
+
|
198
|
+
|
171
199
|
it "should understand last X months" do
|
172
200
|
EntryDate.parse("last 1 month").should == EntryDate.new(:year => 2010, :month => 1)
|
173
201
|
EntryDate.parse("last 2 months").should == EntryDate.new(:year => 2009, :month => 12)
|
@@ -192,6 +220,12 @@ module Appstats
|
|
192
220
|
EntryDate.parse("previous 3 years").should == EntryDate.new(:year => 2007)
|
193
221
|
end
|
194
222
|
|
223
|
+
it "should understand previous X quarters" do
|
224
|
+
EntryDate.parse("previous 1 quarter").should == EntryDate.new(:year => 2009, :month => 10)
|
225
|
+
EntryDate.parse("previous 2 quarters").should == EntryDate.new(:year => 2009, :month => 7)
|
226
|
+
EntryDate.parse("previous 3 quarters").should == EntryDate.new(:year => 2009, :month => 4)
|
227
|
+
end
|
228
|
+
|
195
229
|
it "should understand previous X months" do
|
196
230
|
EntryDate.parse("previous 1 month").should == EntryDate.new(:year => 2009, :month => 12)
|
197
231
|
EntryDate.parse("previous 2 months").should == EntryDate.new(:year => 2009, :month => 11)
|