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...
Related
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 %>
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.
I have a question about joins (and has_many belongs) ...
I have 3 tables in the database.
Advertisers
id
name
Categories
Linked with Advertisers (gem awesome_nested_set)
Comments
id
advertiser_id
comment
recommend
I can not see how many comments there are on the advertiser, and how many of these comments are recommended.
Advertisers Data
1 | abc
Comments Data
id | a_id | text | rec
---+------+---------+-----
1 | 1 | blabla | 1
2 | 1 | blablab | 1
3 | 1 | blablac | 1
4 | 1 | blablad | 0
In this case there are four comments and three of them are recommended.
In my view, I need to retrieve the following
Listing Category abapai
Advertiser name: abc
There are 3 comments recommended
a total of 4
Could someone help me?
In the comments model you can use scope method to filter for recommended comments. Like this:
scope :recommended_comments, where("recommended = '1'")
Then in the Advertiser view you could use it like this (I gussed some attribute names):
Listing Category <%= #advertiser.category.name %>
Advertiser name: <%= #advertiser.name %>
There are <%= #advertiser.comments.recommended_comments.count %> comments recommended
a total of <%= #advertiser.comments.count %>
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;
I am new to coding (never taken a CS class and have a very very minimalist understanding of the MVC architecture) and working on a test project to learn it :-) I want to setup some tables tables:
Table 1:
Table_1 ID | Name | Attribute_1 | Attribute_2 | Attribute_3 | etc
1 | Blah | attribute 1 | attribute 2 | attribute 3 |
Table 2:
Table_2 ID | Skill |
1 | nun_chucks |
2 | bow |
3 | arrow |
Table 3:
Table_3 ID | Primary_1 ID | Primary_2 ID |
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
And then be able to visualize the data so it reads like:
Name | nunchucks, bow, arrow | attribute 1 | attribute 2 | attribute 3 |
I have used Scaffold to create the MVC for Table 1 & 2 and have the #page for each one respectively. I am not sure how to combine them to create table 3 and then make the data show as I am asking.
Thanks!
This is a good reference for what you're doing: http://guides.rubyonrails.org/association_basics.html
INNER JOIN is what you want which is a database operation.
Take a look in your database servers manual and read up on it.
Essentially it takes two tables, joins them by a given column (for instance, Table_2.ID and Table_3.ID) and spits out a summary of both tables joined leaving out columns that don't match across the tables.
Given your example,
and the following contoller in Rails 3
class SomeController < ActionController
def show
#table_1 = Table1.find(params[:id],:include => :table2)
# Table1 and Table2 are your models
# where table2 would be your has_many_and_belongs_to_many assoc in Table1
end
end
you could write this up in show.html.erb like
<%= #table_1.name %>
<%= #table_1.table_2.map(&:skill) %>
<%= #table_1.attribute_1 %>
<%= #table_1.attribute_2 %>
<%= #table_1.attribute_3 %>