Rails 5, Postgesql and grouping by association - ruby-on-rails

I'm struggling to get my head around Postgesql and grouping by association.
I have a trial which has_many repetitions. I want to group by a column in repetitions to use in a variable. Currently i have:
#trials = Trial.joins(:repetitions).group('repetitions.repetition_index')
But get this error.
PG::GroupingError: ERROR: column "trials.id" must appear in the GROUP BY clause or be used in an aggregate function
Now from reading up about this i need to include trials.id in my group method, but when I do the output isn't grouped by repetitions.repetition_index anymore. It seems as though it groups by trials.id.
How do I go about making sure that group is only by repetitions.repetition_index?
Update
Trying:
#trials = Trial.joins(:repetitions).select('repetitions.repetition_index,repetitions.treatment_index').group('trials.id, repetitions.repetition_index,repetitions.treatment_index')
And calling:
<% #trials.each do |r| %>
<table class="table table-bordered">
<tr>
<td><%= r.repetition_index %></td>
<td><%= r.treatment_index %></td>
</tr>
</table>
<% end %>
Gives me an output of:
|1|1|
-----
|1|2|
-----
|2|1|
-----
|2|2|
-----
When I'm looking to get:
| |1|
|1|2|
-----
| |1|
|2|2|
-----

If you want to get rid of this error you can do
#trials = Trial.joins(:repetitions).group('trials.id, repetitions.repetition_index')
If you don't want to group by trails.id and want to group by repetitions.repetition_index you have to select only repetitions.repetition_index from query like
#trials = Trial.joins(:repetitions).select('repetitions.repetition_index').group('repetitions.repetition_index')
Let me know if you are clear or not
Update
As per your updated question i think you need something like below. query isn't tested . let me know if its not working
Trial.
joins(:repetitions).
select('repetitions.repetition_index,repetitions.treatment_index').
group_by {
|repetition| repetitions.repetition_index
}

Related

Ruby - how to join output from a Rails command?

There are multiple calculations I'd like to be able to perform on my database, all centred around a user id, but I don't know how to recombine them after.
Let's say I have three tables for User, Purchases, and DiscountUsage. I want to find the last date a user made a purchase and the total number of discounts used. I would run two separate commands:
User.joins(:purchases).group("users.id").select("users.id", "MAX(purchases.purchase_date)")
User.joins(:discount_usages).group("users.id").select("users.id", "COUNT(*)")
I want my final output to be one table though, joined on users.id, but the output from select isn't the right data type to work on with Rails functions. How can I represent that the users.id values from both calls are the same and thus join them based on those columns?
I assume a User may not have any purchases and not all purchases use discount codes.
However, you want a full listing of each user with their last purchase date and total discount usages over all purchases. You may need to use a right join.
Something like:
query = User.select('users.id AS user_id', 'MAX(purchases.purchase_date) AS last_purchase_date', 'COUNT(discount_usages.id) AS total_discount_usages')
.joins('LEFT JOIN purchases ON purchases.user_id = users.id')
.joins('LEFT JOIN discount_usages ON discount_usages.user_id = purchases.user_id')
.group('users.id')
.to_sql
Then in order to grab the fields you could use:
rows = ApplicationRecord.connection.select_all(query).to_hash
https://guides.rubyonrails.org/active_record_querying.html#select-all
This will give you an array of hashes with keys: 'user_id', 'last_purchase_date', 'total_discount_usages'.
...
<% rows.each do |row| %>
<% row.symbolize_keys! %>
<tr>
<td><%= row[:user_id] %></td>
<td><%= row[:last_purchase_date] %></td>
<td><%= row[:total_discount_usages] %></td>
<tr>
...
You can select aggregates from joined tables and access them in the model by using aliases:
#users = User.joins(:purchases, :discount_usages)
.select(
"users.id",
"MAX(purchases.purchase_date) AS latest_purchase",
"COUNT(discount_usages.*) AS discount_usages_count"
)
.group("users.id")
If you want to hydrate the other attributes of the records select users.* instead of just users.id.
<table>
<thead>
<tr>
<th>Id</th>
<th>Latest Purchase</th>
<th>Discount usages</th>
</tr>
</thead>
<tbody>
<% #users.each do |user| %>
<tr>
<td><%= user.id %></td>
<td><%= user.latest_purchase %></td>
<td><%= user.discount_usages_count %></td>
</tr>
<% end %>
</tbody>
</table>
I want my final output to be one table though, joined on users.id, but
the output from select isn't the right data type to work on with Rails
functions.
Not sure quite what you mean here. This example will return a collection of user records just like a normal query.
If you want a "raw" result with just an array of arrays use .pluck instead of .select.

