appstats 0.1.0 → 0.3.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.
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