Key Performance Indicators

This gem helps you to track key indicators in your Rails app.

Have you even wanted to get daily reports with information:

  • how many users registered to my app?

  • how many users registered today?

  • what is a total revenue today?

  • how many times certain event occurred in app?

and so on?

This gem lets you define your application state reports with ease. Look:

Installation

Rails 3.x

Add to your Gemfile:

gem "kpi"

and run in console in project directory:

$ bundle

Rails 2.x

This gem is tested with Rails 2.3.8.

Defining report

Example report in app/models/daily_kpi_report.rb:

class DailyKpiReport < KPI::Report
  def users_count
    result "Users count", User.count, :description => "Total users count"
  end

  def today_registrations_count
    result "Today registrations count", User.where("created_at > ?", @time - 24.hours)
  end

  def total_income
    result "Total income", Order.sum(:total), :unit => 'EUR'
  end

  def today_income
    result "Total income", Order.where(:created_at => today).sum(:total), :unit => 'EUR'
  end

  private

  def today
    ((time - 24.hours)..time)
  end
end

Each defined method should return a KPI::Entry object with following accessors:

  • name

  • value

  • description

  • unit

A collection of KPI::Entry is available through Enuberable: Report#entries

It is important, if your indicator depends on current time (last 24 hours, last week), to rely on @time instance variable instead of Time.now. This lets you to generate report for every moment in history:

report = DailyKpiReport.new(:time => 2.days.ago)

Merged reports

You can create merged report from two or more reports of the same type. Look at examples:

> today = DailyKpiReport.new
> today.users_count.value
# => 20
> today.today_income.value
# => 350
> yesterday = DailyKpiReport.new(:time => 1.day.ago)
> yesterday.users_count.value
# => 16
> yesterday.today_income.value
# => 250
> diff = KPI::MergedReport.new(today, yesterday) do |today, yesterday|
>   KPI::Entry.new "$$ (change)", today.value - yesterday.value
> end
> avg = KPI::MergedReport.new(today, yesterday) do |*entries|
>   KPI::Entry.new "$$ (avg)", entries.map(&:value).inject(:+) / entries.size
> end
> diff.users_count.value
# => 4
> avg.today_income.value
# => 300
> avg.today_income.unit
# => "EUR"

More reports can be passed to constructor of Merged report:

avg = KPI::MergedReport.new(week_now, week_last, week_next_to_last) do |*entries|
  # ...
end

Other

  • You can override report title by overriding title method in report:

    class DailyKpiReport < KPI::Report
      def title; "Your daily report"; end
      # ...
    end
    
  • You can use Whenever gem to generate periodic reports

  • Private methods are not considered as indicator methods and can be used as helper methods:

    class WeeklyReport < KPI::Report
      # ...
      private
    
      def week_range
        t_prev_mon_begin = time - (time.wday-1)*24*60*60 - time.hour*60*60 - time.min*60 - time.sec
        t_next_sun_end = t_prev_mon_begin + 7*24*60*60 - 1
        (t_prev_mon_begin..t_next_sun_end)
      end
    end
    

Todo

  • Generators for configuration and reports

  • Database persistence - storing reports in different databases: PostgreSQL & Mongo prioritized - should be done in external gems

  • Support for “result” helper in block passed to constructor of MergedReport

  • Remove ActiveSupport requirement

Contributing to KPI

  • Check out the latest master to make sure the feature hasn’t been implemented or the bug hasn’t been fixed yet

  • Check out the issue tracker to make sure someone already hasn’t requested it and/or contributed it

  • Fork the project

  • Start a feature/bugfix branch

  • Commit and push until you are happy with your contribution

  • Make sure to add tests for it. This is important so I don’t break it in a future version unintentionally.

  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright © 2011 Artur Roszczyk. See LICENSE.txt for further details.