List data from two models

I have two models, Items and Calibrations. Items has many calibrations, meaning that every year the instruments have to be calibrated. Fields, date_calibration and date_expired, are located in the "calibrations" table. (Items: has_many :calibrations, calibration: belongs_to item)
I need list/show all the items that are expiring. I can list all the items without problem of course but, I don't know how to add date_expired to the list.
In the Items controller:
#items = Item.all.order("created_at DESC")
In the Index:
<% #items.each do |item| %>
<tr>
<td><%= item.cod %></td>
<td><%= item.number %></td>
<td><%= item.den_cont %></td>
<td><%= item.branch %></td>
<td><%= item.model %></td>
</tr>
<% end %>
I'm using Aptana and PostgreSQL version 9.1, Ruby 2.1 and Rails 4.1.
Can anyone of you suggest any solution or point me to the right direction?
UPDATE
What should I change to show the item using the sentence below..
Item_controller
Item.includes(:calibrations).where('calibrations.date_expired <= ?' , 2014/07/12)
Index
<% #items.each do |item| %>
Return undefined method each.
ALSO
Any idea on how to show a traffic light depending on how many days left to calibration_date ?? Tks again!
As long you have your relations properly defined in your models I do believe something similar to the following should do the trick!
Item.joins(:calibrations).where(date_expired <whatever condition>)
The equivalent SQL being:
SELECT Items.*
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
With the equivalent SQL of the above statement being (using the includes method):
SELECT *
FROM Items
LEFT OUTER JOIN Calibrations
ON Calibrations.item_id = Items.item_id
WHERE date_expired <whatever condition>
Hope this helps!
Also, if you're not wanting to return any data related to the calibrations table (which it looks like this is the case), I would go with the joins method, however if you are, the includes method would be the way to go (http://tomdallimore.com/blog/includes-vs-joins-in-rails-when-and-where/)
Also, the following may be of interest: http://guides.rubyonrails.org/active_record_querying.html (particularly section 12)!
#items = Item.includes(:calibrations)
.where('calibrations.date_expired <= ?', some_date)
include will join the two tables and allow you to specify conditions on items based on columns from the calibrations table. I think that's what you wanted isn't it?

Average values by group

