I have some problems and I don't know wath's the best way to resolve it.
I have do a method, and I would like to help this in two views : poi.views and track.index
The method :
#distance_a_to_b = Track.find_by_sql(
["SELECT
ST_Distance(line::geography, pta::geography) +
ST_Distance(line::geography, ptb::geography) +
ST_Length(ST_LineSubstring(
line,
least(ST_Line_Locate_Point(line, pta), ST_Line_Locate_Point(line, ptb)),
greatest(ST_Line_Locate_Point(line, pta), ST_Line_Locate_Point(line, ptb)))::geography) AS dst_line
FROM (
SELECT
'SRID=4326;LINESTRING(1.457834243774414 43.597960902821576,1.462029218673706 43.59636807591895)'::geometry line,
'SRID=4326;POINT(1.457994 43.598124)'::geometry pta,
'SRID=4326;POINT(1.461628 43.596128)'::geometry ptb
) data"
])
I need to call this methdod in two views....
poi.show = distance bewtveen A to poi (point())
and
track.index = distance for each poi (point())
This method need 3 arguments :
a = start point ( query params) as a Point()
b = an end point as a Point()
and a linestring or merge linestring
How can I post this arguments to this method ?
As kind of :
#distance_a_to_b = Track.find_by_sql(
["SELECT
ST_Distance(line::geography, pta::geography) +
ST_Distance(line::geography, ptb::geography) +
ST_Length(ST_LineSubstring(
line,
least(ST_Line_Locate_Point(line, pta), ST_Line_Locate_Point(line, ptb)),
greatest(ST_Line_Locate_Point(line, pta), ST_Line_Locate_Point(line, ptb)))::geography) AS dst_line
FROM (
SELECT
'#track.path'::geometry line,
'#poi.lonlat'::geometry pta,
'query: params'::geometry ptb
) data"
])
How can I post the variables from each view ?
How can I get the result from this method, from each view ? by call method ?
Table pois :
t.geography "lonlat", limit: {:srid=>4326, :type=>"st_point", :geographic=>true}
Table tracks :
t.geometry "path", limit: {:srid=>4326, :type=>"line_string"}
Tracks has_many pois
Poi belongs_to track
Edit
Following the advice, here's what I did
In poi controller (Just to define the datas) :
def index
track = Track.friendly.find(params[:track_id])
#pois = Poi.where(track_id: track)
#track = Track.find_by id: 1
#poi = Poi.find_by id: 1
respond_to do |format|
format.html
end
In poi model :
def distance_along_track_to(poi2, track)
distance_sql = <<-SQL
SELECT
ST_Distance(tr.path::geography, pta.lonlat::geography) +
ST_Distance(tr.path::geography, ptb.lonlat::geography) +
ST_Length(ST_LineSubstring(
tr.path,
least(ST_Line_Locate_Point(tr.path, pta.lonlat::geometry), ST_Line_Locate_Point(tr.path, ptb.lonlat::geometry)),
greatest(ST_Line_Locate_Point(tr.path, pta.lonlat::geometry), ST_Line_Locate_Point(tr.path, ptb.lonlat::geometry)))::geography) AS dst_line
FROM tracks tr, pois pta, pois ptb
WHERE tr.id = #{track.id}
AND pta.id = #{self.id}
AND ptb.id = #{poi2.id}
SQL
Poi.find_by_sql(distance_sql).dst_line
end
In the index view :
<% #pois.each do |poi| %>
<div>
<%= poi.name %>
<%= poi.track_id %>
<%= #poi.distance_along_track_to(poi, #track) %> %>
</div>
<% end %>
And now I have this error message :
undefined method `dst_line' for [#<Poi id: nil>]:Array
I don't understand why #poi = nil ?
Suppose you have #poi1, #poi2 and #track, you can write your method as follows:
class Track
def distance_between_two_pois(poi1, poi2)
distance_sql = <<-SQL
SELECT
ST_Distance(tr.path, pta.lonlat) +
ST_Distance(tr.path, ptb.lonlat) +
ST_Length(ST_LineSubstring(
tr.path,
least(ST_Line_Locate_Point(tr.path, pta.lonlat), ST_Line_Locate_Point(tr.path, ptb.lonlat)),
greatest(ST_Line_Locate_Point(tr.path, pta.lonlat), ST_Line_Locate_Point(tr.path, ptb.lonlat)))::geography) AS dst_line
FROM tracks tr, pois pta, pois ptb
WHERE tr.id = #{self.id}
AND pta.id = #{poi1.id}
AND ptb.id = #{poi2.id}
SQL
Track.find_by_sql(distance_sql).first.dst_line
end
end
and then you could call it as follows
#track.distance_between_two_pois(#poi1, #poi2)
Not sure what the best api is? I can also imagine writing something like?
#poi1.distance_along_track_to(#poi2, #track)
Related
Having issue while passing limit per paje display 50 records.
if params[:limit].present? && params[:offset].present?
offset_value = (params[:offset].to_i - 1) * params[:limit].to_i
limit << " LIMIT #{params[:limit].to_i} OFFSET #{offset_value}"
elsif params[:limit].present?
limit << " LIMIT #{params[:limit].to_i}"
else
if params[:offset].present?
offset_value = (params[:offset].to_i - 1) * 50
limit << " LIMIT 50 OFFSET #{offset_value}"
else
limit << " LIMIT 50"
end
end
query << "
select pp.id,
ud.last_name || ' ' || ud.first_name as full_name,
pp.image_file_name,
pp.gender_type ,
pp.no_of_view_for_last_30_days ,
pp.no_of_likes ,
pp.no_of_comments,
pp.tenpo_name_display,
pp.online_open ,
pp.online_comment from product pp
inner join user_details ud on pp.user_id = ud.user_id
inner join user_labels ul on ul.user_id = ud.user_id
where pp.flag = false and end_dt is null"
#posts = ActiveRecord::Base.connection.execute(query + limit)
having error in view, below line.
<%= will_paginate #posts, :renderer => BootstrapPagination::Rails %>
trying to make pagination, but having error like total_pages after passing objet to view.
It seems like you're trying to use will_paginate gem, aren't you?
If that's the case, you're doing too much in your controller. will_paginate is supposed to do all those calculations (offsets etc.) for you:
#posts = Post.paginate(page: params[:page])
Please note also that https://github.com/bootstrap-ruby/will_paginate-bootstrap (which it seems you're trying to use) is no longer maintained.
I done with this. get total count from query.
query << " select count(*) OVER() AS total_count,
pp.id,
ud.last_name || ' ' || ud.first_name as full_name,
pp.image_file_name,
pp.gender_type ,
pp.no_of_view_for_last_30_days ,
pp.no_of_likes ,
pp.no_of_comments,
pp.tenpo_name_display,
pp.online_open ,
pp.online_comment from product pp
inner join user_details ud on pp.user_id = ud.user_id
inner join user_labels ul on ul.user_id = ud.user_id
where pp.flag = false and end_dt is null"
offset = params[:page].present? ? (params[:page].to_i - 1) * 30 : 0
#posts = ActiveRecord::Base.connection.execute(query + 'limit 30 offset ' + offset.to_s )
posts_count = !#posts.nil? ? #posts.first["total_count"] : 0
#post_count = #posts.to_a.paginate(page: params[:page], per_page: 30, total_entries: posts_count)
passing param as total_entries to pagination. It's work.
I am using find_by_sql to do a query on my Conversationalist model using Postgres as the DB server:
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
LEFT JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])
It works fine if there is a result. But if there is no result then I get an array with an empty Conversationalist object: [#<Conversationalist id: nil, conversable_type: nil...>]
Here is what I get as a result doing a direct query on the DB:
What I am expecting is an empty array since no rows should be returned but instead I get a result. How would I get an empty array if no results are returned?
ADDITIONAL CONTEXT
What I am trying to do is essentially a chat. When someone messages another user, the code above first checks to see if those two people are already chatting. If they are the message gets added to the chat. If not, a new Chat gets created and the message gets added:
class MessagesController < ApplicationController
def create
message = new_message
conversation = already_conversing?
if conversation.empty? || conversation.first.id.nil?
chat = Chat.new
chat.messages << message
chat.conversationalists << sender
chat.conversationalists << recipient
chat.save!
else
chat = Chat.find(conversation.first.chat_id)
chat.messages << message
end
head :ok
end
private
def new_message
Message.new(
sender_id: params[:sender_id],
sender_type: params[:sender_type],
recipient_id: params[:recipient_id],
recipient_type: params[:recipient_type],
message: params[:message]
)
end
def already_conversing?
Conversationalist.conversing?(
params[:recipient_id],
params[:recipient_type],
params[:sender_id],
params[:sender_type]
)
end
end
The Model:
class Conversationalist < ApplicationRecord
def self.conversing?(recipient_id, recipient_type, sender_id, sender_type)
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
LEFT JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])
end
end
So I was able to figure it out with the help of #Beartech from the comments above. Essentially the issue was happening because of the LEFT JOIN. If there are any results in t1 then Rails returns an array with an empty object. Similarly, if it was a RIGHT JOIN and t2 had a result, Rails would do the same. So the fix, in order to get an empty array, is to change the join to an INNER JOIN:
Conversationalist.find_by_sql(['
SELECT * FROM (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t1
INNER JOIN (
SELECT * FROM conversationalists
WHERE conversable_id = ? AND conversable_type = ?
) t2
ON t1.chat_id = t2.chat_id',
recipient_id, recipient_type, sender_id, sender_type])
In my rails application I need to call a method defined in one table from another controller. There are two tables named coordinates and tweets. The condition for the tweets table is decided by the coordinates table. In my final view I need to display the attributes of the tweets table for which the condition is decided by the coordinates table.
My Coordinates table code
class Coordinates<ActiveRecord::Base
def self.query()
a = Coordinates.where("city=?", params[:show])
b = a.first
if a.count == 1
latitude = b.latitude
longitude= b.longitude
if(latitude=0 && longitude=0) then
return sql="Select * from tweets where tweet_text LIKE '%text%' AND user_loc LIKE '%show%' order by id desc"
else if (latitude!=0 && longitude!=0)
min_lat = latitude - 1.0
max_lat = latitude + 1.0
min_lng = longitude - 1.0
max_lng = longitude + 1.0
return sql = "Select * from tweets where tweet_text LIKE '%text%' AND ( ((longitude BETWEEN min_lng and max_lng) AND (latitude BETWEEN min_lat and max_lat)) OR (user_loc LIKE '%show%') ) order by id desc"
else
return sql="Select * from tweets where tweet_text LIKE '%text%'"
end
end
end
My tweets table
class Tweets<ActiveRecord::Base
attr_accessible :id, :tweet_created_at, :tweet_id, :tweet_text, :tweet_source, :user_id, :user_name, :user_sc_name, :user_loc, :user_img, :longitude, :latitude, :place, :country
end
I need to call the query definition from my tweets_controller so that it decides what query to fetch from the tweets table and display in the final view . But the params function is not working in the model.rb file . I want something like this
My tweets_controller.rb
class TweetsController<ApplicationController
def index
Coordinates.query()
end
end
My final view code
<%= #tweets.each do |tweets| %>
<ul>
<li><%= tweets.id %></li>
<li><%= tweets.tweet_created_at %></li>
<li><%= tweets.tweet_source %></li>
<li><%= tweets.tweet_text %></li>
<li><%= tweets.user_id %></li>
<li><%= tweets.user_name %></li>
<li><%= tweets.user_sc_name %></li>
<li><%= tweets.user_loc %></li>
<li><%= tweets.user_img %></li>
<li><%= tweets.longitude %></li>
<li><%= tweets.latitude %></li>
<li><%= tweets.place %></li>
<li><%= tweets.country %></li>
</ul>
<% end %>
I am not able to call the query function defined in coordinates table from tweetscontroller. Also I tried using helper methods but that does not seem to help and sounds very complicated. Anybody kindly help me with this
Actually, I see a few issues with your code.
First off: use meaningful names. You are retrieving tweets pertaining to a certain city. Secondly, in your query method, most of what you are trying to do is retrieving tweets, which should be in the tweet model.
What your code does wrong:
it just builds the sql?
it searches for tweets containing the fixed text %text%, I am assuming that should be a given search-term
it searches for a given user-location %show%, I am assuming that should be the city-name (your params[:show]
What I would suggest:
finding tweets should be in Tweet model
use more smaller methods
for simplicity I assume your search-term is params[:text]
So I would write your code as follows:
class TweetsController < ApplicationController
def index
city = params[:show]
search_term = params[:text]
city_coordinates = Coordinates.where('city=?', city)
#tweets = if city_coordinates.count == 1 && city_coordinates.first.valid_location?
Tweet.for_coordinate(city_coordinates.first)
else
Tweet.for_user_location(city)
end
#tweets = #tweets.where("tweet_text like ?", "%#{search_term}%")
end
end
Do not build the sql yourself, let activerecord do that for you. Also: where is lazy, so you can easily chain them.
Then in your Coordinate model add
class Coordinate < ActiveRecord::Base
def valid_location?
self.latitude != 0 && self.longitude != 0
end
end
and in your Tweet model
class Tweet < ActiveRecord::Base
def self.for_coordinate(coordinate)
bbox = { min_lat: coordinate.latitude - 1.0, max_lat: coordinate.latitude + 1.0,
min_lng: coordinate.latitude - 1.0, max_lng: coordinate.latitude + 1.0
}
Tweet.where("(longitude BETWEEN ? and ?) AND (latitude BETWEEN ? and ?)) OR (user_loc LIKE ?)",
bbox[:min_lng], bbox[:max_lng], bbox[:min_lat], bbox[:max_lat], "%#{coordinate.city}%")
end
def self.for_user_location(city)
Tweet.where("user_loc like ?", "%#{city}%")
end
end
In your self.query method in model, set an argument like this :
class Coordinates<ActiveRecord::Base
def self.query(something)
a = Coordinates.where("city=?", something)
b = a.first
if a.count == 1
latitude = b.latitude
longitude= b.longitude
if(latitude=0 && longitude=0) then
return sql="Select * from tweets where tweet_text LIKE '%text%' AND user_loc LIKE '%show%' order by id desc"
else if (latitude!=0 && longitude!=0)
min_lat = latitude - 1.0
max_lat = latitude + 1.0
min_lng = longitude - 1.0
max_lng = longitude + 1.0
return sql = "Select * from tweets where tweet_text LIKE '%text%' AND ( ((longitude BETWEEN min_lng and max_lng) AND (latitude BETWEEN min_lat and max_lat)) OR (user_loc LIKE '%show%') ) order by id desc"
else
return sql="Select * from tweets where tweet_text LIKE '%text%'"
end
end
end
In your controller, pass your params[:show] as as parameter :
class TweetsController<ApplicationController
def index
#tweets = Coordinates.query(params[:show])
end
end
Now it should work. Thanks
You call Coordinates.query() but you don't actually store the return value.
Try this:
#tweets = Coordinates.query()
I would like to use the following formula to calculate a Bayesian score for each of my products, based on their scores (0-100) and number of votes:
( (avg_num_votes * avg_rating) + (this_num_votes * this_rating) ) /
(avg_num_votes + this_num_votes)
Each product has_many reviews, and each review has a 'score' which is 0-100.
When pulling the list of products from the database what would be the best approach to calculating the Bayesian score to be listed in a table in the view?
EDIT: I am looking for something along the lines of:
Product.each do |product|
#product_bayesian = ((Review.count * Review.average(:score)) + (product.reviews.count + product.reviews.average(:score))/(Review.count+product.reviews.count)
end
but, done in the most efficient way possible, possibly through a join in the controller?
Here is my solution:
def overall
#products = Product.all
#overall_count = Review.count
#overall_average = Review.average(:score)
#unranked = {}
#products.each do |product|
#review_score_raw = product.reviews.average(:score)
#review_score = #review_score_raw.to_int if #review_score_raw
#review_count = product.reviews.count
if product.reviews.count == 0
#bayesian = 0
else
#bayesian = (((#overall_count * #overall_average) + (#review_count * #review_score_raw))/(#overall_count + #review_count))
end
#unranked[product] = #bayesian
end
#ranked = #unranked.sort_by {|key, value| value}.reverse.each_with_index.to_a
end
I have a fairly complex method in my controller that basically outputs data to be used in a view to create a donut graph.
def courses_allocated
course_id = params[:course_id];
client_id = params[:client_id];
override_client_id = get_client_id_for_current_user
unless override_client_id.nil?
client_id = override_client_id
end
category_course_enrollments = CourseEnrollment.select("course_categories.title, COUNT(*) as count").
joins("INNER JOIN courses ON course_enrollments.course_id = courses.id").
joins("INNER JOIN course_categories ON courses.course_category_id = course_categories.id").
group("course_categories.id").
order("course_categories.title")
course_enrollments = CourseEnrollment.select("COUNT(*) as count, course_enrollments.course_id, courses.title").
joins("INNER JOIN courses ON course_enrollments.course_id = courses.id").
joins("INNER JOIN course_categories ON courses.course_category_id = course_categories.id").
group("course_enrollments.course_id").
order("course_categories.title")
unless course_id.blank?
category_course_enrollments = category_course_enrollments.where("course_enrollments.course_id = ?" , course_id.to_i)
course_enrollments = course_enrollments.where("course_enrollments.course_id = ?" , course_id.to_i)
end
unless client_id.blank?
category_course_enrollments = category_course_enrollments.where("courses.client_id = ?", client_id)
course_enrollments = course_enrollments.where("courses.client_id = ?", client_id)
end
#category_data = []
#course_assigned_data = []
#course_assigned_detail_data = []
category_course_enrollments.each do |category_course_enrollment|
#category_data.push([category_course_enrollment.title, category_course_enrollment.count]);
end
course_enrollments.each do |course_enrollment|
not_started = CourseEnrollment.select("COUNT(patient_id) AS total_not_started").
where('started IS NULL').
where('course_id = ?', course_enrollment.course_id).first.total_not_started
in_progress = CourseEnrollment.select("COUNT(patient_id) AS total_in_progress").
where('started IS NOT NULL').
where('completed IS NULL').
where('course_id = ?', course_enrollment.course_id).first.total_in_progress
completed = CourseEnrollment.select("COUNT(patient_id) AS total_completed").
where('completed IS NOT NULL').
where('course_id = ?', course_enrollment.course_id).first.total_completed
#course_assigned_data.push([course_enrollment.title, course_enrollment.count]);
#course_assigned_detail_data.push({'name'=>course_enrollment.title + " Not Started", 'y'=> not_started, 'color'=>'#ff8800'});
#course_assigned_detail_data.push({'name'=>course_enrollment.title + " In Progress", 'y'=> in_progress, 'color'=>'#0088ff'});
#course_assigned_detail_data.push({'name'=>course_enrollment.title + " Completed", 'y'=> completed ,'color'=>'#44cc44'});
end
end
The View for the donut graph (besides the input for a form is:)
<div id="reportcoursesallocatedgraph">
</div>
<script type="text/javascript">
new IS.ReportCoursesAllocated('Course Allocated', <%= raw(ActiveSupport::JSON.encode(#category_data)); %>, <%= raw(ActiveSupport::JSON.encode(#course_assigned_data)); %>, <%= raw(ActiveSupport::JSON.encode(#course_assigned_detail_data)); %>, 'reportcoursesallocatedgraph');
</script>
I want to reuse the logic from courses_allocated from a method in the same class; def dashboard. (The dashboard method basically creates a bunch of different graphs)
Should I make a private method that they can both share?
If the logic is identical, then you can just alias dashboard to courses_allocated. To do that, you can put this right below the courses_allocated action method.
alias dashboard courses_allocated