appstats 0.1.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- appstats (0.1.0)
4
+ appstats (0.3.1)
5
5
  daemons
6
6
  net-scp
7
7
  rails (>= 2.3.0)
@@ -0,0 +1,14 @@
1
+ class CreateAppstatsActions < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :appstats_actions do |t|
4
+ t.string :name
5
+ t.string :plural_name
6
+ t.string :status
7
+ t.timestamps
8
+ end
9
+ end
10
+
11
+ def self.down
12
+ drop_table :appstats_actions
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ class AlignEntryTimeNames < ActiveRecord::Migration
2
+ def self.up
3
+ remove_index :appstats_entries, :name => "index_entries_by_minute"
4
+ rename_column :appstats_entries, :minute, :min
5
+ rename_column :appstats_entries, :second, :sec
6
+ add_index :appstats_entries, [:year,:month,:day,:hour,:min], :name => "index_entries_by_minute"
7
+ end
8
+
9
+ def self.down
10
+ remove_index :appstats_entries, :name => "index_entries_by_minute"
11
+ rename_column :appstats_entries, :min, :minute
12
+ rename_column :appstats_entries, :sec, :second
13
+ add_index :appstats_entries, [:year,:month,:day,:hour,:minute], :name => "index_entries_by_minute"
14
+ end
15
+ end
data/db/schema.rb CHANGED
@@ -10,7 +10,15 @@
10
10
  #
11
11
  # It's strongly recommended to check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(:version => 20110207200431) do
13
+ ActiveRecord::Schema.define(:version => 20110208210921) do
14
+
15
+ create_table "appstats_actions", :force => true do |t|
16
+ t.string "name"
17
+ t.string "plural_name"
18
+ t.string "status"
19
+ t.datetime "created_at"
20
+ t.datetime "updated_at"
21
+ end
14
22
 
15
23
  create_table "appstats_contexts", :force => true do |t|
16
24
  t.string "context_key"
@@ -37,12 +45,12 @@ ActiveRecord::Schema.define(:version => 20110207200431) do
37
45
  t.integer "month"
38
46
  t.integer "day"
39
47
  t.integer "hour"
40
- t.integer "minute"
41
- t.integer "second"
48
+ t.integer "min"
49
+ t.integer "sec"
42
50
  end
43
51
 
44
52
  add_index "appstats_entries", ["action"], :name => "index_appstats_entries_on_action"
45
- add_index "appstats_entries", ["year", "month", "day", "hour", "minute"], :name => "index_entries_by_minute"
53
+ add_index "appstats_entries", ["year", "month", "day", "hour", "min"], :name => "index_entries_by_minute"
46
54
  add_index "appstats_entries", ["year", "month", "day", "hour"], :name => "index_entries_by_hour"
47
55
  add_index "appstats_entries", ["year", "month", "day"], :name => "index_entries_by_day"
48
56
  add_index "appstats_entries", ["year", "month"], :name => "index_entries_by_month"
data/lib/appstats.rb CHANGED
@@ -2,10 +2,14 @@ require 'rubygems'
2
2
  require 'active_record'
3
3
  require "#{File.dirname(__FILE__)}/appstats/code_injections"
4
4
  require "#{File.dirname(__FILE__)}/appstats/entry"
5
+ require "#{File.dirname(__FILE__)}/appstats/entry_date"
6
+ require "#{File.dirname(__FILE__)}/appstats/date_range"
7
+ require "#{File.dirname(__FILE__)}/appstats/action"
5
8
  require "#{File.dirname(__FILE__)}/appstats/context"
6
9
  require "#{File.dirname(__FILE__)}/appstats/tasks"
7
10
  require "#{File.dirname(__FILE__)}/appstats/logger"
8
11
  require "#{File.dirname(__FILE__)}/appstats/log_collector"
12
+ require "#{File.dirname(__FILE__)}/appstats/query"
9
13
 
10
14
  # required in the appstats.gemspec
11
15
  # require "#{File.dirname(__FILE__)}/appstats/version"
