Ask A Question

Notifications

You’re not receiving notifications from this thread.

Chartkick and impressionist gem render not working

Ben John Bagley asked in Rails

EDIT 1

Here is a screenshot of the current graph

Original Post

I'm using both chartkick and the impressionist gem and I seem to be running into it's not working in regards the current implementation I am trying to show the impressions per day in the graph.

I have a dashboard, with the raffles built in and I'm trying to get the impressions from the raffles to show in a graph on the Dashboard index using an _overview.html.erb partial.

Here is how I have the code so far.

_overview.html.erb

<%= area_chart charts_impressions_path, colors: ["#7123D1"] %>

raffles_controller.rb

class Dashboard::RafflesController < ApplicationController
  before_action :set_dashboard_raffle, only: [:show, :edit, :update, :destroy]

  def show
    @dashboard_raffle = Dashboard::Raffle.find(params[:id])
    impressionist(@dashboard_raffle)
  end

  def new
    @dashboard_raffle = Dashboard::Raffle.new
  end

  def edit
  end

  def create
    @dashboard_raffle = Dashboard::Raffle.new(dashboard_raffle_params)

    if @dashboard_raffle.save
      redirect_to @dashboard_raffle, notice: 'Raffle was successfully created.'
    else
      render :new
    end
  end

  def update
    if @dashboard_raffle.update(dashboard_raffle_params)
      redirect_to @dashboard_raffle, notice: 'Raffle was successfully updated.'
    else
      render :edit
    end
  end

  def destroy
    @dashboard_raffle.destroy
    redirect_to dashboard_raffles_url, notice: 'Raffle was successfully destroyed.'
  end

  private

    def set_dashboard_raffle
      @dashboard_raffle = Dashboard::Raffle.find(params[:id])
    end

    def dashboard_raffle_params
      params.require(:dashboard_raffle).permit(:title, :organisation, :prizes, :raise_limit, :deadline, :max_entires, :ticket_price, :raffle_limit)
    end
end

raffle.rb

class Dashboard::Raffle < ApplicationRecord
  is_impressionable
  belongs_to :user
end

charts_controller.rb

class ChartsController < ApplicationController
  def impressions
    render json: impressions = Dashboard::Raffle.all.map{ |r| r.impressions }.flatten
  end
end

dashboard.rb

module Dashboard
  def self.table_name_prefix
    'dashboard_'
  end
end

dashboard_controller.rb

class DashboardController < ApplicationController
  before_action :authenticate_user!

  def index
    @dashboard_raffles = Dashboard::Raffle.all
  end
end

Here is the

Any help here is appreciated.

Reply
Reply

Hey Jacob,

So I have added a cached impressions_count counter and column, here is the full schema file

ActiveRecord::Schema.define(version: 20171207012830) do

  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "dashboard_raffles", force: :cascade do |t|
    t.string "title"
    t.string "organisation"
    t.integer "prizes"
    t.float "raise_limit"
    t.datetime "deadline"
    t.integer "max_entires"
    t.float "ticket_price"
    t.integer "raffle_limit"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "impressions_count"
  end

  create_table "impressions", force: :cascade do |t|
    t.string "impressionable_type"
    t.integer "impressionable_id"
    t.integer "user_id"
    t.string "controller_name"
    t.string "action_name"
    t.string "view_name"
    t.string "request_hash"
    t.string "ip_address"
    t.string "session_hash"
    t.text "message"
    t.text "referrer"
    t.text "params"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["controller_name", "action_name", "ip_address"], name: "controlleraction_ip_index"
    t.index ["controller_name", "action_name", "request_hash"], name: "controlleraction_request_index"
    t.index ["controller_name", "action_name", "session_hash"], name: "controlleraction_session_index"
    t.index ["impressionable_type", "impressionable_id", "ip_address"], name: "poly_ip_index"
    t.index ["impressionable_type", "impressionable_id", "params"], name: "poly_params_request_index"
    t.index ["impressionable_type", "impressionable_id", "request_hash"], name: "poly_request_index"
    t.index ["impressionable_type", "impressionable_id", "session_hash"], name: "poly_session_index"
    t.index ["impressionable_type", "message", "impressionable_id"], name: "impressionable_type_message_index"
    t.index ["user_id"], name: "index_impressions_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer "sign_in_count", default: 0, null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.inet "current_sign_in_ip"
    t.inet "last_sign_in_ip"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

