Calculated values from data returned by Ransack are removed when paginating with Kaminari - ruby-on-rails

On my Rails application, I am performing calculations on the data returned by Ransack. These are:
Total count
Averages on certain fields
The averages work but as soon as I go to the next page (using Kaminari pagination) the values disappear. However, total count continues to work.
From the view, this works:
<%= #products.total_count %>
Also from the view, this works, but when going to the next page, it doesn't:
<%= number_to_percentage(#products.average(:status), precision: 0) %>
Does anyone have any ideas?
Products controller:
def index
#q = Product.ransack(params[:q])
#q.sorts = 'start_date desc' if #q.sorts.empty?
#products = #q.result.page(params[:page]).per(30)
end
This is the SQL query from when it works on the first page:
(23.7ms) SELECT AVG("products"."price_eur") AS avg_id FROM "products" WHERE (status > 100) AND "products"."above_revenue_average" = 't' AND ("products"."name" ILIKE '%a%') LIMIT 30 OFFSET 0
And the params:
Parameters: {"utf8"=>"✓", "q"=>{"name_cont"=>"a", "type_eq"=>"", "upward_trending"=>"1", "downward_trending"=>"", "category_id_in"=>[""], "country_eq"=>"", "price_eur_gteq"=>"", "price_eur_lteq"=>"", "start_date_gteq"=>"", "start_date_lteq"=>""}, "commit"=>"All"}
This is the SQL query from when it stops working on the second page:
(23.8ms) SELECT AVG("products"."price_eur") AS avg_id FROM "products" WHERE (status > 100) AND "products"."above_revenue_average" = 't' AND ("products"."name" ILIKE '%a%')
And the params:
Parameters: {"commit"=>"All", "page"=>"2", "q"=>{"category_id_in"=>[""], "country_eq"=>"", "downward_trending"=>"", "name_cont"=>"a", "price_eur_gteq"=>"", "price_eur_lteq"=>"", "start_date_gteq"=>"", "start_date_lteq"=>"", "type_eq"=>"", "upward_trending"=>"1"}, "utf8"=>"✓"}

This turned out to be quite an easy fix, just need to run the calculations on #q.result
<%= number_to_percentage(#q.result.average(:status), precision: 0) %>

Related

How can I add pagination on shopify API products with rails gem

I'm using the rails gem shopify API gem to fetch shopify products. I'm fetching products like this:
ShopifyAPI::Product.find(:all, params: { page: parmas[:page],
limit: 10,
title: params[:search]
})
this API returns products searched by title with limit 10.
So How can I add pagination on my view or can I use will_paginate gem for it?
Please help!, thanks! in advance.
I think is late for your work. But this how to it:
Example
#orders = ShopifyAPI::Order.find(:all, :params => {:status => "any", :limit => 3, :order => "created_at DESC" })
#orders.each do |order|
id = order.id
name = order.name
end
while #orders.next_page?
#orders = #orders.fetch_next_page
#orders.each do |order|
id = order.id
name = order.name
end
end
So you can create paginable array based on what you receive in each loop. The first call defines the limit for all subsequents call to fetch_next_page. In my case each page contain 3 element, you can go up to 250.
Another way is to navigate using shopify "next_page_info" parameter, so you only need to fetch the first query and include all filters, then just navigate back/fwd with page infos(previous_page_info or next_page_info).
Example 2
first_batch_products = ShopifyAPI::Product.find(:all, params: { limit: 50 })
second_batch_products = ShopifyAPI::Product.find(:all, params: { limit: 50, page_info: first_batch_products.next_page_info })
next_page_info = #orders.next_page_info
prev_page_info = #orders.previous_page_info
More info here: https://github.com/Shopify/shopify_api#pagination
Regards.
You can use Kaminari.paginate_array(*args) with array(array of Shopify products) as first argument and with option as hash, where you have to set value to total_count key.
Full example:
#paginatable_array = Kaminari.paginate_array(#shopify_products, total_count:145).page(1).per(10)
use 1 as argument to .page method, because your array will contain only 10 objects(if limit is 10 and 10 objects in view)
In view just use helper method
<%= paginate #paginatable_array %>
I hope that it will help!

How can set a default value in select tag?

I have a table and want to set default value
Here is the table:
|customers|
|id| |name|
1 ABC
2 DEF
3 GHI
4 JKL
Here is the controller:
#customers.Customer.all
#customer_selected = Customer.find(:all,:conditions=>['id=1'])
Here is the view, showing all customers
<%= select_tag "customer_id", options_for_select(#customers.collect {|t| [t.name,t.id]},params[:customer_id].to_i) %>
I tried this line to select customer_id=1 as default value (selected)
<%= select_tag "customer_id", options_for_select(#customers.collect {|t| [t.name,t.id]},params[:customer_id].to_i),:selected =>#customer_selected.name %>
I'm getting this error
undefined method `name' for #<Array:0x7f31d8098560>
Please somebody can help me ?
One thing to bear in mind is that if params[:customer_id] isn't there, then it will be equal to nil, and nil.to_i gives 0. This should be fine as you shouldn't have a customer with id 0 but it's something to be aware of.
I think this should work:
<% default_id = params[:customer_id] ? params[:customer_id].to_i : (#customer_selected && #customer_selected.id) %>
<%= select_tag "customer_id", options_for_select(#customers.collect {|t| [t.name,t.id]}, default_id) %>
This will use params[:customer_id] if it's defined, or #customer_selected.id if #customer_selected is defined.
#customer_selected is an array, as the error states. This is because find(:all) returns an array, even if there is only one result.
You should be able to replace
#customer_selected = Customer.find(:all,:conditions=>['id=1'])
with
#customer_selected = Customer.find(1)
Without breaking anything, since find looks up objects based on ID anyway and returns a single record rather than a collection.

How to make 'sticky' posts in Rails?

I'm still sort of new to Rails. I'm building a blog type website and would like to make some of my posts "sticky", just like Wordpress stickies (stick to the top).
I know you can order posts by its created_at column, which is what I'm doing now. But how would I have my "sticky" posts stay above my other posts no matter what its created_at date is?
Current code:
#posts = Post.all.order('created_at desc')
Everything's working fine now. There was a minor issue with my code
#posts = Post.order('sticky, created_at desc')
works fine..
Just add a boolean attribute sticky to your Post model and then do:
#posts = Post.order('sticky, created_at desc')
Try this?
In controller:
#sticky_posts = Post.where(sticky: true)
#common_posts = Post.where(sticky: false).order('created_at DESC')
In view:
<%= render :partial => 'post', locals: {posts: #sticky_post %>
<%= render :partial => 'post', locals: {posts: #common_post %>
Just make two query and it can do what you want.
You have string attribute : post_type (sticky, normal, or unpublished). If you are still using string attribute, try this :
#posts = Post.where('post_type not in (?)', 'unpublished').order('post_type desc, created_at desc')
Note :
.where('post_type not in (?)', 'unpublished')
not display post have unpublished post_type.
.order('post_type desc, created_at desc')
order post_type with descending (sticky, normal) and
created_at with descending
But that is not recommended, You should use boolean attribute, example
t.boolean :sticky
# sticky : true , normal : false
t.boolean :publish
# publish : true , unpublish : false
And you can use this on your controller :
#posts = Post.where(:publish => true).order('sticky DESC, created_at DESC')
See example :
Ruby on Rails 3.2.x - Sticky and Publish Post

Rails, how to use searchlogic to reorder returned objects

So I have had various forms of this working in the last while, but never all working together.
for reference I have categories / Brands / Products, with the right relationships working: the site is http://emeraldcityguitars.com to see it in action.
So in my brands controller show action:
#category = Category.find_by_url_name(params[:category_id])
#brand = Brand.find(params[:id])
#search = Product.brand_id_equals(#brand.id).category_id_equals(#category.id).descend_by_price
#products = #search.paginate(:page => params[:page])
this works fine, as is evidenced in my log:
Category Load (25.6ms) SELECT * FROM "categories"
Category Load (0.2ms) SELECT * FROM "categories" WHERE ("categories"."url_name" = 'acoustic-guitars') LIMIT 1
Brand Load (0.6ms) SELECT * FROM "brands" WHERE ("brands"."id" = 14)
Product Load (4.8ms) SELECT * FROM "products" WHERE ((products.category_id = 3) AND (products.brand_id = 14)) ORDER BY products.price DESC LIMIT 6 OFFSET 0
SQL (0.2ms) SELECT count(*) AS count_all FROM "products" WHERE ((products.category_id = 3) AND (products.brand_id = 14))
Rendering template within layouts/application
Rendering brands/show
You can see that its grabbing products descending by price.
In my Brand#show I am doing the following:
<%- form_for [#category, #brand], :html => {:method => 'get', :id => 'sort_form', :class => 'sort_form'} do -%>
<label>Sort by: </label> <%= select_tag :order, product_sort_options %>
<%= submit_tag 'Go' %>
<%- end -%>
The goal being that a user could sort by a couple different options.
I also have this in my products_helper:
def product_sort_options
options_for_select([
['', nil],
['Newest to Oldest', 'descend_by_date'],
['Oldest to Newest', 'ascend_by_date'],
['Price: Highest to Lowest', 'descend_by_price'],
['Price: Lowest to Highest', 'ascend_by_price'],
['Name', 'ascend_by_name']
])
end
The issue I am having is that if I click the drop down and do price lowest to highest it reloads the page, with "?order=ascend_by_price" at the end of the url but there is no change in the order of the products.
any help is appreciated.
You'll need to add in a call to include the value of your :order parameter. It only gets included in the search by default if you're doing something like Product.search(params[:search]) (and the order parameter would then have to be in params[:search][:order]).
So, something like this should work:
#search = Product.brand_id_equals(#brand.id)
#search.category_id_equals(#category.id)
#search.order(params[:order] || :descend_by_price)
I've split it out to make it easier to read. The last line specifies to order by the value of your order parameter or the default ordering if that's not available.

Named Scope Problem With "Has Many Through" Association

I'm using named scopes to process some filtering actions, and the log is showing that everything is working perfectly, except that after the app goes and finds the correct data, it then ignores what it found and just lists a find.all instead of the filtered result. Here's the details.
I have 3 models: Users, Markets and Schedules.
User has_many :schedules
User has_many :markets, :through => :schedules
Market has_many :schedules
Market has_many :users, :through => :schedules
Schedule belongs_to :user
Schedule belongs_to :market
On the "show" page for each market, I display the users who sell things at that market. I also display the days of the week that such users are at the market. This data is contained in the join model (i.e. schedules).
On the market page, I need to support filtering the users by day of the week that the user is at the market.
In my Market controller, I have this:
def show
#market = Market.find(params[:id])
#users = #market.users.filter_marketdate(params[:filter])
end
In my Market model, I have this:
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my User model, I have this:
named_scope :monday, :include => :schedules, :conditions => {'schedules.monday' => true }
AND
def self.filter_marketdate(filter)
case filter
when 'monday' then monday.all
else find(:all)
end
end
In my show view for Markets, I have this:
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.schedules.each do |c| %>
<%= link_to c.user.name, c.user %>
<% end %>
Here's the weird part. The following is my log output when I select the Monday filter. If you look at the Select Schedules line of the output, you can see that the query is finding a limited number of user IDs. These are, in fact, the correct user IDs that I want to display for my filtered query. The part I don't understand is that after the app does the query perfectly, it then goes and loads all of the users instead of just the filtered results. I'm not sure what I'm missing.
Processing MarketsController#show (for ip at 2009-09-21 05:19:25) [GET]
Parameters: {"id"=>"1", "filter"=>"monday"}
Market Load (0.8ms) SELECT * FROM "markets" WHERE ("markets"."id" = 1)
User Load (7.3ms) SELECT "users".* FROM "users" INNER JOIN "schedules" ON "users".id = "schedules".user_id WHERE ((("schedules".market_id = 1)) AND ("schedules"."monday" = 't'))
Schedule Load (4.3ms) SELECT "schedules".* FROM "schedules" WHERE ("schedules".user_id IN (16,23,25,30,39,61,73,75,85,95,97,111,112,116,121,123,126,134,136,145,160,165,171,188,189))
Rendering template within layouts/application
Rendering markets/show
Schedule Load (14.3ms) SELECT * FROM "schedules" WHERE ("schedules".market_id = 1)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 2)
User Load (0.8ms) SELECT * FROM "users" WHERE ("users"."id" = 8)
It goes on to list every user who is connected to this particular market, essentially doing a find.all.
UPDATE
In response to Brad's comment below, I tried changing the code in my markets/show view to the following, but I got the same result. I agree that the problem is in here somewhere, but I can't quite figure out how to solve it.
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #market.users.each do |user| %>
<%= link_to user.name, user %>
<% end %>
In your view, you are iterating over each market.schedule, not over the #users variable that you set. In fact, you aren't even using #users in the code you show us.
Shouldnt that be:
#Market.rb
has_many :users, :through => :schedules
Beside this, maybe this plugin helps you:
http://github.com/ntalbott/query_trace
It provides feedback for each sql statement and where it comes from..
f.e.:
Before:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
Schedule Load (0.011488) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
Resource Load (0.022471) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
After:
Schedule Load (0.023687) SELECT * FROM schedules WHERE (schedules.id = 3) LIMIT 1
app/models/available_work.rb:50:in `study_method'
app/helpers/plan_helper.rb:4:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
Resource Load (0.001076) SELECT * FROM resources WHERE (resources.id = 328) LIMIT 1
app/models/available_work.rb:54:in `div_type'
app/helpers/plan_helper.rb:6:in `work_description'
app/views/plan/_resource_schedule.rhtml:27:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_resource_schedule.rhtml:24:in `_run_rhtml_plan__resource_schedule'
app/views/plan/_schedule_listing.rhtml:5:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:3:in `_run_rhtml_plan__schedule_listing'
app/views/plan/_schedule_listing.rhtml:1:in `_run_rhtml_plan__schedule_listing'
app/views/plan/index.rhtml:6:in `_run_rhtml_plan_index'
vendor/plugins/textmate_footnotes/lib/textmate_footnotes.rb:60:in `render'
UPDATE:
You have to call
<%= link_to_unless_current "All", :filter => 'all' %>
<%= link_to_unless_current "Mon", :filter => 'monday' %>
<% #users.each do |user| %>
<%= link_to user.name, user %>
<% end %>

Resources