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 !
I think it is not possible for cumulative data if you only have first occurred timestamp and current status.
If your errors will never be deleted and the only action is resolve (Change from active -> resolved status), you could add a new field, indicate when the error was resolved.
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
If the error can be reopened/resolved multiple times, can be deleted, or having multiple statuses, this approach will be invalid though. You may consider storing the history amount for each day in a new model via a daily cronjob or something like that, and just query those amounts. For example
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
Related
I want to get the weekly/monthly/yearly report and in that report, I need to get the total of the field due_amount on daily basis for this week/month/year
I need something like
Sunday => 1000 (due_amount 500+500)
Monday => 1200
Tuesday => 500
upto Saturday(end of week)
If its monthly then from 1st to 31st and if it is yearly then Jan Feb to December.
What I do is
Invoice.group(&:week).where('due_date BETWEEN ? AND ?', date_range[:current_start_date].to_date, date_range[:current_end_date].to_date).sum(:due_amount)
but its not working
I can also do date_trunc('week', ) but I do not know how to do it.
Please help me.
The easy solution - add the gem groupdate and you will be able to do something like
Invoice.group_by_day(:due_date, range: 2.weeks.ago.midnight..Time.now).map(&:due_amount).sum
I have an active relation Bar object with an attribute shift_date. shift_date represents each day between March and June. March and June comes from Foo which has attributes start_month and end_month:
f = Foo.find(1)
days = (f.end_month - f.start_month).to_i
weeks = (days * 0.142857).round(2)
f.bars will give me days objects. Where days is the total amount of objetcs.
My trouble is to get Bars objects, objects for week 1, 2 or 3 etc:
f.bars.where('shift_date >= ?', (weeks/7.days)).group_by{ |result| result }
operator does not exist: timestamp without time zone >= numeric
So what am I saying? Give me all objects on week 1 or week 5, if any. How do I go about this, please?
Im on to something but not right:
f.bars.where('shift_date >= ?', Date.today).group_by{ |result| result}
Edit:
Im almost there. I could splat out the days with:
days_array = *(f.start_month..f.end_month)
then
f.bars.where(shift_date: days_array[0]..days_array[7])
That would be the answer! But...not really. For my views, I need to group the splatted days in a 7 days interval as week, so days_array[0] to days_array[7] would be week 1 and days_array[8] to days_array[14] would be week 2 etc. How to show that in the view? This will give me everything I need.
I am using ruby 2.0.0 an rails 4.0.0.
I have a web service call that is going to run on the 1st of every month.
It's job is to schedule emails to go out on the second Tuesday of the month.
The "Scheduler" web service call has a parameter like this:
?schedule_for_1d&1h&1m
That would schedule the emails to go out 1 day, 1 hour, and 1 minute after the api call is executed.
Right before I make that API call I need to calculate the time, in days, hours, and minutes, between "Time.now" and the second Tuesday of the month, whenever that is.
How can I write that code out and get it in the format of the above "1d&1h&1m"?
def scheduleSecondTuesday
now = Time.now
secondTuesday = 8 + (2-Time.local(now.year,now.month,1).wday) % 7
s = Time.local(now.year,now.month,secondTuesday) - now
dhm = [24*3600,3600,60].inject([]) { |a,b| x,s = s.divmod(b); a<<x }
'?schedule_for_%dd&%dh&%dm' % dhm
end
scheduleSecondTuesday
# => "?schedule_for_5d&9h&48m"
One safety feature included here is that if the task that runs on the 1st of every month fails for some reason and runs some time prior to the second Tuesday or the month, it will still output the correct string.
I am writing a ruby program that will ask a user a to type a message, then a year, month, day and time and then email a message when that time comes. I store that data into a database table. The program should loop until that day comes and then carry out the sending.
Without using a database, I have written the following:
def send_Message(m_to, m_from, m_body, month, day, year, hour, min)
x = 0
t = Time.now
if (t.day == day
&& t.month == month
&& t.year == year
&& t.strftime("%I") == hour
&& t.strftime("%M") == min )
x = x +5
message = #client.account.sms.messages.create(:body => m_body,
:to => m_to,
:from => m_from)
puts message.sid
else
sleep_time = Time.new(year,month,day)
total_sleep = sleep_time - t
sleep(total_sleep)
message = #client.account.sms.messages.create(:body => m_body,
:to => m_to,
:from => m_from)
puts message.sid
end
end
but it only really seems to work with the month, day, year thing, but not time. There are two questions I am seeking an answer to: how can I carry out looping through a database, seeking chronologically messages to send, and with my example code above, how can I use the hour and minute of the day along with the day month and year to send the message.
P.S: I am designing this app on over to rails, where a user/app sends an API request and then the process I mentioned will be carried out.
You have a few approaches:
Look into the gem whenever
gem install whenever
https://github.com/javan/whenever
It doesn't do exactly what you asked but it is much better than running ruby for 6 month's waiting for an email to be sent.
An even better approach is resque https://github.com/defunkt/resque
Then add https://github.com/bvandenbos/resque-scheduler and your solution is very nice without the drag of a million ruby instances waiting to send an email.
Good Luck...
BTW: I made a service to do exactly what you are asking. I have specs and the code. If you want I can get it to you.
Given a date, how do I find the nearest Monday in Rails?
I know I can do things like:
Date.tomorrow
Date.today
Is there something like Date.nearest :monday ?
The commercial method on the Date object will let you do this. This example will get you the next Monday.
Date.commercial(Date.today.year, 1+Date.today.cweek, 1)
If you need the next or previous Monday, whichever is closest, you can do:
Date.commercial(Date.today.year, Date.today.cwday.modulo(4)+Date.today.cweek, 1)
I can't execute this right now, so forgive me if there are syntax errors.
It's a little bit tricky, but not so hard to calculate.
Use ActiveSupport::DateAndTimeCalculations#end_of_week to calculate end of a week, this method accepts a start_day parameter that is used to indicate start day of the week (it's :monday by default). They even have implemented sunday method.
The trick is the following: if you want to calculate closest Monday, you may calculate it as a end of the week which starts on Tuesday (Tue => 1st day, Wed => 2nd day, ..., Mon => 7th day which is also end of the week).
So all you need to do is:
# it will return current date if today is Monday and nearest Monday otherwise
Date.today.end_of_week(:tuesday)
I know this is an old thread but it's always nice to keep it current for future seekers.
Let's assume today is say Friday the 19th of August. All I do to get my nearest Monday is this:
monday = Date.today.monday
Then from there you can go back a week or forward a week like this:
last_monday = monday.last_week
next_monday = monday.next_week
Assuming you want both directions: Date.today.beginning_of_week + 7*(Date.today.wday/5)
Untested, so you might need to finetune, but here you go:
def Date.nearest_monday
today = Date.today
wday = today.wday
if wday > 4 # over the half of the week
today + (7 - wday) # next monday
else
today - (1 + wday) # previous monday
end
end