ruby-on-railsrubymodelchart.jschartkick

Chartkick cumulative chart by date never descending


I have a problem with using a cumulative chart with Chartkick. At the moment, here is how the chart is built in Ruby:

errors = Error.where(status: 'active')
sum = errors.where('first_occurrence_date <= ?', Time.zone.today - 30.days).count    
line_chart errors.group_by_day(:first_occurrence_date, last: 30).count.map { |x,y| { x => (sum += y)} }.reduce({}, :merge)

I have an external service giving me the errors happening on my Rails app, and I collect them using API calls and store them in a database.

The problem is, these errors can have the status Resolved or Active. On the external platform, I have the possibility to "resolve" the errors when I think I have dealt with the bug. Therefore the status goes from active -> resolved

All these errors have a first occurrence timestamp, on which I'm building my chart. Let's imagine the following scenario:

Monday => 0 errors
Tuesday => 10 errors occurring for the first time 
Wednesday => 2 errors (which occurred on tuesday) resolved (active -> resolved) => total of 8 active errors
Thursday => 4 errors occurring for the first time
Friday => 1 error which occurred on Thursday solved, and 1 on Tuesday solved

My graph will have the following values on Wednesday

Monday => 0
Tuesday => 7
Wednesday => 7
Thursday => 10
Friday => 10

(because I only take the errors which have the active status in my chart)

What I would like would be:

Monday => 0
Tuesday => 10
Wednesday => 8
Thursday => 12
Friday =>10

I have thought a moment about how to do, and can't manage to find a solution, anyone has any idea on how to solve this issue ?

Thanks a lot !


Solution

  • I think it is not possible for cumulative data if you only have first occurred timestamp and current status.

    The error will be considered active on a day if they are created before or on that day, and not resolved or resolved after that day. So you query would be:

    # Don't filter by status here
    errors = Error.where('first_occurrence_date <= ?', Time.zone.today - 30.days)
    
    day_data = (29.days.ago.to_date..Time.zone.today).to_h { |day| [day, 0] }
    
    errors.find_each do |error|
      resolved_date = error.resolved_at&.to_date || Time.zone.tomorrow
      (error.first_occurrence_date..resolved_date).each do |day|
        day_data[day] += 1
      end
    end
    

    Cronjob: run at the end of the day

    # first_occurrence_date doesn't matter, we only count the errors still active by the end of the day
    DayReport.create(day: Time.zone.today, active_errors_count: Error.where(status: "active").count)
    

    Then simply query for the chart

    line_chart DayReport.pluck(:day, :active_errors_count).to_h