@@ -0,0 +1,19 @@
1
+
2
+ module Appstats
3
+ class Action < ActiveRecord::Base
4
+ set_table_name "appstats_actions"
5
+
6
+ attr_accessible :name, :plural_name, :status
7
+
8
+ def self.update_actions
9
+ sql = "select distinct(action) from appstats_entries where action not in (select name from appstats_actions)"
10
+ count = 0
11
+ ActiveRecord::Base.connection.execute(sql).each do |row|
12
+ Appstats::Action.create(:name => row[0], :plural_name => row[0].pluralize, :status => 'derived')
13
+ count += 1
14
+ end
15
+ count
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,159 @@
1
+
2
+ module Appstats
3
+ class DateRange
4
+
5
+ attr_accessor :from, :to, :format
6
+
7
+ def initialize(data = {})
8
+ @from = data[:from]
9
+ @to = data[:to]
10
+ @format = data[:format] || :inclusive
11
+ end
12
+
13
+ def from_to_s
14
+ return nil if @from.nil?
15
+ mode = @format == :inclusive ? :start : :end
16
+ @from.to_time(mode).strftime('%Y-%m-%d %H:%M:%S')
17
+ end
18
+
19
+ def to_to_s
20
+ return nil if @to.nil?
21
+ mode = @format == :exclusive ? :start : :end
22
+ @to.to_time(mode).strftime('%Y-%m-%d %H:%M:%S')
23
+ end
24
+
25
+ def to_sql
26
+ return "1=1" if @from.nil? && @to.nil?
27
+
28
+ if !@from.nil? && @to.nil?
29
+ return case @format
30
+ when :inclusive then
31
+ "occurred_at >= '#{from_to_s}'"
32
+ when :exclusive then
33
+ "occurred_at > '#{from_to_s}'"
34
+ when :fixed_point then
35
+ answer = "("
36
+ [:year,:month,:day,:hour,:min,:sec].each do |t|
37
+ next if from.send(t).nil?
38
+ answer += " and " unless answer.size == 1
39
+ answer += "#{t}=#{from.send(t)}"
40
+ end
41
+ answer += ")"
42
+ answer
43
+ end
44
+ elsif @from.nil? && !@to.nil?
45
+ return case @format
46
+ when :inclusive then "occurred_at <= '#{to_to_s}'"
47
+ when :exclusive then "occurred_at < '#{to_to_s}'"
48
+ else "1=1"
49
+ end
50
+ else
51
+ return case @format
52
+ when :inclusive then "(occurred_at >= '#{from_to_s}' and occurred_at <= '#{to_to_s}')"
53
+ when :exclusive then "(occurred_at > '#{from_to_s}' and occurred_at < '#{to_to_s}')"
54
+ else "1=1"
55
+ end
56
+ end
57
+
58
+
59
+ end
60
+
61
+ def self.parse(raw_input)
62
+ range = DateRange.new
63
+ return range if raw_input.nil? || raw_input == ''
64
+ input = raw_input.strip
65
+
66
+ m = input.match(/^today|yesterday|YTD|ytd$/)
67
+ unless m.nil?
68
+ range.from = EntryDate.parse(input)
69
+ range.format = :fixed_point
70
+ end
71
+
72
+ m = input.match(/^between\s*(.*)\s*and\s*(.*)$/)
73
+ unless m.nil?
74
+ range.from = EntryDate.parse(m[1])
75
+ range.to = EntryDate.parse(m[2])
76
+ return range
77
+ end
78
+
79
+ m = input.match(/^(in|on)\s*(.*)$/)
80
+ unless m.nil?
81
+ range.from = EntryDate.parse(m[2])
82
+ range.format = :fixed_point
83
+ return range
84
+ end
85
+
86
+ m = input.match(/^before\s*(.*)$/)
87
+ unless m.nil?
88
+ range.to = EntryDate.parse(m[1])
89
+ range.format = :exclusive
90
+ return range
91
+ end
92
+
93
+ m = input.match(/^after\s*(.*)$/)
94
+ unless m.nil?
95
+ range.from = EntryDate.parse(m[1])
96
+ range.format = :exclusive
97
+ return range
98
+ end
99
+
100
+ m = input.match(/^since\s*(.*)$/)
101
+ unless m.nil?
102
+ range.from = EntryDate.parse(m[1])
103
+ range.format = :inclusive
104
+ return range
105
+ end
106
+
107
+
108
+ m = input.match(/^this\s*(year|month|week|day)$/)
109
+ unless m.nil?
110
+ range.from = EntryDate.parse(input)
111
+ range.format = m[1] == "week" ? :inclusive : :fixed_point
112
+ return range
113
+ end
114
+
115
+ m = input.match(/^(last|previous)\s*(year|month|week|day)$/)
116
+ unless m.nil?
117
+ range.from = EntryDate.parse(input)
118
+ if m[2] == "week"
119
+ range.to = range.from.end_of_week
120
+ range.format = :inclusive
121
+ else
122
+ range.format = :fixed_point
123
+ end
124
+ return range
125
+ end
126
+
127
+ m = input.match(/^last\s*(.+)\s*(year|years|month|months|week|weeks|day|days)$/)
128
+ unless m.nil?
129
+ range.from = EntryDate.parse(input)
130
+ range.format = :inclusive
131
+ return range
132
+ end
133
+
134
+ m = input.match(/^previous\s*(.+)\s*(year|month|week|day)s?$/)
135
+ unless m.nil?
136
+ range.from = EntryDate.parse(input)
137
+ to = EntryDate.parse("last #{m[2]}")
138
+ to = to.end_of_week if m[2] == "week"
139
+ range.to = to
140
+ range.format = :inclusive
141
+ return range
142
+ end
143
+
144
+ range
145
+ end
146
+
147
+ def ==(o)
148
+ o.class == self.class && o.send(:state) == state
149
+ end
150
+ alias_method :eql?, :==
151
+
152
+ private
153
+
154
+ def state
155
+ [@from, @to, @format]
156
+ end
157
+
158
+ end
159
+ end
@@ -17,15 +17,15 @@ module Appstats
17
17
  self[:month] = nil