end

Here is the migration

class AddImpressionsCountToDashboardRaffle < ActiveRecord::Migration[5.1]
  def change
    add_column :dashboard_raffles, :impressions_count, :int, default: 0
  end
end

The counter goes up on refresh so the counter works.

Here is the raffle.rb model

class Dashboard::Raffle < ApplicationRecord
  is_impressionable :counter_cache => true, :column_name => :impressions_count
end

I still have the same code in the charts_controller.rb so I'm not sure if I need to change anything here now. The impressons are still not showing in the graph.

Reply

Ok great, so you can check impressionist off your list now, sounds like it's working as expected.

So for the chart, can you show your view that has your chartkick code in it?

Reply

Here is the _overview.html.erb file

<section class="content active" id="overview">
  <section class="grid text-center">
    <section class="grid__col grid__col--1-of-4 box">
      <h3>Total Payments</h3>
      <span>600</span>
    </section>
    <section class="grid__col grid__col--1-of-4 box">
      <h3>Total Raised (All raffles)</h3>
      <span>£2,500.00</span>
    </section>
    <section class="grid__col grid__col--1-of-4 box">
      <h3>Total Entries</h3>
      <span>142</span>
    </section>
  </section> <!-- end three grid -->

  <section class="grid text-center">
    <section class="grid__col grid__col--1-of-2">
      <h3>Overall Impressions</h3>
      <%= area_chart charts_impressions_path, colors: ["#7123D1"] %>
    </section>
    <section class="grid__col grid__col--1-of-2">
      <h3>Total Impressions</h3>
      <%= area_chart charts_impressions_path %>
    </section>
  </section>
</section>

I have two graphs here doing the same thing, I will change the second graph but I do want to get at least the impressions data flowing.

Also here is a screenshot of the file structure

Reply

Scratch that - you're using the chartkick gem. I haven't used that before, so I'll have to read up on it some, but check to make sure the chart is getting data. Does anything show up in your inspect console in your browser?

Reply

Here is everything

charts_controller.rb

class ChartsController < ApplicationController
  def impressions
    # render json: Dashboard::Raffle.all.impressions_count.group_by_day(:created_at).count
    render json: impressions = Dashboard::Raffle.all.map{ |r| r.impressions }.flatten
  end
end

raffle.rb

class Dashboard::Raffle < ApplicationRecord
  is_impressionable :counter_cache => true, :column_name => :impressions_count, :unique => :all
end

raffles_controller.rb

class Dashboard::RafflesController < ApplicationController
  before_action :set_dashboard_raffle, only: [:show, :edit, :update, :destroy]

  def show
    @dashboard_raffle = Dashboard::Raffle.find(params[:id])
    impressionist(@dashboard_raffle)
  end

  def new
    @dashboard_raffle = Dashboard::Raffle.new
  end

  def edit
  end

  def create
    @dashboard_raffle = Dashboard::Raffle.new(dashboard_raffle_params)

    if @dashboard_raffle.save
      redirect_to @dashboard_raffle, notice: 'Raffle was successfully created.'
    else
      render :new
    end
  end

  def update
    if @dashboard_raffle.update(dashboard_raffle_params)
      redirect_to @dashboard_raffle, notice: 'Raffle was successfully updated.'
    else
      render :edit
    end
  end

  def destroy
    @dashboard_raffle.destroy
    redirect_to dashboard_raffles_url, notice: 'Raffle was successfully destroyed.'
  end

  private

    def set_dashboard_raffle
      @dashboard_raffle = Dashboard::Raffle.find(params[:id])
    end

    def dashboard_raffle_params
      params.require(:dashboard_raffle).permit(:title, :organisation, :prizes, :raise_limit, :deadline, :max_entires, :ticket_price, :raffle_limit)
    end
