Rails, two dimensional table, pivot, nested hash loops - ruby-on-rails

I am building grade-book report - a two dimensional table that shows lesson names going horizontally and a list of students going vertically.
Student Name | LessonID x | LessonID x | LessonID x
Joe 95% 95%
Mary 80% 80%
Sam 80% 80%
My data is in a table that has these fields:
student_id, lesson_id, grade_in_pct, grade_in_pts, grade_high, grade_low, grade_median
The total number of students and lessons is not fixed.
I considered using ruport/acts_as_reportable or mysql pivot procedure, however it looks like the pivot only gives me one dimension. So that's not going to work, because in my view I want to add mouse-over features and conditional formatting to show more info on each grade.
So I think my only option is to generate a nested hash and then loop through it in the view. What are your thoughts? Could someone suggest a way to build a nested hash? Would it be too processor intensive to loop through 250 rows (~50 students, 5 lessons each)?
I am stuck. Please help. Thanks!

This is how I would do it:
MODELS:
Student Model:
has_many: Grades
has_and_belongs_to_many: Lessons
Lesson Model:
has_many: Grades
has_and_belongs_to_many: Students
Grade Model:
belongs_to: Student, Lesson
CONTROLLER:
#data = Student.all
#lessons = Lesson.all
VIEW:
header_row
#data.each do |student|
#lessons.each do |lesson|
student.grades.find_by_lesson(lesson).some_data

Related

Calculate SUM based on a column from associated model

I have a table called vehicles, which has a column called vehicle_id and price.
I have a table called sales, which references the vehicles table. It has the columns vehicle_id (references the vehicle table) and sale_status which can equal to 'sold' or 'loan'.
I am trying to calculate the total price of vehicles which equal to 'sold' in the sales table. Help is much appreciated!
This is what I have so far but it returns the wrong number.
vehicle.rb:
def self.vehicles_price_sum
vehicles_sold.sum(:price).to_f
end
def self.vehicles_sold
Vehicle.where(id: Sale.where(sale_status: 'Sold'))
end
You can try with a subquery (which is close to you solution, yet you need to provide a column name explicitly for in clause with select, otherwise sales' id column is going to be provided):
Vehicle.where(id: Sale.where(status: "Sold").select(:vehicle_id)).sum(:price)
# SELECT SUM(`vehicles`.`price`) FROM `vehicles` WHERE `vehicles`.`id` IN (SELECT `sales`.`vehicle_id` FROM `sales` WHERE `sales`.`sale_status` = 'Sold')

How to multiply different table in Ruby on Rails

I have 3 tables:
table 1 has column price
tabel 2 has column total
table 3 has column total_price
Question:
I want the column total_price is the result from multiplication of
price and total:
total_price = price * total
im very newbie.
Do it like this:
Table1.all.each_with_index do |el, index|
Table3.create(total_price: el.price * Table2.all[index])
end
I am not going into all the details like relations, but the comments below make the point, but let's assume that these tables might have been just an extraction from Excel table or something like that.
Of course this is only if you want to create the third table. I am assuming that is what you want (?)

Stuck - Joining 3 tables, performing function on

Forgive me as newbie. I have a multi-level join table around the following...
Category
has_many :products
Products
has_many :sales
Sales
belongs_to :product
Within the category I am looking to display a list of each of the best-selling products (as determined by the number of sales, which are individually logged).
I can print a total sum of sale value on the product page OK (which is column sales.sum - my column is called sum), but cannot print a full list of all products fitting into a category, whilst performing a sum function on the sales.sum column for each product.
I currently have this code in category controller;
def show
#all = Category.joins(products: :sales)
end
I think this is right but I cannot get the right view. Appreciate this may not be the best way to present the question on SO, but tried many different ways to print it in my view file, but really stuck.
Could anyone point me in the right direction?
Thank you in advance! :-)
EDIT - to make it a bit clearer what I'm trying to achieve...
Category -> Products -> Sales
Want to display a list of each Product that belongs to a Category (by category_id), but put them in list of total Sales (which belongs to Products by product_id. Column is called 'sum' within the Sales table and shows value such as $100 Product 1, $50 Product 2, $40 Product 1, et cetera).
So, multi level join.
For those interested, finally worked out the answer;
<ol>
<%= #category.products.each do |su| %>
<li><%= su.name %> <%= su.sales.sum(:sum) %></li>
<% end %>
</ol>
Hope this helps someone else!

Many-to-many data model puzzle