18
18
  self[:day] = nil
19
19
  self[:hour] = nil
20
- self[:minute] = nil
21
- self[:second] = nil
20
+ self[:min] = nil
21
+ self[:sec] = nil
22
22
  else
23
23
  self[:year] = value.year
24
24
  self[:month] = value.month
25
25
  self[:day] = value.day
26
26
  self[:hour] = value.hour
27
- self[:minute] = value.min
28
- self[:second] = value.sec
27
+ self[:min] = value.min
28
+ self[:sec] = value.sec
29
29
  end
30
30
  end
31
31
 
@@ -0,0 +1,156 @@
1
+
2
+ module Appstats
3
+ class EntryDate
4
+
5
+ attr_accessor :year, :month, :day, :hour, :min, :sec
6
+
7
+ def initialize(data = {})
8
+ @year = data[:year]
9
+ @month = data[:month]
10
+ @day = data[:day]
11
+ @hour = data[:hour]
12
+ @min = data[:min]
13
+ @sec = data[:sec]
14
+ end
15
+
16
+ def end_of_week
17
+ week = self.dup
18
+ t = to_time.end_of_week
19
+ week.year = t.year
20
+ week.month = t.month
21
+ week.day = t.day
22
+ week
23
+ end
24
+
25
+ def to_time(mode = :start)
26
+ return Time.now if @year.nil?
27
+ t = Time.parse("#{@year}-#{@month||'01'}-#{@day||'01'} #{@hour||'00'}:#{@min||'00'}:#{@sec||'00'}")
28
+
29
+ if mode == :end
30
+ t = t.end_of_year if @month.nil?
31
+ t = t.end_of_month if @day.nil?
32
+ t = t.end_of_day if @hour.nil?
33
+ end
34
+ t
35
+ end
36
+
37
+ def to_s
38
+ s = ""
39
+ return s if @year.nil?
40
+ s += "#{@year}"
41
+
42
+ return s if @month.nil?
43
+ s += "-#{@month.to_s.rjust(2,'0')}"
44
+
45
+ return s if @day.nil?
46
+ s += "-#{@day.to_s.rjust(2,'0')}"
47
+
48
+ return s if @hour.nil?
49
+ s += " #{@hour.to_s.rjust(2,'0')}"
50
+
51
+ return s if @min.nil?
52
+ s += ":#{@min.to_s.rjust(2,'0')}"
53
+
54
+ return s if @sec.nil?
55
+ s += ":#{@sec.to_s.rjust(2,'0')}"
56
+
57
+ s
58
+ end
59
+
60
+ def self.parse(raw_input)
61
+ date = EntryDate.new
62
+ return date if raw_input.nil? || raw_input == ''
63
+ input = raw_input.strip
64
+
65
+ if input.match(/^\d*$/) # year
66
+ date.year = input.to_i
67
+ return date
68
+ end
69
+
70
+ t = Time.now
71
+ t_parts = nil
72
+
73
+ if input.match(/^YTD|ytd$/)
74
+ t_parts = [:year]
75
+ elsif input.match(/^today$/)
76
+ t_parts = [:year,:month,:day]
77
+ elsif input.match(/^yesterday$/)
78
+ t -= 1.day
79
+ t_parts = [:year,:month,:day]
80
+ elsif input.match(/^this year$/)
81
+ t_parts = [:year]
82
+ elsif input.match(/^this month$/)
83
+ t_parts = [:year,:month]
84
+ elsif input.match(/^this week$/)
85
+ t = t.beginning_of_week
86
+ t_parts = [:year,:month,:day]
87
+ elsif input.match(/^this day$/)
88
+ t_parts = [:year,:month,:day]
89
+ elsif input.match(/^(.*),[^\d]*(\d*)$/) # month, year
90
+ t = Time.parse(input)
91
+ t_parts = [:year,:month]
92
+ elsif input.match(/^(\d*)-(\d*)-(\d*)$/) # YYYY-mm-dd
93
+ t = Time.parse(input)
94
+ t_parts = [:year,:month,:day]
95
+ end
96
+
97
+ m = input.match(/^(last|previous)\s*(\d*)\s*years?$/)
98
+ if m
99
+ t -= last_date_offset(m).year
100
+ t_parts = [:year]
101
+ end
102
+
103
+ m = input.match(/^(last|previous)\s*(\d*)\s*months?$/)
104
+ if m
105
+ t -= last_date_offset(m).month
106
+ t_parts = [:year,:month]
107
+ end
108
+
109
+ m = input.match(/^(last|previous)\s*(\d*)\s*weeks?$/)
110
+ if m
111
+ t = (t - last_date_offset(m).week).beginning_of_week
112
+ t_parts = [:year,:month,:day]
113
+ end
114
+
115
+ m = input.match(/^(last|previous)\s*(\d*)\s*days?$/)
116
+ if m
117
+ t -= last_date_offset(m).day
118
+ t_parts = [:year,:month,:day]
119
+ end
120
+
121
+ unless t_parts.nil?
122
+ t_parts.each do |label|
123
+ date.send("#{label}=",t.send(label))
124
+ end
125
+ return date
126
+ end
127
+
128
+
129
+ begin
130
+ t = Time.parse(input)
131
+ return EntryDate.new(:year => t.year, :month => t.month, :day => t.day, :hour => t.hour, :min => t.min, :sec => t.sec)
132
+ rescue
133
+ return EntryDate.new
134
+ end
135
+ end
136
+
137
+ def ==(o)
138
+ o.class == self.class && o.send(:state) == state
139
+ end
140
+ alias_method :eql?, :==
141
+
142
+ private
143
+
144
+ def state
145
+ [@year, @month, @day, @hour, @min, @sec]
146
+ end
147
+
148
+ # (last|previous) (\d*)
149
+ def self.last_date_offset(match)
150
+ offset = match[1] == "last" ? -1 : 0
151
+ amount = match[2] == "" ? 1 : match[2].to_i + offset
152
+ amount
153
+ end
154
+
155
+ end
156
+ end