end

dashboard_controller.rb

class DashboardController < ApplicationController
  before_action :authenticate_user!

  def index
    @dashboard_raffles = Dashboard::Raffle.all
  end
end

routes.rb

Rails.application.routes.draw do
  namespace :dashboard do
    resources :raffles
  end
  devise_for :users
  root 'welcome#index'

  get 'about', to: 'pages#about', as: 'about'

  # Dashboard Routes
  get 'dashboard', to: 'dashboard#index', as: 'dashboard'

  namespace :charts do
    get 'impressions'
  end
end

Hopefully somewhere in that file outputs is what you need.

Reply

I think your impressions method needs to look like this

class ChartsController < ApplicationController
  def impressions
    render json: Dashboard::Raffle.all.map(&:impressionist_count)
  end
end

Check your console in your browser and see if there are any errors being thrown

Reply

Here is what I get now

screenshot

Reply

You're going to have to do some debugging, looks like it's an error with the way the data is being presented to chartkick. You'll have to dig into how chartkick is expecting the data to be presented, then verify that your json is structured that way.

Check out this SO: https://stackoverflow.com/questions/24601985/morris-js-uncaught-typeerror-cannot-read-property-match-of-undefined

It's for MorrisJS, but net effect should be the same.

If you can throw together an example repo that produces the same results I can take a better look.

Reply

So I had a play around with the documentation.

I got this to work

def impressions
  render json: Dashboard::Raffle.all.group(:impressions_count).count
end

screenshot

If I do

render json: Dashboard::Raffle.all.group_by_day(:impressions_count).count

it doesn't work, so I'm guessing something group_by_day doesn't work even though I have the gem 'groupdate' installed, strange.

I need to have a play around and make the graph more readable, with the labels etc.

Reply

You'll want to check how the chartkick is expecting the date to be formatted.

Check: https://github.com/ankane/chartkick#data to make sure your date being passed by group_by_date matches, you may need to do some manipulation of the time before sending to chartkick.

Reply

Got it

screenshot screenshot #2

using the following line

<%= area_chart Dashboard::Raffle.group(:title).group_by_hour(:created_at).maximum(:impressions_count), min: nil, refresh: 10, colors: ["#62518C", "#F7AE07"] %>

Thank you for all your help.

Reply

Hey,

Quick question.

I'm trying to target group_by_minute or group_by_hour and it just seems to be going on the same line in the graph.

Here is the line of code

<%= area_chart Dashboard::Raffle.group(:title).group_by_minute(:created_at).maximum(:impressions_count), colors: ["#62518C", "#F7AE07"] %>

I get this screenshot see how the value is at 180?

I have increased the counter to 211 screenshot but everything is staying on the one line and it's showing the wrong time, so the time or anything like that isn't been taken into account like so screenshot.

Any tips?

Reply

As far as I can tell, to accomplish that your query is going to be quite a bit more complex.

What you end up needing is to provide a hash to chartkick that is similar to this:

[{name: "Test Raffle 2", data: [["8am-9am", 34], ["9am-10am", 12], ["10am-11am", 45], ["11am-12pm", 89]]}]

Which would give you:

In order to achieve this, you have to use the impressionists date search query. The lines below would give you the impression count for the first Raffle that occured within the last hour.

start_time = Time.now - 1.hour
end_time = Time.now
Dashboard::Raffle.first.impressionist_count( start_date: start_time, end_date: end_time)

You're going to have to make a function that queries each range of time within the date range you want, in the desired intervals (minutes, hours, days, etc) to build the array above.

