Show objects based on selected week - ruby-on-rails

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.

Related

SPSS - flag cases within a calendar month

I have a list of prisoners and when their prison term started (PrisonStart) and when it ended (PrisonEnd). If they're still in prison, PrisonEnd is blank.
I would like to flag prisoners who were in prison at least one full calendar month during a 6-month period (1/1/16 to 5/30/16).
compute PeriodBeg = date.mdy(01,01,16).
compute PeriodEnd = date.mdy(05,30,16).
formats PeriodBeg PeriodEnd (adate10).
execute.
Any suggestions for how best to go about this? Seems I might need to compare prisoners' start and end dates separately for each month during the 6-month period (like below), and then select any prisoner with at least one full month, but I'm wondering if there's a more efficient way.
if ((PrisonStart le [January 1, 2016]) and (PrisonEnd ge ([January 31, 2016]) | missing(PrisonEnd))) InPrisonJan = 1.
if ((PrisonStart le [February 1, 2016]) and (PrisonEnd ge ([February 28, 2016]) | missing(PrisonEnd))) InPrisonFeb = 1.
etc.
execute.
Some sample data below. The first two prisoners should be flagged as having been in prison for at least one full calendar month during the 6-month period (OneMonth = 1). The last three prisoners were not in prison for at lease one full calendar month during the 6-month period (OneMonth = 0).
data list list /PrisonerID (F8.0) PrisonStart (adate10) PrisonEnd (adate10) PeriodBeg (adate10) PeriodEnd (adate10).
begin data
1 10.3.14 7.12.16 1.1.16 5.30.16
2 2.9.16 4.1.16 1.1.16 5.30.16
3 5.2.16 10.11.16 1.1.16 5.30.16
4 12.1.13 2.8.14 1.1.16 5.30.16
5 1.7.16 1.20.16 1.1.16 5.30.16
6 1.1.17 3.2.17 1.1.16 5.30.16
end data.
The following syntax avoids mentioning the last day of each month separately, so it could be used to automate across any number of months. The trick to check if the date is the last day of the month, is by checking day "0" of NEXT month:
do repeat inpr=inPrison1 to inPrison5/ mon=1 to 5.
compute inpr=( PrisonStart<=date.dmy(1,mon,2016) and
(PrisonEnd>=date.dmy(0,mon+1,2016) or missing (PrisonEnd)) ).
end repeat.

Grouping array by sequencial date

This is more of a brains storm question
I would like to run something like
Student Model
id
name
age
so on...
GameStatus Model
id
game_id
student_id
status
created_at
Student has many game_statuses.
so every time a student opens a game it is added to the game_status model
I want to send little messages like... Congratulation! You have completed 5 challenges in a row.
So the marks are 5, 10, 15, 20 and 30.
It's pretty simple till here. I just loop through students and then loop through their game_statuses and if the count comes up to one of those marks I'll send a message.
But I want to make sure they hit those marks in a row.
As in 5 in the last five days or 10 in the last 10 days not broken.
So 3 times the week after last 1 time last week and 1 time this week will return 5 but it should not trigger the reward.
I was thinking of looking into the created_at date and check if they are in a array of dates such as [today, today -1, today -2, today -3, today -4]
#student = Student.find(param[:id])
#games = #student.game_statuses
total_games = []
for game in #games do
if game.status = 'completed'
total_games += game.id
end
end
return total_games.count
Would it be possible to achieve this in a more elegant way?

.change function is not working for Dates for even number of months in ruby

Hi I have define this method
def change_date
date = Date.today
start_date = date.change(year: 2015, month: (2 * 3)).at_beginning_of_quarter
p 'aaaaaa'
p start_date
end
give me invalid date error .change is not working or am I doing it in a wrong way please guide me how to solve this. Thanx in advance.
This is because the month you are specifying doesn't have the current day.
I mean the current month (July) has 31 days but the month you're setting (June) has only 30 days. You can change your code like so:
# in Rails:
date = Date.today.beginning_of_month # or Date.today.change(day: 1)
Then chain your 'change' in front of the date variable.
This actually happens, because today is the 31 of July, and not all months have 31 days in it, for example June, the 6th month, has only 30 days in it.

Rails: Is there away to get the Date object that is the closest Monday to today?

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

In Rails, display time between two dates in English

In a Rails project I want to find the difference between two dates and then display it in natural language. Something like
>> (date1 - date2).to_natural_language
"3 years, 2 months, 1 week, 6 days"
Basically this for ruby.
Google and the Rails API haven't turned up anything. I've found some things that will give you the difference in one unit (ie, how many weeks between two dates) but not something that will accurately calculate years, months, weeks, days all together.
The Rails' ActionView module includes two methods that may do what you require:
distance_of_time_in_words
distance_of_time_in_words_to_now
The other answers may not give the type of output that you're looking for, because instead of giving a string of years, months, etc., the Rails helpers just show the largest unit. If you're looking for something more broken down, here's another option. Stick this method into a helper:
def time_diff_in_natural_language(from_time, to_time)
from_time = from_time.to_time if from_time.respond_to?(:to_time)
to_time = to_time.to_time if to_time.respond_to?(:to_time)
distance_in_seconds = ((to_time - from_time).abs).round
components = []
%w(year month week day).each do |interval|
# For each interval type, if the amount of time remaining is greater than
# one unit, calculate how many units fit into the remaining time.
if distance_in_seconds >= 1.send(interval)
delta = (distance_in_seconds / 1.send(interval)).floor
distance_in_seconds -= delta.send(interval)
components << pluralize(delta, interval)
# if above line give pain. try below one
# components << interval.pluralize(delta)
end
end
components.join(", ")
end
And then in a view you can say something like:
<%= time_diff_in_natural_language(Time.now, 2.5.years.ago) %>
=> 2 years, 6 months, 2 days
The given method only goes down to days, but can be easily extended to add in smaller units if desired.
I tried Daniel's solution and found some incorrect results for a few test cases, due to the fact that it doesn't correctly handle the variable number of days found in months:
> 30.days < 1.month
=> false
So, for example:> d1 = DateTime.civil(2011,4,4)
> d2 = d1 + 1.year + 5.months
> time_diff_in_natural_language(d1,d2)
=> "1 year, 5 months, 3 days"
The following will give you the correct number of {years,months,days,hours,minutes,seconds}:
def time_diff(from_time, to_time)
%w(year month day hour minute second).map do |interval|
distance_in_seconds = (to_time.to_i - from_time.to_i).round(1)
delta = (distance_in_seconds / 1.send(interval)).floor
delta -= 1 if from_time + delta.send(interval) > to_time
from_time += delta.send(interval)
delta
end
end
> time_diff(d1,d2)
=> [1, 5, 0, 0, 0, 0]
distance_of_time_in_words is the most accurate here. Daniel's answer is actully wrong: 2.5 years ago should produce exactly 2 years, 6 months. The issue is that months contain 28-31 day, and years might be leap.
I wish I knew how to fix this :(
DateHelper#distance_of_time_in_words
def date_diff_in_natural_language(date_from, date_to)
components = []
%w(years months days).each do |interval_name|
interval = 1.send(interval_name)
count_intervals = 0
while date_from + interval <= date_to
date_from += interval
count_intervals += 1
end
components << pluralize(count_intervals, interval_name) if count_intervals > 0
end
components.join(', ')
end

Resources