I'm using chartkick to create some line charts to visualize data in my Rails app. At the moment, however, it shows ALL of the data in my database. Obviously this isn't always ideal. I'd like to give the user the ability to specify the date range of the data shown. I would use the created_at value of the record.
I cannot find any instructions in the documentation on how to do this.
https://github.com/ankane/chartkick.js?files=1
I have created a date picker which will give me a URL like http://localhost:3000/metrics?utf8=%E2%9C%93&search%5Bdate_from%5D=2019-11-01&search%5Bdate_to%5D=2019-11-17&commit=Search
Can chartkicker somehow access those parameters?
In my code a Club has many DailyMetrics, and I've created a metrics action in the clubs controller.
View
<%= javascript_include_tag "https://google.com/jsapi" %>
<div class="row">
<h1>Metrics</h1>
<div class="pull-right range-query">
<%= form_tag metrics_path, method: :get do %>
<%= text_field_tag 'search[date_from]', #search.date_from %>
<%= text_field_tag 'search[date_to]', #search.date_to %>
<%= submit_tag 'Search', class: 'btn search-button' %>
<% end %>
</div>
</div>
<h3>Total active students</h3>
<%= line_chart #club.daily_metrics.group(:created_at).sum(:total_active_students), library: { animation: { easing: 'easeOutQuad'}} %>
<h3>Lost students</h3>
<%= line_chart #club.daily_metrics.group(:created_at).sum(:lost_students), library: { animation: { easing: 'easeOutQuad'}} %>
<h3>New students</h3>
<%= line_chart #club.daily_metrics.group(:created_at).sum(:new_students), library: { animation: { easing: 'easeOutQuad'}} %>
Controller
def metrics
#club = current_club
#search = MetricSearch.new(params[:search])
#metrics = #search.scopeContr
end
Model for searching for metrics
class MetricSearch
attr_reader :date_from, :date_to
def initialize(params)
params ||= {}
#date_from = parsed_date(params[:date_from], 7.days.ago.to_date.to_s)
#date_to = parsed_date(params[:date_to], Date.today.to_s)
end
def scope
DailyMetric.where('created_at BETWEEN ? AND ?', #date_from, #date_to)
end
private
def parsed_date(date_string, default)
Date.parse(date_string)
rescue ArgumentError, TypeError
default
end
end
Is what I'm trying to do possible?
I posted a link to an great article in your comments, but you can use a gem called gem 'groupdate' as well.
# Gemfile
gem 'groupdate'
Assume your user model has_many :orders and you want to find out their spending over the past 6 month:
# order.rb
def self.monthly_spending
group_by_month(:date, last: 6, current: false).sum('orders.total')
end
In your view:
<%= line_chart current_user.orders.monthly_spending %>
Here is another tutorial how to group by date with chartkick and groupdate gem.
If you want to preselect a from-to-date for query you can setup a datepicker in your frontend and send the params to your controller
def show
orders = Order.where('created_at > %s AND created_at < %s', params[:start_date], params[:end_date])
end
call like above the group_by methods on your prefiltered list of orders.
Related
I have an Rails api that consumes an external design search api using the HTTParty gem. With the data that is returned to my view I'd like to be able to save selected entries to my database. Kind of like a bookmark function. Is there anyway of having a button next to each search result item that would achieve this? Any help would really be appreciated. My current code is below.
designs controller:
class DesignsController < ApplicationController
def index
#search = Api.new.find(params[:q])['results']
end
end
Class method:
class Api
include HTTParty
base_uri "http://search.example.com/searchv2"
attr_accessor :name, :limit, :offset
# Find a particular design, based on its name
def find(name)
self.class.get("/designs", query: { q: name }).parsed_response
end
end
View:
<h1>Design Search</h1>
<%= form_tag(designs_path, method: :get) do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
<h2>Search results:</h2>
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<% end %>
Design model:
class Design < ApplicationRecord
end
app/views/designs/index.html.erb:
...
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<%= button_to 'Save',
designs_path(
design: design.slice('name', 'thumbnail_url') # etc...
),
method: :post,
data: { disable_with: 'Loading...' }
%>
<% end %>
app/config/routes.rb:
resources :designs, only: [:index, :create]
app/controllers/designs_controller.rb:
class DesignsController < ApplicationController
# ...
def create
#design = Design.new(design_params)
#design.save!
# just an example...
redirect_to designs_path, success: 'Design has been saved!'
end
private
def design_params
params.require(:design).permit(:name, :thumbnail_url) # etc...
end
end
TODOs:
your Design model needs to have attributes that you want to be saved (i.e: :name? and :thumbnail_url?). My assumption was that you already have created/migrated these attributes; if not yet, then please do add them.
because there's still a vulnerability in my code above in that any user can modify the <button>'s <form action='...'> (i.e. they can modify the "name" of the design as they wish when it gets Saved), then my solution above is still not the best solution, though for simplicity and that assuming that this vulnerability is not a problem for you, then the above code already is sufficient.
I have a form that takes bookings for an event for people. The form displays events vertically, and a name & checkbox for each of the possible people next to each event.
How should I best convey the two pieces of information that i need per checkbox? that is, the event_id and the person_id
I'm not totally sure wether I got you right. This is the model I assume you're talking about:
# event.rb
class Event
has_many :people
scope :possible_people, -> { #whatever .. }
end
# person.rb
class Person
belongs_to :event
end
# events_controller.rb
class EventsController
def index
#events = Event.all
end
end
And this might be a possible solution to change an events relation to people:
# index.html.erb
<ul id="events">
<% #events.each do |event| %>
<li class="event">
<%= form_for #event do |form| %>
<% event.possible_people.each do |person| %>
<%= check_box_tag "event[person_ids][]", person.id, #event.people.include?(person) %>
<% end %>
<%= f.submit_tag 'Save Event' %>
<% end %>
</li>
<% end %>
</ul>
The important part is <%= check_box_tag "event[person_ids][]", person.id, #event.people.include?(person) %> where you actually change the the relation of a specific person to the event.
Good luck ;)
Well, you can try out something like below line, I am assuming you have a multiselect checkboxes and i am passing a Hash of event_id => plate_id as value to checkbox.
<%= check_box_tag 'booking[event_people_ids][]', {booking.event_id => booking.plate_id} %>
You will get the value in params as:
booking => {event_people_ids =>["{"72"=>"3"}}
I ended up doing this:
<%= check_box_tag "booking[]", "#{event.id}-#{person.id}" %>
and then in then to process them:
params[:booking].each do |booking|
booking = booking.split('-')
a = {
:booking_id => #booking.id,
:person_id => booking[1],
:event_id => booking[0]
}
Appointment.create(a)
end
I was hoping for a more railish way to do it but this works.
I'm trying to make a date range form in my rails app, so the user can input two dates, and these dates will post to a query to return results from an API the application is connected to.
My model looks like this: It allows the first date and second date to default to create last week's date range, but it is supposed to accept options to overwrite the default...
class Day
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :first_day, :second_day
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def self.monday(options = {})
if options[:first_day]
return Date.parse(options[:first_day])
else
d = Date.today
seven_days_ago = (d - 7)
seven_days_ago.beginning_of_week
end
end
#defines the end of last week for the query
def self.sunday(options = {})
if options[:second_day]
return Date.parse(options[:second_day])
else
d = Date.today
seven_days_ago = (d - 7)
seven_days_ago.end_of_week
end
end
def persisted?
false
end
end
Form:
<%= form_for #day do |f| %>
<div class="field">
<%= f.label 'Beginning Date' %>
<%= f.datepicker :first_day, :size => 6 %>
<%= f.label 'End Date' %>
<%= f.datepicker :second_day, :size => 6 %>
</div>
<div class="actions">
<%= f.submit 'Submit' %>
</div>
<% end %>
And controller:
def new
#day = Day.new
end
# POST /days
# POST /days.json
def create
#day = Day.new(params[:day])
if #day.valid?
redirect_to root_url
end
end
I am currently accessing the Day.monday and Day.sunday methods in queries that live in some of my controllers. These values always end up being the default, even if the user posts other dates through the form (the idea is to overwrite the default values).
I would prefer a javascript option - the user inputs two dates, they stick to the methods without refreshing, then when the dates are reset it goes back to default.
Thanks for any help!
I am trying to create a compare functionality for an index of schools. Currently I am using the following code which takes any checked school and adds it to the school_ids[] in the params.
In my gemfile:
gem 'will_paginate'
In my school's index.html.erb:
<%= form_tag compare_path, :method => 'get' do %>
<%= submit_tag "Compare" %>
<ul>
<% #schools.each do |school| %>
<li>
<%= check_box_tag'school_ids[]', school.id %>
<%= link_to school.name, school %><br>
<%= school.city %>, <%= school.state %>
</li>
<% end %>
</ul>
<% end %>
In my school controller I have:
def compare
#schools = School.find(params[:school_ids])
end
This works great as long as all of the check schools are on the same page. But since I'm using will_paginate to paginate the list of schools, if I change pages, the check boxes do not persist. I'm assuming I need to save to sessions somehow.
Do you mean you want to be able to add a check mark to a school A on page 1 of the index, go to page 2 of the index and add another check mark for school B, then submit the compare form and see schools A and B? If that's the case, then you're correct, you need to get the check boxes into the session. Attach a js click event, like
$('.checkbox_class').click(function(){
$.post('update_session_method', { school_id: $(this).val(), checked: $(this).is(:checked)]);
});
then add a controller method
def update_session_method
session[:school_ids] ||= []
if params[:checked]
session[:school_ids] << params[:school_id]
else
session[:school_ids].delete(params[:school_id])
end
end
then your compare method
def compare
#schools = School.find(params[:school_ids].merge(session[:school_ids] || []))
end
I got lost when I use 'ransack', a Rails Serching gem.
What I want to do is to work check-boxes for an asociated model.
Here is my code.
shows_controller.rb
class ShowsController < ApplicationController
def index
#q = Show.search(params[:q])
#shows = #q.result(:distinct => true)
#shows = #shows.joins(:tickets)
respond_to do |format|
format.html # index.html.erb
format.json { render json: #shows }
end
end
index.html.erb
<%= search_form_for #q do |f| %>
<%= f.label "Show's Title: " %>
<%= f.search_field :title_cont %>
<%= f.label "Buy at:" %>
<%= check_box_tag 'q[shows_tickets_store_cont[]]', 'venue' %>At Venue
<%= check_box_tag 'q[shows_tickets_store_cont[]]', 'ticketmaster' %>Ticketmaster
<%= submit_tag "Find Now" %>
<% end %>
<% #shows.each do |show| %>
<%= show.title %> |
<% show.tickets.each do |ticket| %>
<%= ticket.store %><br />
<% end %>
<% end %>
show.rb
class Show < ActiveRecord::Base
has_many :tickets
end
ticket.rb
class Ticket < ActiveRecord::Base
belongs_to :show
end
When I wrote something in the search_field, checked the "check_box" and clicked "Find Now" button, the log showed like below;
Parameters: {"utf8"=>"✓", "q"=>{"title_cont"=>"something", "shows_tickets_store_cont"=>"venue"}, "commit"=>"Find Now"}
Show Load (1.1ms) SELECT DISTINCT `shows`.* FROM `shows` INNER JOIN `tickets` ON `tickets`.`show_id` = `shows`.`id` WHERE (`shows`.`title` LIKE '%something%') LIMIT 25 OFFSET 0
I have no idea why the SQL doesn't have WHERE clause for Ticket.store, in spite of ticket controller received "shows_tickets_title_cont"=>"venue".
Please, suggest solution for this.
Thanks in Advance.
Actually the problems is because your second key is: shows_tickets_store_con but is should be shows_tickets_store_cont. It accepts attribute if it has predicate _cont.
Ransack documentation:
https://github.com/ernie/ransack
cont (contains) and start (starts with) are just two of the available search predicates. See Constants for a full list.
# Edited 1
I made investigation a bit.
I do not think that your approach is good for your situation. If all the checkboxes are selected then you will have problems with your meta search - you have to set another predicate. In your situation it could be in predicate - because you use multiple values (checkboxes).
To have SQL like:
"shows_tickets_store" IN ('venue','something')
Possible predicates:
https://github.com/ernie/ransack/wiki/Basic-Searching
https://github.com/ernie/ransack/blob/master/lib/ransack/constants.rb
Also read this:
https://github.com/ernie/ransack/issues/20
https://github.com/ernie/ransack/issues/53