Reply

So I have tried this

<%= area_chart Dashboard::Raffle.first.impressionist_count(start_date: Time.now - 1.hour, end_date: Time.now), refresh: 60, colors: ["#62518C", "#F7AE07"] %>

and I get the following screenshot no data is passing, I would like to eventually pass all raffles impressions into the graph by hour.

Any ideas.

Reply

Right, because that's just a single query for a single point in time. Go into your rails console and type in your query and look at the output

Dashboard::Raffle.first.impressionist_count(start_date: Time.now - 1.hour, end_date: Time.now)

You'll see it only gives you a single value. So you have to make a function that will iterate over a range of time to eventually build up a hash that looks like this:

[{name: "Test Raffle 2", data: [["8am-9am", 34], ["9am-10am", 12], ["10am-11am", 45], ["11am-12pm", 89]]}]
Reply

This is what I get

irb(main):001:0> Dashboard::Raffle.first.impressionist_count(start_date: Time.now - 1.hour, end_date: Time.now)
  Dashboard::Raffle Load (0.4ms)  SELECT  "dashboard_raffles".* FROM "dashboard_raffles" ORDER BY "dashboard_raffles"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (1.5ms)  SELECT COUNT(DISTINCT "impressions"."request_hash") FROM "impressions" WHERE "impressions"."impressionable_id" = $1 AND "impressions"."impressionable_type" = $2 AND (created_at >= '2017-12-08 13:07:55.321801' and created_at <= '2017-12-08 14:07:55.321866')  [["impressionable_id", 1], ["impressionable_type", "Dashboard::Raffle"]]
=> 0
Reply

Yes, the last thing you see there is the return value - 0

So that's one value for one time range for one raffle. Now you need to create a function to build the hash like I showed so you end up with a range of time values along with their respective impression counts for each raffle.

So basically, what you're needing is something similar to:

edits made

raffles = Dashboard::Raffle.all

# time_ranges you need another function that you can give it a start_time and an end_time, 
# and then have it spit out an array like you see below that is an array of grouped time spans. 
# Example below shows two time groups that span one hour each.
time_ranges = [['2017-12-08 08:00:00 -0600', '2017-12-08 09:00:00 -0600'], ['2017-12-08 09:00:00 -0600', '2017-12-08 10:00:00 -0600']]

raffle_hash = {}

raffles.each do |raffle|
  impressions = []
    time_ranges.each do |time|
        impressions << [[ time[0], time[1] ], raffle.impressionist_count(start_date: time[0], end_date: time[1]) ]
    end
  raffle_hash[raffle.name] = impressions
end

return raffle_hash

This would give you an output like this:

[{name: "Test Raffle 2", data: [[['2017-12-08 08:00:00 -0600', '2017-12-08 09:00:00 -0600'], 34], [['2017-12-08 09:00:00 -0600', '2017-12-08 10:00:00 -0600'], 12]]}]

This is just an example of the basic steps you're going to have to take to get your output.

Reply

Oops, noticed a mistake - that's what I get for trying to write this out in a comment box :)

raffles = Dashboard::Raffle.all
time_ranges = [['2017-12-08 08:00:00 -0600', '2017-12-08 09:00:00 -0600'], ['2017-12-08 09:00:00 -0600', '2017-12-08 10:00:00 -0600']]

raffles_array = [] # don't need raffles_hash, instead just an array to load

raffles.each do |raffle|
  impressions = []
    time_ranges.each do |time|
        impressions << [[ time[0], time[1] ], raffle.impressionist_count(start_date: time[0], end_date: time[1]) ]
    end
  raffles_array << { name => raffle.name, data => impressions } 
end

return raffles_array

You may need to do some more playing but I think that's pretty close.

Reply
Join the discussion
Create an account Log in

Want to stay up-to-date with Ruby on Rails?

Join 87,400+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.