Sorting multiple models in one view table - ruby-on-rails

I have a model which has various different types of information associated with it.
Opportunity.rb
has_many :notes
has_many :emails
has_many :calls
I currently have 3 tables on the show page where in the controller I call
#notes = #opportunity.notes.sorted
#emails = #opportunity.emails.sorted
#calls = #opportunity.calls.sorted
And display them in 3 tables
I'd like to sort them chronologically so I can get a timeline.
Each one of the models has date, and I want to sort by the date then return the objects in date order.
i.e.
The table goes:
Note | Content | 11-Feb | Bob
Call | Content | 07-Feb | Dave
Email | Content | 04-Feb | Steve
Call | Content | 03-Feb | Dave
Note | Content | 01-Feb | Margaret
I can't work out how I merge those tables in the controller into an object I can use in the view so that I can create a table, sort on the date ("created_at") and still keep everything looking nice and clean.

The simplest way would be to combine the 3 collections into an array and sort it by created_at. In your controller:
#combined = (#notes + #emails + #calls).sort_by {|record| record.created_at}
You can then use #combined in your view to show the timeline.

Related

Create line chart grouped by two fields

I'm trying to build a line chart with Chartkick that show the number sales by sellers in days, but I don't know how can I do this.
I'm already showing in a table the sellers name and your sales with this code:
#sales = Sale.where(created_at: DateTime.current.beginning_of_month..DateTime.current.end_of_month)
#sales_seller = #sales.joins(:user).select("user_id, COUNT(sales.id) AS total").group('user_id')
This returns:
+--------+-------+
| Name | Total |
+--------+-------+
| Jack | 10 |
| Kevin | 3 |
| Andrea | 11 |
+--------+-------+
How can I put the sales.created_at in this scenario?
My models
Sales
belongs_to :user
User (seller)
has_many :sales
This should work.
I used num_of_sales as the column in sales which contains the number of sales, you must fix it based on your Sale model:
data = #user.joins(:sales).select('users.name as name, sales.num_of_sales').group(:name).sum(:num_of_sales)
It is grouped by user name, but maybe it is better to group by user_id.
To filter sales, just chain a where method:
data = #user.joins(:sales).where(.....).select(.....
Then you can plot as line with:
<%= line_chart data %>

Rails create a table dynamically considering another table

