daily 0.0.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/MIT-LICENSE +20 -0
- data/README.mdown +46 -0
- data/Rakefile +7 -0
- data/app/assets/images/rails.png +0 -0
- data/app/assets/javascripts/application.js +9 -0
- data/app/assets/stylesheets/application.css +16 -0
- data/app/assets/stylesheets/bootstrap.css +2467 -0
- data/app/controllers/application_controller.rb +14 -0
- data/app/controllers/files_controller.rb +5 -0
- data/app/controllers/main_controller.rb +5 -0
- data/app/controllers/reports_controller.rb +35 -0
- data/app/controllers/tables_controller.rb +20 -0
- data/app/controllers/users_controller.rb +28 -0
- data/app/formatters/html_formatter.rb +65 -0
- data/app/formatters/json_formatter.rb +81 -0
- data/app/helpers/application_helper.rb +85 -0
- data/app/jobs/generate_report_job.rb +13 -0
- data/app/models/report.rb +112 -0
- data/app/models/table.rb +65 -0
- data/app/models/user.rb +20 -0
- data/app/transforms/column_filter.rb +7 -0
- data/app/transforms/moving_average.rb +119 -0
- data/app/transforms/transform.rb +32 -0
- data/app/views/devise/sessions/new.erb +15 -0
- data/app/views/layouts/application.html.erb +40 -0
- data/app/views/main/home.erb +4 -0
- data/app/views/reports/_form.erb +14 -0
- data/app/views/reports/_list.erb +9 -0
- data/app/views/reports/edit.erb +2 -0
- data/app/views/reports/index.erb +3 -0
- data/app/views/reports/new.erb +2 -0
- data/app/views/reports/show.erb +23 -0
- data/app/views/tables/_form.erb +8 -0
- data/app/views/tables/_list.erb +9 -0
- data/app/views/tables/edit.erb +2 -0
- data/app/views/tables/index.erb +3 -0
- data/app/views/tables/new.erb +2 -0
- data/app/views/tables/show.erb +35 -0
- data/app/views/users/_form.erb +5 -0
- data/app/views/users/_items.erb +8 -0
- data/app/views/users/_list.erb +9 -0
- data/app/views/users/edit.erb +2 -0
- data/app/views/users/index.erb +3 -0
- data/app/views/users/new.erb +2 -0
- data/app/views/users/show.erb +4 -0
- data/config/application.rb +52 -0
- data/config/authorization_rules.rb +49 -0
- data/config/boot.rb +6 -0
- data/config/daily.example.yml +19 -0
- data/config/daily.yml +27 -0
- data/config/database.yml +25 -0
- data/config/environment.rb +5 -0
- data/config/environments/development.rb +32 -0
- data/config/environments/production.rb +63 -0
- data/config/environments/test.rb +39 -0
- data/config/initializers/backtrace_silencers.rb +7 -0
- data/config/initializers/devise.rb +212 -0
- data/config/initializers/inflections.rb +10 -0
- data/config/initializers/mime_types.rb +5 -0
- data/config/initializers/ruport.rb +5 -0
- data/config/initializers/secret_token.rb +7 -0
- data/config/initializers/session_store.rb +8 -0
- data/config/initializers/simple_form.rb +113 -0
- data/config/initializers/wrap_parameters.rb +14 -0
- data/config/locales/devise.en.yml +58 -0
- data/config/locales/en.yml +5 -0
- data/config/locales/simple_form.en.yml +24 -0
- data/config/routes.rb +29 -0
- data/db/development.sqlite3 +0 -0
- data/db/migrate/20111109081414_devise_create_users.rb +28 -0
- data/db/migrate/20111111165640_create_tables.rb +14 -0
- data/db/migrate/20111112022333_add_name_to_tables.rb +9 -0
- data/db/migrate/20111112170802_add_reports.rb +15 -0
- data/db/migrate/20111113000026_add_guid_to_tables.rb +9 -0
- data/db/migrate/20111113073326_add_times_to_reports.rb +11 -0
- data/db/migrate/20111113075747_add_times_to_tables.rb +9 -0
- data/db/migrate/20111114041729_create_delayed_jobs.rb +21 -0
- data/db/migrate/20111114053016_add_report_to_delayed_jobs.rb +11 -0
- data/db/migrate/20111115014959_add_admin_to_users.rb +9 -0
- data/db/migrate/20111127065357_add_column_names_to_table.rb +9 -0
- data/db/migrate/20111203020425_add_transform_to_table.rb +16 -0
- data/db/migrate/20111214020029_add_formatter_data_to_reports.rb +8 -0
- data/db/schema.rb +80 -0
- data/db/seeds.rb +7 -0
- data/db/test.sqlite3 +0 -0
- data/lib/daily.rb +9 -0
- data/lib/daily/daily_config.rb +51 -0
- data/lib/daily/engine.rb +4 -0
- data/lib/daily/has_data.rb +52 -0
- data/lib/daily/shared_behaviors.rb +65 -0
- data/lib/daily/version.rb +3 -0
- data/lib/tasks/user.rake +24 -0
- metadata +454 -0
data/app/models/table.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
class Table < ActiveRecord::Base
|
2
|
+
include SharedBehaviors
|
3
|
+
|
4
|
+
belongs_to :user
|
5
|
+
has_many :reports
|
6
|
+
|
7
|
+
generate_guid :guid
|
8
|
+
|
9
|
+
validates_presence_of :user
|
10
|
+
validates_unique_presence_of :name
|
11
|
+
validates_stripped_presence_of :data
|
12
|
+
validates_stripped_presence_of :data_type
|
13
|
+
|
14
|
+
validate :data_type_known
|
15
|
+
|
16
|
+
serialize :column_names, Array
|
17
|
+
|
18
|
+
def sql?
|
19
|
+
data_type == "sql"
|
20
|
+
end
|
21
|
+
|
22
|
+
def result
|
23
|
+
fetch
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch
|
27
|
+
time = Time.now.to_i
|
28
|
+
|
29
|
+
out = fetch_data
|
30
|
+
out = apply_transform(out)
|
31
|
+
|
32
|
+
atts = {}
|
33
|
+
atts[:fetch_time_in_seconds] = Time.now.to_i - time
|
34
|
+
atts[:column_names] = out.column_names
|
35
|
+
self.attributes = atts
|
36
|
+
save unless new_record?
|
37
|
+
|
38
|
+
out
|
39
|
+
end
|
40
|
+
|
41
|
+
def test
|
42
|
+
# ouputs html of the table
|
43
|
+
begin
|
44
|
+
fetch.to_html.html_safe
|
45
|
+
rescue => e
|
46
|
+
out = "#{e.message}"
|
47
|
+
out += "\n\nTrace shown in development environment:\n#{e.backtrace.join("\n")}" if Rails.env.development?
|
48
|
+
out.gsub("\n", "<br/>").html_safe
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
def data_type_known
|
54
|
+
return if data_type.blank?
|
55
|
+
unless sql?
|
56
|
+
errors.add(:data_type, "is not known")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_data
|
61
|
+
return Ruport::Query.new(data).result if sql?
|
62
|
+
Ruport::Data::Table.new
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
# Include default devise modules. Others available are:
|
3
|
+
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
|
4
|
+
devise :database_authenticatable, :registerable,
|
5
|
+
:recoverable, :rememberable, :trackable, :validatable
|
6
|
+
|
7
|
+
# Setup accessible (or protected) attributes for your model
|
8
|
+
attr_accessible :email, :password, :password_confirmation, :remember_me
|
9
|
+
|
10
|
+
has_many :tables
|
11
|
+
has_many :reports
|
12
|
+
|
13
|
+
def role_symbols
|
14
|
+
return [] if new_record?
|
15
|
+
|
16
|
+
out = [:user]
|
17
|
+
out << :admin if admin?
|
18
|
+
out
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
class MovingAverage < Transform
|
2
|
+
def self.form_keys
|
3
|
+
[:columns, :days, :date]
|
4
|
+
end
|
5
|
+
|
6
|
+
def num_days
|
7
|
+
val = setting(:days).to_i
|
8
|
+
val <= 0 ? 7 : val
|
9
|
+
end
|
10
|
+
|
11
|
+
def date_column
|
12
|
+
setting(:date) || default_date_column || first_date_column
|
13
|
+
end
|
14
|
+
|
15
|
+
def result
|
16
|
+
cols = self.columns
|
17
|
+
datecol = self.date_column
|
18
|
+
daycount = self.num_days
|
19
|
+
|
20
|
+
avg_cols = cols.collect { |col| "#{col}_#{daycount}" }
|
21
|
+
|
22
|
+
out = Ruport::Data::Table.new( :column_names => [datecol] + cols + avg_cols)
|
23
|
+
|
24
|
+
data = day_data
|
25
|
+
days = data["days"]
|
26
|
+
first = data["earliest"]
|
27
|
+
latest = data["latest"]
|
28
|
+
|
29
|
+
current = first
|
30
|
+
while current <= latest do
|
31
|
+
row = {}
|
32
|
+
row[datecol] = current
|
33
|
+
|
34
|
+
cols.each do |col|
|
35
|
+
# this cols value for the day
|
36
|
+
value = 0
|
37
|
+
value = days[current][col].to_f if days[current]
|
38
|
+
|
39
|
+
# add them up
|
40
|
+
sum = 0
|
41
|
+
count = 0
|
42
|
+
check = current - daycount + 1
|
43
|
+
while check <= current
|
44
|
+
count += 1 unless check < first
|
45
|
+
sum += days[check][col].to_f if days[check]
|
46
|
+
check += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
row["#{col}_#{daycount}"] = sum / count
|
50
|
+
row["#{col}"] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
out << row
|
54
|
+
current += 1
|
55
|
+
end
|
56
|
+
|
57
|
+
out
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def self.to_day(time_or_date)
|
63
|
+
time = Time.zone.parse(time_or_date.strftime("%Y-%m-%d %H:%M:%S %z"))
|
64
|
+
Date.new(time.year, time.month, time.day)
|
65
|
+
end
|
66
|
+
|
67
|
+
def day_data
|
68
|
+
earliest = Date.new(2032,1,1)
|
69
|
+
latest = Date.new(1492,1,1)
|
70
|
+
datenum = column_name_hash[date_column]
|
71
|
+
cols = self.columns
|
72
|
+
|
73
|
+
days = {}
|
74
|
+
|
75
|
+
table.each do |row|
|
76
|
+
time = row[datenum]
|
77
|
+
day = self.class.to_day(time)
|
78
|
+
latest = day if day > latest
|
79
|
+
earliest = day if day < earliest
|
80
|
+
|
81
|
+
days[day] ||= {}
|
82
|
+
cols.each do |col|
|
83
|
+
index = column_name_hash[col]
|
84
|
+
if index
|
85
|
+
days[day][col] ||= 0
|
86
|
+
days[day][col] += row[index].to_f
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
{"days" => days, "earliest" => earliest, "latest" => latest}
|
92
|
+
end
|
93
|
+
|
94
|
+
def column_name_hash
|
95
|
+
return @column_name_hash if @column_name_hash
|
96
|
+
@column_name_hash = {}
|
97
|
+
table.column_names.each_with_index do |name, i|
|
98
|
+
@column_name_hash[name.to_s] = i
|
99
|
+
end
|
100
|
+
@column_name_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
def default_date_column
|
104
|
+
["created_at", "updated_at", "state_changed_at"].each do |col|
|
105
|
+
return col if column_name_hash[col]
|
106
|
+
end
|
107
|
+
return nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def first_date_column
|
111
|
+
return nil if table.size == 0
|
112
|
+
table[0].each_with_index do |val, i|
|
113
|
+
if val.is_a? Date or val.is_a? Time
|
114
|
+
return table.column_names[i]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class Transform
|
2
|
+
def self.transforms
|
3
|
+
[ColumnFilter, MovingAverage]
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.display_name
|
7
|
+
name.demodulize.underscore.humanize.titleize
|
8
|
+
end
|
9
|
+
def self.form_keys
|
10
|
+
[:columns] # override to get more / different
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :table
|
14
|
+
attr_accessor :settings
|
15
|
+
def initialize(table, settings)
|
16
|
+
self.table = table
|
17
|
+
self.settings = (settings || {}).symbolize_keys
|
18
|
+
end
|
19
|
+
|
20
|
+
def setting(key, default = nil)
|
21
|
+
val = settings[key.to_sym]
|
22
|
+
val.blank? ? default : val
|
23
|
+
end
|
24
|
+
|
25
|
+
def columns
|
26
|
+
setting(:columns, []).collect(&:to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
def result
|
30
|
+
raise("Transforms must override result")
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h2>Sign in</h2>
|
2
|
+
|
3
|
+
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
|
4
|
+
<div><%= f.label :email %><br />
|
5
|
+
<%= f.email_field :email %></div>
|
6
|
+
|
7
|
+
<div><%= f.label :password %><br />
|
8
|
+
<%= f.password_field :password %></div>
|
9
|
+
|
10
|
+
<% if devise_mapping.rememberable? -%>
|
11
|
+
<div><%= f.check_box :remember_me %> <%= f.label :remember_me %></div>
|
12
|
+
<% end -%>
|
13
|
+
|
14
|
+
<div><%= f.submit "Sign in" %></div>
|
15
|
+
<% end %>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= page_title %></title>
|
5
|
+
<%= stylesheet_link_tag "application" %>
|
6
|
+
<%= javascript_include_tag "application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<div class="topbar">
|
11
|
+
<div class="fill">
|
12
|
+
<div class="container">
|
13
|
+
<a class="brand" href="<%= current_user ? user_root_path : root_path %>">Daily</a>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="container">
|
19
|
+
<% if header_title %>
|
20
|
+
<h1><%= header_title %></h1>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if notice %>
|
24
|
+
<div class="alert-message success">
|
25
|
+
<p><%= notice %></p>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
|
29
|
+
<% if alert %>
|
30
|
+
<div class="alert-message error">
|
31
|
+
<p><%= alert %></p>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<%= yield %>
|
36
|
+
|
37
|
+
</div>
|
38
|
+
|
39
|
+
</body>
|
40
|
+
</html>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<h3>Table: <%= link_to @report.table.name, table_path(@report.table) %></h3>
|
2
|
+
|
3
|
+
<%= simple_form_for [@report.table, @report] do |f| %>
|
4
|
+
<%= f.input :name %>
|
5
|
+
<%= f.input :filename unless @report.new_record? %>
|
6
|
+
|
7
|
+
<%= f.input :formatter, :collection => Report.formatters %>
|
8
|
+
<%= f.input :formatter_json, :as => :text %>
|
9
|
+
|
10
|
+
<%= f.input :transform, :collection => Transform.transforms, :label_method => :display_name, :value_method => :name %>
|
11
|
+
<%= f.input :transform_json, :as => :text %>
|
12
|
+
|
13
|
+
<%= f.button :submit %>
|
14
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<% title @report.name %>
|
2
|
+
<h3>Table: <%= link_to @report.table.name, table_path(@report.table) %></h3>
|
3
|
+
|
4
|
+
<% if @report.file_exists? %>
|
5
|
+
<h5>URL: <%= link_to @report.url(root_url), @report.url(root_url) %></h5>
|
6
|
+
<% else %>
|
7
|
+
<h5>URL: <%= @report.url(root_url) %></h5>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<h6>User: <%= "#{@report.user.email}" %></h6>
|
11
|
+
<h6>Running time: <%= report_time_run(@report) %></h6>
|
12
|
+
<h6>Last updated: <%= report_time_ago(@report) %></h6>
|
13
|
+
<h6>Next updated: <%= report_time_next(@report) %></h6>
|
14
|
+
<%= button_to "Queue Update Now", generate_table_report_path(@report.table, @report) if permitted_to? :generate, @report %>
|
15
|
+
|
16
|
+
<%= content_tag(:p, link_to("Edit", edit_table_report_path(@report.table, @report))) if permitted_to? :edit, @report %>
|
17
|
+
|
18
|
+
<%= formatter_display(@report) %>
|
19
|
+
|
20
|
+
<%= transform_display(@report) %>
|
21
|
+
|
22
|
+
<hr/>
|
23
|
+
<%= report_error_html(@report) %>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<%= simple_form_for @table do |f| %>
|
2
|
+
<%= f.input :name %>
|
3
|
+
<%= f.input :data %>
|
4
|
+
|
5
|
+
<%= f.input :transform, :collection => Transform.transforms, :label_method => :display_name, :value_method => :name %>
|
6
|
+
<%= f.input :transform_json, :as => :text %>
|
7
|
+
<%= f.button :submit %>
|
8
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<% title @table.name %>
|
2
|
+
|
3
|
+
<h3>Reports</h3>
|
4
|
+
<p><%= link_to "New report from this table", new_table_report_path(@table) if permitted_to? :report, @table %></p>
|
5
|
+
<%= render "reports/list", :list => @table.reports %>
|
6
|
+
|
7
|
+
<hr/>
|
8
|
+
|
9
|
+
<h6>User: <%= "#{@table.user.email}" %></h6>
|
10
|
+
<h6>Type: <%= "#{@table.data_type}" %></h6>
|
11
|
+
<h6>Running time: <%= table_time_run(@table) %></h6>
|
12
|
+
<%= content_tag(:p, link_to("Edit", edit_table_path(@table))) if permitted_to? :edit, @table %>
|
13
|
+
|
14
|
+
<% if @test_html %>
|
15
|
+
<p><%= link_to "Hide Table Output", table_path(@table) %></p>
|
16
|
+
<%= @test_html %>
|
17
|
+
<% else %>
|
18
|
+
<p><%= link_to "See Table Output", table_path(@table, :test => true) %></p>
|
19
|
+
|
20
|
+
<% if @table.column_names and @table.column_names.size > 0 %>
|
21
|
+
<table>
|
22
|
+
<tr>
|
23
|
+
<% @table.column_names.each do |name| %>
|
24
|
+
<th><%= name %></th>
|
25
|
+
<% end %>
|
26
|
+
</tr>
|
27
|
+
</table>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
<hr/>
|
32
|
+
|
33
|
+
<%= simple_format(@table.data) %>
|
34
|
+
|
35
|
+
<%= transform_display(@table) %>
|