I have two tables in my web app: one is for Donors (called "donors") and the other is for Donation Amounts (called "donations). When you click on a donor, you can see all of their donations.
I'm trying to average values associated with a particular date, for a particular charity. For example, if these records exist for Donor A:
*Donor A*
Date Donation Amount
05/04/2013 30
05/04/2013 40
05/05/2013 15
05/05/2013 75
I'd like the system to also calculate and display that the average donation amount for 05/04/2013 was 35 and the average donation amount for 05/05/2013 was 45.
Currently I've tried using the group attribute in my donor model:
def self.average_donateperdate
Donation.average(:donateamount, conditions: ["donor_id = ?", #donor], group: "donatedate")
end
This doesn't seem to work. Since I'm new to Rails, I'm not entirely sure whether this is the right way to do it or not. There are a few other posts that touch on this topic, but none that have helped me solve the issue. Any help would be greatly appreciated!
The simplest syntax to do this is:
#donor.donations.group(:donatedate).average(:donateamount)
This will return a hash in the format { 'donatedate' => 'average donateamount' }.
Naturally, this assumes you have a has_many :donations association on your Donor model. A reusable method would look like:
def average_donation_by_date
donations.group(:donatedate).average(:donateamount)
end
And you can now simply call:
#donor.average_donation_by_date
To see this visually, you can call this in your controller:
#average_donations = #donor.average_donation_by_date.to_a.sort_by(&:first)
And your view might contain something like this:
<table>
<thead>
<tr>
<th>Date</th>
<th>Average Donation</th>
</tr>
</thead>
<tbody>
<% #average_donations.each do |date, amount| %>
<tr>
<td><%= date.strftime('MM/dd/yyyy') %></td>
<td><%= amount %></td>
</tr>
<% end %>
</tbody>
</table>
Reference
Rails api - calculate grouped values

Sorting my Rails Table

My controller:
def index
#unique_bug = Rating.find_by_sql("SELECT bug FROM ratings WHERE bug <> '' GROUP BY bug")
end
Rating Model:
def self.metrics_bug_count(bug)
count = where(bug:"#{bug}").count
total = Rating.find_by_sql("SELECT bug FROM ratings WHERE bug <> ''").count
percentage = (count.to_f/total.to_f * 100)
return count, percentage
end
My view:
<table class="table table-bordered">
<tr>
<th><h3>Bug</h3></th>
<th><h3>Total Occurrences</h3></th>
<th><h3>Frequency</h3></th>
</tr>
<tr>
<%#unique_bug.each do |rating| %>
<% count, percentage = Rating.metrics_bug_count(rating.bug)%>
<td><p class="text-center"><h4><%= rating.bug.capitalize %></h4></p></td>
<td><p class="text-center"><h4><%= count %></h4></p></td>
<td><p class="text-center"><h4><%= number_to_percentage(percentage, :precision => 2)%></h4></p></td>
</tr>
<%end%>
</table>
How can I make each row (Bug, Total, Frequency) to each be sortable? I watch the Railscast episode on sortable tables, however the sorting there is done in the controller. How can I sort my table with a more complex find that is being performed in my model.
you can use .order
For example, Rating.order("count DESC").
Besides, I think you can avoid using find_by_mysql and use some rails method to do your query which is more readable and easier.
You can get the unique 1 using .uniq as well.
try have a look at
http://guides.rubyonrails.org/active_record_querying.html

Ruby generate table from date range

I've been having a lot of trouble with this:
For an RPI application, all my results are within one table: results
I'm trying to print a table based on a specific data range: 2011-07-31..2012-07-01
In my controller:
class SeasonsController < ApplicationController
def s2012
#results = Result.all
#s2012 = Result.where(:date => (2011-07-31)..(2012-07-01))
end
end
In my view:
<table>
<tr>
<th>Event ID</th>
<th>Date</th>
</tr>
<% #s2012.each do |result| %>
<tr>
<td><%= result.event_id %></td>
<td><%= result.date %></td>
</tr>
<% end %>
</table>
This doesn't output any errors (small miracle), but nothing is displayed in the view. #results = Result.all prints all the whole table just fine. But how can I limit it to a specific data range?
I know you're trying to do this all ActiveRecordy, but imo it's actually less readable than if you just wrote the SQL where clause yourself.
Result.where('date BETWEEN ? AND ?',
Date.parse('2011-07-31'),
Date.parse('2012-07-01'))
maybe try:
Date.parse('2011-07-31')..Date.parse('2012-07-01')
In your current example rails will think you are doing arithmetic. You are looking at a date between the number 1973 and 2004. Instead you want to convert them to date objects first. Do
#s2012 = Result.where(:date => DateTime.parse('2011-07-31')..DateTime.parse('2012-07-01'))

Resources