I'm new to ruby and I'm sorry if I haven't described the issue very well :)
So, I want to know if there is a possibility in Rails for the following:
For example I have a table in which I keep some books. Let's call it Books.
This table contains the following fields: name, nr_of_pages.
How to create a table called Pages which has a number of fields equal to the value from nr_of_pages of a certain book. (the value from the Books' field is introduced by user in "new.html.erb" view)
Books
-----------------------
| name | nr_of_pages |
-----------------------
Pages
-------------------------------------------------
| book_id | page_1 | page_2 | | page_3 | ...etc |
-------------------------------------------------
Of course Pages:
belongs_to :book
And Book:
has_many :pages
And how to fill the second table with data because these two models have different views?
pages would actually be the page content like this:
Books
-----------------------
| name | nr_of_pages |
-----------------------
Pages
-------------------------------
| book_id | page_no | content |
-------------------------------
Then when you create a page, you could say:
Page.create(content: "This is the first page of text", page_no: 1, book: some_book)
Then in your controller you could do something like:
#pages = Book.find(by_some_id).pages.order(page_no: :asc)
This will give you an array of pages to loop through in ascending order, giving you the ability to use an #each loop in your view to display all the pages.

ruby iteration based on table values

The project:
I'm creating a dynamic reporting system.
Metrics define the purpose of the reported data. For example:
"13 house fires."
The admin will define "house fires" as a metric through one form, the reporters will simply add the "13" through a different form.
However, there's another level: I want the reporting to be verbose across connected data points:
"13 house fires during January affecting 42 individuals (or 16 families) "
The verbage is stored in a table "metrics", the data is stored in a table "metrics_data"
Here's the metrics table from the sample above:
metric_id | parentID | childID | prefix | suffix | program_id
1 | 1 | 1 | | house fires | 1
2 | 1 | 2 | during | | 1
3 | 1 | 3 | affecting | individuals | 1
4 | 1 | 4 | (or | families | 1
The key for the sentence-based organization is the parentID - childID relationship.
Here's the metrics_data table:
metric_id | value | date
1 | 13 | 01/01/12
2 | nil | 01/01/12
3 | 42 | 01/01/12
4 | 16 | 01/01/12
The goal:
I want to organize the view (looping through the parentID) to show the metric verbosely:
Program #1
(parentID: 1): "# house fires during (date) affecting # individuals (or # families) "
(parentID: 2): "# shelters during (date) providing # overnight stays (for # individuals) "
Program #2
(parentID: 1): "# new volunteers recruited during (date) "
(parentID: 2): "# volunteers served # hours during (date) "
The code:
class Program < ActiveRecord::Base
has_many :programs_metrics
has_many :metrics, through: :programs_metrics
end
class Metric < ActiveRecord::Base
has_many :programs_metrics
has_many :metrics, through: :programs_metrics
end
Partial _program.html.erb (for programs/index.html.erb):
<% program.metrics.each do |metrics| %>
<div class="row offset1">
<% program.metrics.each do |pid| %> #how do I loop here based on parentID, sorted by childID?
<%= pid.prefix %>
#
<%= pid.suffix %>
<% end %>
</div>
<% end %>
I know I could split this out into a separate table and define the relationship between parentID and childID between two tables, but it seems overly complex to add another relationship layer.
You should probably treat the "parent" and "child" elements like attributes to Metrics, rather than trying to reuse the same model. An alternative would be to create another model called "Events" or whatever, and these Events are related to programs, and Metrics are related to Events. I would probably create a "Metric Type" as well instead of having a prefix or suffix associated directly with each record.
I think that trying to treat all the Metrics at the same level, and trying to loop through them with parentID and childID to create a sentence is infinitely more complicated than just adding another layer to the relationship.
Hopefully this is enough of a push...

How do I split an array (or csv string) into separate columns in the db, in Rails 3?

I have a model (share) in which I save who can edit a lesson.
It is separated from the user model, and from the lesson model.
The data is currently saved as
|share_id | lesson_id | shared_ids |
| 2 | 23 | "45,66,21" |
Where "shared_ids" are the user ids that can edit the lesson
I would like to split it into
|share_id | lesson_id | shared_ids |
| 2 | 23 | "45" |
| 3 | 23 | "66" |
| 4 | 23 | "21" |
I have managed to split the shared_ids, but it is currently saved as one array inside the table :
|share_id | lesson_id | shared_ids |
| 2 | 23 | "45","66","21" |
Thanks for your help.
---- Edit ----
The Share class looks like that:
class Share < ActiveRecord::Base
attr_accessible :shared_token, :shared_ids
belongs_to :lesson
attr_reader :shared_token
serialize :shared_ids
# #Todo: save each seperately #
def shared_token=(ids)
self.shared_ids = ids.split(",")
end
end
---- Edit 2 -----
The form for shared_token in the lesson view:
<%= f.fields_for :shares do |builder| %>
<%= builder.label :shared_token, "Type the user names you wish to share the lesson with:" %>
<%= builder.text_field :shared_token, "data-pre" => #shared_ids %>
<% end %>
Since Shares are children of Lessons, I think you might want to write a methed in your Lesson model that would iterate through the ids in a list (which you've already figured out how to generate) and create a Share for each id. something like...
def build_shares_from_list(ids)
ids.each do |id|
#share = self.shares.build({:share_ids => id})
#share.save
end
end
Then call that from your Lesson controller, and point your form to the appropriate controller action.
how about a pure SQL solution (havent tested the code, just a general direction)
remove the quotes
update shares set share_ids = replace(share_ids, '"', '');
create a temp table which will replace the old one once we're done
create table _shares (share_id, lesson_id, shared_ids);
add the first shared_id from existing lists:
insert into _shares select share_id, lesson_id, substring(shared_ids, 0, locate(shared_ids, ",")) from shares where shared_ids like '%,%';
remove the first shared_id from the existing lists:
update shares set shared_ids = substr(shared_ids, locate(shared_ids, ",") + 1) where shared_ids like '%,%';
repeat 3, 4 until 0 records updated - no more shared_ids with "," - all are single values
add the remaining values - last value in the original ids list, or values which had just one value to begin with:
insert into _shares select share_id, lesson_id, shared_ids from shares;
replace tables
drop table shares; alter table _shares rename shares;
(or, if there are constraints there: truncate table shares, insert into shares select * from _shares;

Approach to limit the visibility of data

Ok, suppose to have this db schema (relation):
|User | (1-->n) |Customer | (1-->n) |Car | (1-->n) |Support |
|--------| |---------| |-----| |-----------|
|id | | user_id | |Brand| |Description|
|username| |lastname | |PS | |Cost |
|password| |firstname| |seats| |hours |
|... | |.. | |... | |... |
The table User is generated by Authlogic.
I have 2 registred users, each one has his customers, etc. . With Authlogic I'm able to allow only authenticated users to reach controllers/views. That's fine, that's what Authlogic is made for.
Now I need to be sure that the user#1 will never reach informations belonging to customers of user#2.
In other words:
if the user#1 goes to http://myapp.com/cars he will see the list of cars belonging to customers of user#1
if the car with the id=131 belongs to the customer of user#1, only user#1 have to be able to reach this information (http://myapp.com/car/1). If the user#2 insert in the browser the same link he doesn't have to be able to see this information.
Some people suggested me to create a relation between the user and each db table in order to check if a record is associated to the current_user.
What do you think? What is the best approach/solution?
So you have 2 things:
In index page of cars controller only cars which belong to the current user should be shown.
You want to restrict show pages to the owner.
As for the index i suggest something like:
def index
# find logic
#cars = Car.find(:all,:conditions=>{:customer_id => current_user.customers.collect(&:id)})
# whatever else
# ...
end
And the show page:
def show
# .....
# after the find logic
redirect_to :index unless current_user.customers.collect(&:id).include? #car.customer_id
# whatever else
# ...
end
This approach is ok for most of the cases, however a better approach for performance is to add a user_id column to the costumers table, this is called denormalization but it's acceptable for performance wise.

Resources