title: Are there any model analytics gems?
body: <p>I'm working on allowing clients to view analytics per day, week, month, in a period of time, grouped by hours or days or months, etc... All of that is based on the <code>created_at</code> attribute.</p>
  
  <p>Is there any gem out there that already does this? Something like:</p>
  
  <pre><code>Posts.analytics(:by =&gt; :day, :period =&gt; :this_week, :column =&gt; :created_at)
  </code></pre>
  
  <p>Would return:</p>
  
  <pre><code>{
  '2012-06-19' =&gt; 14,
  '2012-06-20' =&gt; 0, // Empty rows padding support*
  '2012-06-21' =&gt; 3
  }
  </code></pre>
  
  <p>I'm trying to make it from scratch but it seems like a lot of unecessary work if there's already a gem to do the job.</p>
  
  <h2><strong>Update</strong></h2>
  
  <p>I tried to make an analytics module that gets included into all models for easy analytics generation, But it's really unreliable, Sometimed i get more days than i need, and it's really messy, Could anyone collaborate and rewrite/improve on this:</p>
  
  <pre><code># Usage:
  # include Analytics::Timeline
  # Model.timeline(:period =&gt; :last_24_hours, :time_by =&gt; :hour)
  
  module Analytics
    module Timeline
      def self.included(base)
        base.class_eval {
  
          def self.timeline(*filters)
            filters = filters[0]
            period  = filters[:period] || :this_week
            time_by = filters[:time_by] || :days
            date_column = filters[:date_column] || :created_at
  
            # Named periods conventions
            period_range = case period
              when :last_12_hours
                [Time.now-12.hours, Time.now]
              when :last_24_hours
                [Time.now-24.hours, Time.now]
              when :last_7_days
                [Time.now-7.days, Time.now]
              when :last_30_days
                [Time.now-30.days, Time.now]
              when :this_week
                [Time.now.beginning_of_week, Time.now.end_of_week]
              when :past_week
                [(Time.now - 1.week).beginning_of_week, (Time.now - 1.week).end_of_week]
              when :this_month
                [Time.now.beginning_of_month, Time.now.end_of_month]
              when :past_month
                [(Time.now-1.month).beginning_of_month, (Time.now - 1.month).end_of_month]
              when :this_year
                [Time.now.beginning_of_year, Time.now.end_of_year]
              end
            period_range = period if period.kind_of?(Array)
            period_range = [period, Time.now] if period.is_a?(String)
  
            # determine the SQL group method
            group_column = case time_by
              when :months
                time_suffix = "-01 00:00:00"
                records = where("#{table_name}.#{date_column} &gt; ? AND #{table_name}.#{date_column} &lt;= ?", period_range[0].to_date, period_range[1].to_date)
                "DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m')"
              when :days
                time_suffix = " 00:00:00"
                records = where("#{table_name}.#{date_column} &gt; ? AND #{table_name}.#{date_column} &lt;= ?", period_range[0].to_date, period_range[1].to_date)
                "DATE(#{table_name}.#{date_column.to_s})"
              when :hours
                time_suffix = ":00:00"
                records = where("#{table_name}.#{date_column} &gt; ? AND #{table_name}.#{date_column} &lt;= ?", period_range[0], period_range[1])
                "DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m-%d %H')"
              when :minutes
                time_suffix = ":00"
                records = where("#{table_name}.#{date_column} &gt; ? AND #{table_name}.#{date_column} &lt;= ?", period_range[0], period_range[1])
                "DATE_FORMAT(#{table_name}.#{date_column.to_s}, '%Y-%m-%d %H:%M')"
              end
  
            # Get counts per cycle
            records = records.group(group_column).select("*, count(*) AS series_count, #{group_column} AS series_time")
            series = {}
  
            # Generate placeholder series
            time_table = { :days =&gt; 60*60*24, :hours =&gt; 60*60, :minutes =&gt; 60, :seconds =&gt; 0 }
  
            if time_by == :months
              ticks = 12 * (period_range[1].year - period_range[0].year) + (period_range[1].month + 1) - period_range[0].month
            else
              ticks = (period_range[1] - period_range[0] + 1) / time_table[time_by]
            end
  
            ticks.to_i.times do |i|
              time = period_range[1]-i.send(time_by)
              time = case time_by
              when :minutes
                time.change(:sec =&gt; 0)
              when :hours
                time.change(:min =&gt; 0)
              when :days
                time.change(:hour =&gt; 0)
              when :months
                time.change(:day =&gt; 1, :hour =&gt; 0)
              end
  
              series[time.to_s(:db)] = 0
            end
  
            # Merge real counts with placeholder series
            to_merge = {}
            records.each do |r|
              to_merge[r.series_time.to_s+time_suffix] = r.series_count
            end
  
            series.merge!(to_merge)
          end
  
        }
      end
    end
  end
  </code></pre>