Chartkick is amazing, and I can do super simple things like
<%= column_chart Device.joins(:reviews).group(:brand).average(:average_rating) %>
and get results like:
Now, the problem is that the labels aren't fed through to chartkick. This is what chartkick uses to render the chart:
Chartkick.ColumnChart("chart-5", {"#<Brand:0x00000008a3c048>":"78.8","#<Brand:0x0000000879e840>":"80.70000712076823","#<Brand:0x0000000853e9d8>":"81.11111111111111","#<Brand:0x000000082808e0>":"73.42857142857143"}, {});
So, the big question is... How can I use this super easy querying method but get the brand names on the chart?
I tried:
<%= column_chart Device.joins(:reviews).group(:'brand.name').average(:average_rating) %>
...and other variations to no avail...
Basically I just needed to make a small change to my query:
<%= column_chart Device.joins(:reviews).includes(:brand).group(:'brands.name').average(:average_rating) %>
Then I ended up re-ordering this by average ratings instead of brand name:
devices = Device.joins(:reviews).includes(:brand).group('brands.name').average(:average_rating)
arr = styles.to_a
arr.map! { |pair| [pair.first, pair.second.to_f] }
arr.sort! { |a, b| a.second <=> b.second }
#sorted_hash = ActiveSupport::OrderedHash[arr]
View:
<%= column_chart #sorted_hash %>
Bingo:
Related
I have two models Article - :id, :name, :handle Comment - :id, :name, :article_id
My query looks like data = Article.select("articles.*, comments.*").joins("INNER JOIN comments on articles.id = comments.article_id")
Now both the models have conflicting fields. Ideally I would want to be able to do something like data.first.comments.name or data.first.articles.name.
Note I am aware of option of doming something like articles.name as article_name but I have some tables with around 20 columns. So don't want to do that.
The example you are showing is barely utilising the Rails framework at all. It seems you are thinking to much of the database structure instead of thinking of the result as Ruby Objects.
Here is what I would suggest you do to get access to the data (since you are using Rails, I assume it is for a webpage so my example is for rendering an html.erb template):
# In controller
#articles = Article.includes(:comments)
# In views
<% #articles.each do |article| %>
<h1><%= article.name %></h1>
<% article.comments.each do |comment| %>
<p><%= comment.name %></p>
<% end %>
<% end %>
But if you just want all the column data into a big array or arrays (database style), you could do it like this
rows = Article.includes(:comments).inject([]) { |arr, article|
article.comments.inject(arr) { |arr2, comment|
arr2 << (article.attributes.values + comment.attributes.values)
}
}
If you don't know how the inject method works, I really recommend to read up on it because it is a very useful tool.
I'm stuck on a tiny problem regarding chartkick. I have a rail app where you can create different currencies. You can then create a expense with a title, a amount and choose the currency from a list and the user_id. The relations are made and working. I have in my user controller something like this :
#user_spendings = #current_user.spendings.all.order('date DESC').paginate(:page => params[:page], :per_page => 15)
#sums_by_currency = Currency.joins(:spendings).
select(:symb, 'SUM(spendings.amount) AS amount').
where(spendings: { id: #user_spendings.map(&:id) }).
group(:symb)
And in my show view (as I want the expense from each user to be shown there) something like this :
<% #sums_by_currency.each do |currency| %>
<%= '%.02f' % "#{currency.amount}" %> <%= "#{currency.symb}" %>
<% end %>
That shows me the sum for each spending depending on the currency.
I would like to use this total and use chartkick to display the spending, with the date when this spending has been created.
I've tried several things already
First I went with this just to see :
<% #sums_by_currency.each do |currency| %>
<%= bar_chart currency.amount %>
<% end %>
Well I have to charts appearing but nothing shows up. Maybe the loop isn't the solution. Then I thought about the .map but I don't really know how to put that in place to be honnest.
I tried this aswell :
<%= line_chart #current_user.spendings.group(:date).sum(:amount) %>
That shows me the total spendings from all the currencies. I have to find out how to split all the currencies in different charts and show only the total amount from each currency.
If anyone can give me a clue I would appreciate it.
Thanks alot.
Ok guys I got it !
Took me 2 days only...
For the one interested in the answer here is what I did. I actually didn't change anything in the controller and I let the #sums_by_currency like it is.
Instead I went for that :
<%= column_chart #current_user.spendings.all.joins(:currency).group('currencies.symb').group_by_month(:date, format: "%B %Y").sum(:amount) %>
Give me all the spendings from the current_user from where I joined the currency that I grouped by symb. Then I grouped everything by month and I get the sum from the amount.
Yeah, you need to pass a set of data to the chart, not lots of individual pieces of data for individual charts (ie what you've got with the loop). Using map to convert your currency-objects to their amounts makes sense eg
<%= bar_chart #sums_by_currency.map{|c| c.amount } %>
or if you need a name something like:
<%= bar_chart #sums_by_currency.map{|c| {name: c.unit, data: c.amount} } %>
Where unit is whatever currency unit eg AUD
I currently am working on my first rails project after working through multiple tutorials.
I am trying to build a webpage to track my savings goal for the next 6 months.
essentially the site consists of a form input where I input the date I put money into savings and the amount. This site then updates a line graph to show the current savings vs. the goal line.
So far I have successfully built the form and database (using postgresql) and have created the chart which displays the goal and accepts input from the form.
The problem I have is that the input values are displaying as discrete numbers and not a cumulative sum up that month (i.e. week 4 should be the sum of savings from weeks 1 to 4).
I considered initially having a 3rd column in the db called amount_cum and put some lines in the page load method in the controller, that means the database would recalculate at each page load which may be slow...
Any advise would be appreciated, should I be looking to modify the model or the controller?
Lay on the wise words
class PagesController < ApplicationController
def home
#pages = Pages.new
#progress = Pages.order(:week)
#goal = Goals.all
end
def create
#pages = Pages.new(pages_params)
weeks_year=Date.parse(params[:pages][:week]).strftime("%U")
#pages = Pages.new(pages_params.merge(:week => weeks_year))
if #pages.save
flash[:success]="Savings Uploaded Successfully"
redirect_to root_path
else
flash[:danger]=#pages.errors.full_messages.join(", ")
redirect_to root_path
end
end
private
def pages_params
params.require(:pages).permit(:week, :amount)
end
end
And model file:
class Pages < ActiveRecord::Base
validates :week, presence: true
validates :amount, presence: true
end
my view file - I use chartkick gem to generate the chart:
<%= line_chart [
{name: "Amount Saved", data: #progress.group(:week).sum(:amount)},
{name: "Goal Savings", data: #goal.group(:week).sum(:amount) }] %>
Screen shot of the application so you can see how the graph is to be displayed against the goal.
link
In that case, you could use inject in the view when generating the chart:
<% saved = [] %>
<% #progress.inject(0) do |sum, page| %>
<% current = sum + page.amount
<% saved << current %>
<% current # the return value is passed in as `sum` in the next iteration %>
<% end %>
<%= line_chart [
{name: "Amount Saved", data: saved},
{name: "Goal Savings", data: #goal.group(:week).sum(:amount) }]
%>
Shorter but a little awkward:
<% saved = [] %>
<% #progress.inject(0) { |sum, page| (saved << sum + page.amount).last} %>
<%= line_chart [
{name: "Amount Saved", data: saved},
{name: "Goal Savings", data: #goal.group(:week).sum(:amount) }]
%>
You didn't post any info on the Goal model, but you could use a similar technique if you are also looking to plot the incremental increase in the goal each week.
EDIT:
If you need an array of arrays, you could try something like this (forgive me, I'm doing this on my phone)
<% #progress.inject(0) { |sum, page| (saved << [page.week, sum + page.amount]).last.last} %>
EDIT 2:
I don't think inject should modify the original #progress array (if that's what you're asking), but for the sake of argument (and diagnostics), try the following (we can certainly pretty this up if it works):
<% saved = [] %>
<% cum_sum = 0 %>
<% #progress.map do |page| %>
<% cum_sum += page.amount %>
<% saved << [page.week, cum_sum] %>
<% end %>
<%= line_chart [
{name: "Amount Saved", data: saved},
{name: "Goal Savings", data: #goal.group(:week).sum(:amount) }]
%>
There is a simpler way to calculate sums in Rails - you don't have to use inject, you can simply call sum on the array of records. In your case it would be something like:
#progress.sum(:amount)
However, the way I interpreted your question on the first read, I thought that, you were trying to create an array of partial sums from an array of actual sums. I don't know if that's of any use for you, but anyway, here's how I would do that using Ruby's each_with_object and with_index and Rails' sum:
amounts = [100, 200, 150, 300, 120]
partial_sums = amounts.each_with_object([]).with_index do |(_, obj), i|
obj << amounts[0..i].sum
end # => [100, 300, 450, 750, 870]
# Examples:
partial_sums[0] = 100 # 1st sum - just the first element
partial_sums[2] = 450 # 3rd partial sum
partial_sums[-1] = 870 # the total of all the amounts from the array
Hope you find that useful.
I am trying to make a chart to display a users weight, using highcharts. I have all of the correct js files included and when passed a simple array of integers, it works just fine.
However I am unsure, having read the highcharts docs, how to pass the correct data into the series options using rails.
my user model is as follows:
A user has many weigh_ins, which has weight:float user_id:integer id:integer created_at:datetime fields. (if this is relevant)
Below is my view, as you can see i have tried to iterate through the users weights and then pass that as the data. I know it needs to be fed json and an array, but im not sure how to format my data to fit in that way.
borderWidth: 0
},
series: [{
pointInterval: <%= 1.day * 1000 %>,
name: 'weight',
data: <% #user.weigh_ins.each do |weight| %>
<%= weight.weight.to_json %>
<% end %>
}]
If there is any more code that needs posting, just shout.
any suggestions would be greatly appreciated.
Thata defo on the right track, however, the data is dynamic so a user can update their weights everyday, so the chart needs to update aswell. If i use the square brackets like above then the graph doesnt even render. Now i have this but it doesnt produce the correct output either. I need to find the weigh_ins for the correct user, then iterate through all of them and select the weight figure which is a float. Not sure how to tackle it.
data: <% user = #user.weigh_ins %>
<% user.each do |user_weight| %>
[<%= user_weight.weight %> ]
<% end %>
I'm something similar with this code:
<% bio_array = Array.new(#bio.size) %>
<% #biometrics.each_with_index {|x, index| %>
<% bio_array += [x[:weight]] %>
<% } %>
(... some code ...)
series: [{
name: 'Some Name',
data: <%= bio_array %>
Please note that this code is not optimized for you case (in my case i have other logic that i omitted for being useless to your case - i had a multidimensional arrays with [data and value]).
if you want to parse data like that , you don't really have to use Json , becoz since yo don't update dynamically , you can just echo it like [34,32,24,25,12] bt first you hv to make that String from Ruby , and that's exactly what is json is used for too , check your Json output,
btw try echo some Ruby String like
=> 1,2,3,4,5 [<% echo str %> ]
and if your json out put hv no squre brackets [] then try adding them
data:[ <% #user.weigh_ins.each do |weight| %> ]
How to add "0," after each loop in Ruby (At the data: part of the series)
So first loop would
show only the value
next loop 0,value
next loop 0,0,value
next loop 0,0,0,value
etc..
series: [
<% #prot = ProjectTask.where("project_id = ? AND taskType = ?" ,#project.id, "Pre-Sales")%>
<% #prot.each do |prt| %>
<% hoursSum = 0 %>
{
name: '<%= prt.task_name%>',
data: [(here after each loop of #prot i want to add "0," here)<% #taskHours = Effort.where(:project_task_id => prt.id) %>
<% #taskHours.each do |th| %>
<% hoursSum = hoursSum + th.hours %>
<% end %>
<%= hoursSum%>
]
},<% end %>
<% #prot.each_with_index do |index, prt| %>
...
<%= "0," * index %>
...
<% end %>
This logic seems too complex to be in a view - I would migrate it to a helper function that builds up the data array and call that instead.
I think you'll find it a lot easier to do what you want then without having to deal with all the clutter of the erb tags etc. There's lots of ways you could do it - Yossi's suggestion of using each_with_index is a perfectly good one.
Two little things though - I would advise against shortened names for stuff like #prot - just call it #project_task. It's more readable and you can guarantee you call it the same thing throughout your code (instead of having some places where you call it #prot, others where it's #ptask etc) which will save you more time than you lose typing a longer name, I promise you.
Also - you use camelCase for some of your variables - I would advise sticking with the Ruby convention of snake_case.