Given
3 ActiveRecord models:
class Dealer < ActiveRecord::Base
end
class CarMake < ActiveRecord::Base
has_many :car_models
end
class CarModel < ActiveRecord::Base
belongs_to :car_make
end
non of either CarMake or CarModel should have additional foregin keys (making managing of makes/models isolated and independent),
adding join tables or associations is not prohibited and is welcome.
Problem
I need dealer to have assigned a desired subset of available car_makes and desired subset of car_models for each of respectively assigned car_make.
Example
Given this data:
car_models car_makes
------------------------ -------------
id car_make_id title id title
1 1 Flex 1 Ford
2 1 Fiesta 2 Chevrolet
3 1 Focus 3 Mercury
4 2 Impala 4 Nissan
5 2 Suburan
6 3 Milan
7 4 Altima
What I want is to do:
dealer1.his_makes # => [Ford, Chevrolet, Mercury]
dealer1.his_models # => [Flex, Fiesta, Impala, Milan]
dealer2.his_makes # => [Ford, Mercury, Nissan]
dealer2.his_models # => [Fiesta, Focus, Altima]
My question is:
Which associations/tables should I add to achieve this?.
Add an Inventory model that belongs to Dealer, CarModel, and CarMake. Toss in a 'quantity' field just for fun.
You could argue that CarModel isn't necessary, but if it's a common query, seems like a reasonable spot to de-normalize.
A join table between dealer and car makes would allow you to specify "desired car makes by dealer". If all the desired models were of the make specified in this table then I'd create a join table between DealerCarMakes and CarModels to specify the desired models for that make for that dealer.
While you could use a single table, there would be some issues:
You'd need a distinct on the query that retrieves desirable car makes, which often indicates a lack of normalisation.
Being able to specify a desirable car make would be dependent on there being a desirable car model for that make -- maybe not an issue here, but definitely an issue in other cases where there is not such a dependency.
You could not have attributes at the DealerCarMake level, such as "desirable since date" or a value range.

Rails has_many association count child rows

What is the "rails way" to efficiently grab all rows of a parent table along with a count of the number of children each row has?
I don't want to use counter_cache as I want to run these counts based on some time conditions.
The cliche blog example:
Table of articles. Each article has 0 or more comments.
I want to be able to pull how many comments each article has in the past hour, day, week.
However, ideally I don't want to iterate over the list and make separate sql calls for each article nor do I want to use :include to prefetch all of the data and process it on the app server.
I want to run one SQL statement and get one result set with all the info.
I know I can hard code out the full SQL, and maybe could use a .find and just set the :joins, :group, and :conditions parameters... BUT I am wondering if there is a "better" way... aka "The Rails Way"
This activerecord call should do what you want:
Article.find(:all, :select => 'articles.*, count(posts.id) as post_count',
:joins => 'left outer join posts on posts.article_id = articles.id',
:group => 'articles.id'
)
This will return a list of article objects, each of which has the method post_count on it that contains the number of posts on the article as a string.
The method executes sql similar to the following:
SELECT articles.*, count(posts.id) AS post_count
FROM `articles`
LEFT OUTER JOIN posts ON posts.article_id = articles.id
GROUP BY articles.id
If you're curious, this is a sample of the MySQL results you might see from running such a query:
+----+----------------+------------+
| id | text | post_count |
+----+----------------+------------+
| 1 | TEXT TEXT TEXT | 1 |
| 2 | TEXT TEXT TEXT | 3 |
| 3 | TEXT TEXT TEXT | 0 |
+----+----------------+------------+
Rails 3 Version
For Rails 3, you'd be looking at something like this:
Article.select("articles.*, count(comments.id) AS comments_count")
.joins("LEFT OUTER JOIN comments ON comments.article_id = articles.id")
.group("articles.id")
Thanks to Gdeglin for the Rails 2 version.
Rails 5 Version
Since Rails 5 there is left_outer_joins so you can simplify to:
Article.select("articles.*, count(comments.id) AS comments_count")
.left_outer_joins(:comments)
.group("articles.id")
And because you were asking about the Rails Way: There isn't a way to simplify/railsify this more with ActiveRecord.
From a SQL perspective, this looks trivial - Just write up a new query.
From a Rails perspective, The values you mention are computed values. So if you use find_by_sql, the Model class would not know about the computed fields and hence would return the computed values as strings even if you manage to translate the query into Rails speak. See linked question below.
The general drift (from the responses I got to that question) was to have a separate class be responsible for the rollup / computing the desired values.
How to get rails to return SUM(columnName) attributes with right datatype instead of a string?
A simple way that I used to solve this problem was
In my model I did:
class Article < ActiveRecord::Base
has_many :posts
def count_posts
Post.where(:article_id => self.id).count
end
end
Now, you can use for example:
Articles.first.count_posts
Im not sure if it can be more efficient way, But its a solution and in my opinion more elegant than the others.
I made this work this way:
def show
section = Section.find(params[:id])
students = Student.where( :section_id => section.id ).count
render json: {status: 'SUCCESS', section: students},status: :ok
end
In this I had 2 models Section and Student. So I have to count the number of students who matches a particular id of section.

Resources