query_report 1.0.4 → 1.0.5
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/README.md +4 -3
- data/app/helpers/query_report_link_helper.rb +4 -0
- data/app/mailers/report_mailer.rb +10 -0
- data/app/views/query_report/_content.html.erb +9 -0
- data/app/views/query_report/_links.html.erb +2 -1
- data/app/views/query_report/_list.html.erb +15 -0
- data/app/views/query_report/_record_footer.html.erb +9 -0
- data/app/views/query_report/_records.html.erb +3 -2
- data/app/views/query_report/_search.html.erb +10 -7
- data/app/views/query_report/list.html.erb +1 -14
- data/app/views/query_report/list.js.erb +9 -0
- data/app/views/report_mailer/send_report.html.erb +1 -0
- data/config/locales/query_report.yml +3 -1
- data/lib/query_report.rb +7 -3
- data/lib/query_report/column.rb +36 -0
- data/lib/query_report/comparator.rb +2 -2
- data/lib/query_report/config.rb +4 -0
- data/lib/query_report/helper.rb +20 -2
- data/lib/query_report/report.rb +4 -0
- data/lib/query_report/report_pdf.rb +14 -6
- data/lib/query_report/version.rb +1 -1
- data/test/dummy/app/controllers/invoices_controller.rb +19 -19
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/schema.rb +2 -2
- data/test/dummy/db/seed.rb +0 -1
- data/test/dummy/log/development.log +4357 -0
- data/test/dummy/log/test.log +148 -0
- data/test/functional/report_mailer_test.rb +7 -0
- metadata +14 -6
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[Query report](http://ashrafuzzaman.github.io/query_report/) By [Ashrafuzzaman](http://
|
1
|
+
[Query report](http://ashrafuzzaman.github.io/query_report/) By [Ashrafuzzaman](http://ashrafuzzaman.github.io/site).
|
2
2
|
|
3
3
|
[](http://travis-ci.org/ashrafuzzaman/query_report)
|
4
4
|
|
@@ -9,6 +9,7 @@ Query report is a reporting tool, which does the following:
|
|
9
9
|
* Provide feature to define re usable custom filter
|
10
10
|
|
11
11
|
As built in filter I have used [ransack](https://github.com/ernie/ransack) and pagination with [kaminari](https://github.com/amatsuda/kaminari)
|
12
|
+
|
12
13
|
For a demo see [here](http://query-report-demo.herokuapp.com)
|
13
14
|
|
14
15
|
## The purpose
|
@@ -19,7 +20,7 @@ concentrate in a report is the query and filter.
|
|
19
20
|
Query report is tested with Rails 3. You can add it to your Gemfile with:
|
20
21
|
|
21
22
|
```ruby
|
22
|
-
gem "query_report", "~> 1.0.
|
23
|
+
gem "query_report", "~> 1.0.4"
|
23
24
|
```
|
24
25
|
|
25
26
|
Run the bundle command to install it.
|
@@ -53,4 +54,4 @@ end
|
|
53
54
|
```
|
54
55
|
|
55
56
|
## License
|
56
|
-
MIT License. Copyright © 2013 [Ashrafuzzaman](http://
|
57
|
+
MIT License. Copyright © 2013 [Ashrafuzzaman](http://ashrafuzzaman.github.io/site). See MIT-LICENSE for further details.
|
@@ -6,4 +6,8 @@ module QueryReportLinkHelper
|
|
6
6
|
def link_to_download_report_csv
|
7
7
|
link_to t('views.links.csv'), export_report_url_with_format('csv'), :target => "_blank"
|
8
8
|
end
|
9
|
+
|
10
|
+
def link_to_email_query_report(target_dom_id)
|
11
|
+
link_to t('views.labels.email'), 'javascript:void(0)', :onclick => "QueryReportEmail.openEmailModal('#{target_dom_id}');" if QueryReport.config.allow_email_report
|
12
|
+
end
|
9
13
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class ReportMailer < ActionMailer::Base
|
2
|
+
default from: QueryReport.config.email_from
|
3
|
+
|
4
|
+
def send_report(user, to, subject, message, file_name, attachment)
|
5
|
+
@user = user
|
6
|
+
@message = message
|
7
|
+
attachments["#{file_name}.pdf"] = attachment
|
8
|
+
mail(:to => to, :subject => subject)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<div class="query_report_charts">
|
2
|
+
<%= render :partial => "query_report/charts", locals: {report: report} %>
|
3
|
+
</div>
|
4
|
+
<div class="query_report_records">
|
5
|
+
<%= render :partial => "query_report/records", locals: {report: report} %>
|
6
|
+
</div>
|
7
|
+
<div class="query_report_pagination">
|
8
|
+
<%= paginate report.paginated_query, remote: @remote if report.paginate? %>
|
9
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% uuid = Time.now.to_i
|
2
|
+
@target_dom_id = params[:target_dom_id] || "report_#{uuid}" %>
|
3
|
+
<div class="query_report" id="<%= @target_dom_id %>">
|
4
|
+
<div class="query_report_header">
|
5
|
+
<div class="query_report_search_form" style="<%= report.has_filter? ? '' : 'display: none;' %>">
|
6
|
+
<%= render :partial => "query_report/search", locals: {report: report} %>
|
7
|
+
</div>
|
8
|
+
<div class="query_report_links">
|
9
|
+
<%= render :partial => "query_report/links", locals: {report: report} %>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
<div class="query_report_content">
|
13
|
+
<%= render :partial => "query_report/content", locals: {report: report} %>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -1,8 +1,8 @@
|
|
1
1
|
<% if report.records.size > 0 %>
|
2
|
-
<table class="
|
2
|
+
<table class="<%= QueryReport.config.record_table_class %>" cellpadding="0" cellspacing="0">
|
3
3
|
<thead>
|
4
4
|
<% report.columns.each do |column| %>
|
5
|
-
<th><%= sort_link(report.search, column.name) %></th>
|
5
|
+
<th><%= column.sortable? ? sort_link(report.search, column.name, {}, remote: @remote) : column.name %></th>
|
6
6
|
<% end %>
|
7
7
|
</thead>
|
8
8
|
|
@@ -14,6 +14,7 @@
|
|
14
14
|
<% end %>
|
15
15
|
</tr>
|
16
16
|
<% end %>
|
17
|
+
<%= render :partial => "query_report/record_footer", locals: {report: report} %>
|
17
18
|
</tbody>
|
18
19
|
</table>
|
19
20
|
<% else %>
|
@@ -1,10 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
<%
|
4
|
-
|
5
|
-
<%= query_report_render_filter(filter, comparator) %>
|
6
|
-
<% end %>
|
1
|
+
<%= search_form_for report.search, url: url_for, remote: @remote, html: {:method => :get, :class => 'form-inline'} do |f| %>
|
2
|
+
<% report.filters.each do |filter| %>
|
3
|
+
<% filter.comparators.each do |comparator| %>
|
4
|
+
<%= query_report_render_filter(filter, comparator) %>
|
7
5
|
<% end %>
|
8
|
-
<%= f.submit 'Search', :class => 'btn btn-blue' %>
|
9
6
|
<% end %>
|
7
|
+
<%= hidden_field_tag :send_as_email, false %>
|
8
|
+
<%= hidden_field_tag :email_to %>
|
9
|
+
<%= hidden_field_tag :subject %>
|
10
|
+
<%= hidden_field_tag :message %>
|
11
|
+
<%= hidden_field_tag :target_dom_id, @target_dom_id %>
|
12
|
+
<%= f.submit 'Search', :class => QueryReport.config.search_button_class, :onclick => "$('#{@target_dom_id} #send_as_email').val(0);" %>
|
10
13
|
<% end %>
|
@@ -1,14 +1 @@
|
|
1
|
-
<%= render :partial => "query_report/
|
2
|
-
<br/>
|
3
|
-
<%= render :partial => "query_report/links", locals: {report: @report} %>
|
4
|
-
<%= render :partial => "query_report/charts", locals: {report: @report} %>
|
5
|
-
<br/>
|
6
|
-
<%# if @report.scopes.size > 0 %>
|
7
|
-
<%#= link_to_with_scope('all', @report.current_scope) %>
|
8
|
-
<%# @report.scopes.each do |scope| %>
|
9
|
-
<%#= link_to_with_scope(scope, @report.current_scope) %>
|
10
|
-
<%# end %>
|
11
|
-
<%# end %>
|
12
|
-
|
13
|
-
<%= render :partial => "query_report/records", locals: {report: @report} %>
|
14
|
-
<%= paginate @report.paginated_query if @report.paginate? %>
|
1
|
+
<%= render :partial => "query_report/_list", locals: {report: @report} %>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%if params['target_dom_id'].present?%>
|
2
|
+
if ($('#<%=params['target_dom_id']%> .query_report_content').length == 0) {
|
3
|
+
$('#<%=params['target_dom_id']%>').html('<%= escape_javascript(render :partial => 'query_report/list', locals: {report: @report})%>');
|
4
|
+
} else {
|
5
|
+
$('#<%=params['target_dom_id']%> .query_report_content').html('<%= escape_javascript(render :partial => 'query_report/content', locals: {report: @report})%>');
|
6
|
+
}
|
7
|
+
<% else %>
|
8
|
+
console.log("target_dom_id was not defined");
|
9
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @message.gsub(/\n/, '<br />').html_safe %>
|
data/lib/query_report.rb
CHANGED
@@ -15,14 +15,18 @@ module QueryReport
|
|
15
15
|
config.pdf_options = {
|
16
16
|
template_class: nil,
|
17
17
|
color: '000000',
|
18
|
-
font_size:
|
18
|
+
font_size: 8,
|
19
19
|
table: {
|
20
20
|
row: {odd_bg_color: "DDDDDD", even_bg_color: "FFFFFF"},
|
21
|
-
header: {bg_color: 'AAAAAA', font_size:
|
21
|
+
header: {bg_color: 'AAAAAA', font_size: 8}
|
22
22
|
},
|
23
23
|
chart: { height: 160, width: 200 }
|
24
24
|
}
|
25
|
-
config.date_format
|
25
|
+
config.date_format = :default
|
26
26
|
config.datetime_format = :default
|
27
|
+
config.email_from = "from@example.com"
|
28
|
+
config.allow_email_report = true
|
29
|
+
config.record_table_class = 'table table-bordered table-striped'
|
30
|
+
config.search_button_class = 'btn btn-blue'
|
27
31
|
end
|
28
32
|
end
|
data/lib/query_report/column.rb
CHANGED
@@ -13,11 +13,35 @@ module QueryReport
|
|
13
13
|
# +options+:: Options can have the following,
|
14
14
|
# options[:type] => date | text | whatever
|
15
15
|
# options[:comp] => the comparators used for ransack search, [:gteq, :lteq]
|
16
|
+
# options[:show_total] => set true to calculate total for that column
|
16
17
|
# options[:only_on_web] => the column will appear on the web and not appear in PDF or csv if set to true
|
17
18
|
def column(name, options={}, &block)
|
18
19
|
@columns << Column.new(self, name, options, block)
|
19
20
|
end
|
20
21
|
|
22
|
+
def column_total_with_colspan
|
23
|
+
total_with_colspan = []
|
24
|
+
colspan = 0
|
25
|
+
total_text_printed = false
|
26
|
+
columns.each do |column|
|
27
|
+
if column.has_total?
|
28
|
+
if colspan > 0
|
29
|
+
title = total_text_printed ? '' : I18n.t('query_report.total')
|
30
|
+
total_with_colspan << (colspan == 1 ? {content: title} : {content: title, colspan: colspan})
|
31
|
+
end
|
32
|
+
total_with_colspan << {content: column.total}
|
33
|
+
total_text_printed = true
|
34
|
+
colspan = 0
|
35
|
+
else
|
36
|
+
colspan += 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
if colspan > 0
|
40
|
+
total_with_colspan << {content: '', colspan: colspan}
|
41
|
+
end
|
42
|
+
total_with_colspan
|
43
|
+
end
|
44
|
+
|
21
45
|
class Column
|
22
46
|
attr_reader :report, :name, :options, :type, :data
|
23
47
|
|
@@ -31,6 +55,10 @@ module QueryReport
|
|
31
55
|
@options[:only_on_web] == true
|
32
56
|
end
|
33
57
|
|
58
|
+
def sortable?
|
59
|
+
@options[:sortable] == true
|
60
|
+
end
|
61
|
+
|
34
62
|
def humanize
|
35
63
|
@humanize ||= options[:as] || @report.model_class.human_attribute_name(name)
|
36
64
|
end
|
@@ -38,6 +66,14 @@ module QueryReport
|
|
38
66
|
def value(record)
|
39
67
|
self.data.kind_of?(Symbol) ? record.send(self.name) : self.data.call(record)
|
40
68
|
end
|
69
|
+
|
70
|
+
def has_total?
|
71
|
+
@options[:show_total] == true
|
72
|
+
end
|
73
|
+
|
74
|
+
def total
|
75
|
+
@total ||= has_total? ? report.records.inject(0) {|sum, r| sum + r[humanize] } : nil
|
76
|
+
end
|
41
77
|
end
|
42
78
|
end
|
43
79
|
end
|
@@ -41,9 +41,9 @@ module QueryReport
|
|
41
41
|
def objectified_param_value
|
42
42
|
@stringified_default ||= case @filter.type
|
43
43
|
when :date
|
44
|
-
Date.current.parse(@default)
|
44
|
+
@default.kind_of?(String) ? Date.current.parse(@default) : @default
|
45
45
|
when :datetime
|
46
|
-
Time.zone.parse(@default)
|
46
|
+
@default.kind_of?(String) ? Time.zone.parse(@default) : @default
|
47
47
|
when :boolean
|
48
48
|
@default.to_boolean
|
49
49
|
else
|
data/lib/query_report/config.rb
CHANGED
@@ -4,5 +4,9 @@ module QueryReport
|
|
4
4
|
config_accessor :pdf_options
|
5
5
|
config_accessor :date_format
|
6
6
|
config_accessor :datetime_format
|
7
|
+
config_accessor :email_from
|
8
|
+
config_accessor :allow_email_report
|
9
|
+
config_accessor :record_table_class
|
10
|
+
config_accessor :search_button_class
|
7
11
|
end
|
8
12
|
end
|
data/lib/query_report/helper.rb
CHANGED
@@ -17,15 +17,27 @@ module QueryReport
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def render_report(options)
|
20
|
+
if (params[:send_as_email].to_i > 0)
|
21
|
+
send_pdf_email(params[:email_to], params[:subject], params[:message], action_name, pdf_for_report(options))
|
22
|
+
end
|
23
|
+
|
24
|
+
@remote = false
|
20
25
|
respond_to do |format|
|
21
|
-
format.js
|
26
|
+
format.js do
|
27
|
+
@remote = true
|
28
|
+
render 'query_report/list'
|
29
|
+
end
|
22
30
|
format.html { render 'query_report/list' }
|
23
31
|
format.json { render json: @report.all_records }
|
24
32
|
format.csv { send_data generate_csv_for_report(@report.all_records), :disposition => "attachment;" }
|
25
|
-
format.pdf { send_data
|
33
|
+
format.pdf { send_data pdf_for_report(options) }
|
26
34
|
end
|
27
35
|
end
|
28
36
|
|
37
|
+
def pdf_for_report(options)
|
38
|
+
query_report_pdf_template_class(options).new(@report).to_pdf.render
|
39
|
+
end
|
40
|
+
|
29
41
|
def query_report_pdf_template_class(options)
|
30
42
|
options = QueryReport.config.pdf_options.merge(options)
|
31
43
|
if options[:template_class]
|
@@ -48,5 +60,11 @@ module QueryReport
|
|
48
60
|
nil
|
49
61
|
end
|
50
62
|
end
|
63
|
+
|
64
|
+
def send_pdf_email(email, subject, message, file_name, attachment)
|
65
|
+
@user = current_user
|
66
|
+
to = email.split(',')
|
67
|
+
ReportMailer.send_report(@user, to, subject, message, file_name, attachment).deliver
|
68
|
+
end
|
51
69
|
end
|
52
70
|
end
|
data/lib/query_report/report.rb
CHANGED
@@ -26,8 +26,9 @@ module QueryReport
|
|
26
26
|
private
|
27
27
|
def render_charts_with(report)
|
28
28
|
return if report.charts.empty? #or !report.chart_on_pdf?
|
29
|
-
|
30
|
-
|
29
|
+
num_of_column = 2
|
30
|
+
height = (pdf.bounds.width/num_of_column - 50) * (report.charts.size.to_f/num_of_column).ceil
|
31
|
+
pdf.column_box([0, pdf.cursor], :columns => num_of_column, :width => pdf.bounds.width, :height => height) do
|
31
32
|
report.charts.each do |chart|
|
32
33
|
render_chart(chart)
|
33
34
|
end
|
@@ -38,9 +39,9 @@ module QueryReport
|
|
38
39
|
if chart.respond_to?(:to_blob)
|
39
40
|
blob = chart.to_blob
|
40
41
|
data = StringIO.new(blob)
|
41
|
-
pdf.pad_top(10) do
|
42
|
-
|
43
|
-
end
|
42
|
+
#pdf.pad_top(10) do
|
43
|
+
pdf.image(data, :width => pdf.bounds.width)
|
44
|
+
#end
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
@@ -54,7 +55,7 @@ module QueryReport
|
|
54
55
|
|
55
56
|
def table_content_for(report)
|
56
57
|
table_items = report.all_records
|
57
|
-
table_items.map do |item|
|
58
|
+
items = table_items.map do |item|
|
58
59
|
item_values = []
|
59
60
|
|
60
61
|
report_columns.collect(&:humanize).each do |column|
|
@@ -62,6 +63,13 @@ module QueryReport
|
|
62
63
|
end
|
63
64
|
item_values
|
64
65
|
end
|
66
|
+
|
67
|
+
if report.has_total?
|
68
|
+
items = items << report.column_total_with_colspan.collect do |total|
|
69
|
+
total[:colspan] ? total : total[:content]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
items
|
65
73
|
end
|
66
74
|
|
67
75
|
def render_table_with(report)
|
data/lib/query_report/version.rb
CHANGED
@@ -15,28 +15,28 @@ class InvoicesController < ApplicationController
|
|
15
15
|
column :title do |invoice|
|
16
16
|
link_to invoice.title, invoice
|
17
17
|
end
|
18
|
-
column :total_paid
|
19
|
-
column :total_charged
|
18
|
+
column :total_paid, show_total: true
|
19
|
+
column :total_charged, show_total: true
|
20
20
|
column :paid
|
21
21
|
column :received_by
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
23
|
+
column_chart('Unpaid VS Paid') do
|
24
|
+
add 'Unpaid' do |query|
|
25
|
+
(query.sum('total_charged').to_f - query.sum('total_paid').to_f).to_f
|
26
|
+
end
|
27
|
+
add 'Paid' do |query|
|
28
|
+
query.sum('total_paid').to_f
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
pie_chart('Unpaid VS Paid') do
|
33
|
+
add 'Unpaid' do |query|
|
34
|
+
(query.sum('total_charged').to_f - query.sum('total_paid').to_f).to_f
|
35
|
+
end
|
36
|
+
add 'Paid' do |query|
|
37
|
+
query.sum('total_paid').to_f
|
38
|
+
end
|
39
|
+
end
|
40
40
|
end
|
41
41
|
#ap @report.filters.last.comparators.first.default
|
42
42
|
#ap @report.filters.last.comparators.first.param_value
|
Binary file
|