Two Dimensions

When using a data_method that returns data with two dimensions (i.e. multiple series of data), the data_method can return the data in one of two formats:

Hash

{
  ['Jul 3, 2016', 'Hilton'] => 2,
  ['Jul 10, 2016', 'Hilton'] => 2,
  ['Jul 3, 2016', 'Carlee'] => 3,
  ['Jul 10, 2016', 'Carlee'] => 5
]

Array

[
  [['Jul 3, 2016', 'Hilton'], 2],
  [['Jul 10, 2016', 'Hilton'], 2],
  [['Jul 3, 2016', 'Carlee'], 3],
  [['Jul 10, 2016', 'Carlee'], 5]
]

To create the chart above, simply add the following YAML file and call render_report in any view:

YAML

config/reports_kit/reports/data_methods_two_dimensions.yml

data_method: MyDataMethods.posts_by_created_at_week_and_author
chart:
  options:
    scales:
      xAxes:
      - scaleLabel:
          display: true
          labelString: Creation date
        stacked: true
      yAxes:
      - scaleLabel:
          display: true
          labelString: Posts
        stacked: true

Note: The "chart.options" option is passed to Chart.js, so any values supported by Chart.js are supported here, too.


View

app/views/my_controller/my_view.html.haml

= render_report 'data_methods_two_dimensions'

Data Methods

app/services/my_data_methods.rb

class MyDataMethods
  # { 'Bernice' => 191, 'Carlee' => 160, 'Edison' => 60, ... }
  def self.posts_by_author(properties)
    Post.joins(:author).group('authors.name').order('authors.name').count
  end

  # [
  #   [["Jul 3, 2016", "Hilton"], 2],
  #   [["Jul 10, 2016", "Hilton"], 2],
  #   ...
  #   [["Jul 3, 2016", "Carlee"], 3],
  #   [["Jul 10, 2016", "Carlee"], 5],
  #   ...
  # ]
  def self.posts_by_created_at_week_and_author(properties)
    # The `group_by_week` method is provided by the `groupdate` gem.
    Post.joins(:author).group_by_week('posts.created_at').group('authors.name').count.map do |(date, author_name), count|
      [[date.strftime('%b %-d, %Y'), author_name], count]
    end
  end

  # { 'Bernice' => 191, 'Carlee' => 160, 'Edison' => 60, ... }
  def self.posts_by_author_with_filters(properties)
    ui_filters = properties[:ui_filters]

    posts = Post.joins(:author).group('authors.name').order('authors.name')
    posts = posts.where(created_at: ReportsKit.parse_date_range(ui_filters[:created_at])) if ui_filters[:created_at].present?
    posts = posts.where(is_featured: true) if ui_filters[:is_featured]
    posts = posts.where(author_id: ui_filters[:author]) if ui_filters[:author].present?
    posts = posts.where('title ILIKE ?', "%#{ui_filters[:title]}%") if ui_filters[:title].present?
    posts.count
  end
end

Model

app/models/post.rb

class Post < ApplicationRecord
  belongs_to :author
  has_many :post_views, dependent: :destroy
  has_many :posts_tags, dependent: :destroy
  has_many :tags, through: :posts_tags

  include ReportsKit::Model
  reports_kit do
    aggregation :average_time_to_publish, [:average, 'posts.published_at - posts.created_at']
    contextual_filter :for_author, ->(relation, context_params) { relation.where(author_id: context_params[:author_id]) }
    dimension :approximate_views_count, group: 'ROUND(posts.views_count, -1)'
    filter :is_published, :boolean, conditions: ->(relation) { relation.where(status: 'published') }
  end

  STATUSES = %w(draft private published).freeze

  def to_s
    title
  end
end

Model's Columns

id              integer
author_id       integer
title           string
status          string
published_at    datetime
is_featured     boolean
views_count     integer
created_at      datetime
updated_at      datetime