I need to define a custom month range as a project_year.
Rather than a year starting in January and ending at the end of December, I need to define a project_year as starting August 1st and ending on the last day of July.
I need to group all reports by project_year and then by month. Report has a report_month attribute(dateTime). Essentially I would need to display the reports like this:
Projects:
2015
August
September
October
November
Etc
2014
August
September
October
November
Etc
2013
August
September
October
November
Etc
I've been playing around with the Array.sample method, but without success.
reports = #station.reports.order("report_month asc")
range = reports.last.report_month.year..reports.first.report_month.year
range.to_a.each do |year|
start = year.beginning_of_year + 7.months
finish = (year.beginning_of_year + 1.year - 6.months).end_of_month
yr = reports.sample{ |r| report.report_month >= start and <= finish }
<h1><%= year.strftime("%Y")</h1>
yr.each do |m|
<h1><%= m.strftime("%b")</h1>
end
end
I realize the above isnt appropriate view code, Im just trying to indicate the manner I was trying to use. Which became cumbersome and hard to troubleshoot. After struggling with this for a while I decided to ask here.
Ultimately my the ideal would be to use the group_by feature outlined here:
http://railscasts.com/episodes/29-group-by-month Only instead using group_by project_year.
Is this possible with Rails? Or is there a better way to group by a custom month range?
Does it help if you add a the customer field on the model such as quarter then use the group_by api.
def quarter
"#{created_at.month/4}, #{created_at.year}"
end
Related
TL;DR:
I have a database with thousands of appointments that have start_time and end_time attributes. Given a date range (May 26-June 31), how do I find
every appointment that happens throughout this range?
Appointment from May 15 to May 25 NOT included
Appointment from May 15 to May 29 needs to be included
Appointment from June 1 to June 3 needs to be included
Appointment from June 20 to July 5 needs to be included
Appointment from May 15 to July 15 needs to be included (most difficult part of the problem)
Appointment from July 1 to July 4 NOT included
We have an appointment model that has a start_time and an end_time. If an appointment occurs on any days during the monthly view, it needs to be loaded into the instance variable (#monthly_appointments) so that the simple_calendar gem can display it on the calendar.
Example: An appointment from June 1 to June 3 needs to show up when the user views the "June 2020" calendar. An appointment from May 15 to July 15 also needs to show up every single day during June.
There's 6-day padding on each side of the calendar dates, since if the week starts on a Saturday (June 1), you'll have May 26 - May 31 showing on the June calendar (see picture).
You'd think it was just as easy as saying "If an appointment starts or ends during the given month, add that appointment to the instance variable. However, there are cases when an appointment starts on May 15 and goes for 60 days, until July 15. The appointment neither starts nor ends during June, but it needs to still show up on the calendar.
Originally, we told users, "appointments cannot last longer than 6 months" and then we used this lookup where we assumed, "if the appointment started during the last 6 months, include it in the variable and then we'll let the calendar gem work out the rest."
#monthly_appointments = current_user.appointments.includes(:pet).where(start_time: (Time.zone.now).beginning_of_month - 6.months..(Time.zone.now).end_of_month + 6.days))
However, this query can sometimes save 3,000+ appointments in the variable, when, in reality, there are only 50-70 appointments that NEED to be shown for that month.
So, I wrote up the following code, and it succeeds in finding appointments that occur on at least one day during the calendar's timeframe. It compares the monthly calendar's range(May 26..June 31) and an appointment's range(June 1..June 3), and then looks for any dates that occur in both arrays (&). It works well, but it takes a bit of time because it needs to load ALL the appointments for a user (thousands) and then goes through each one to see if it occurs during that month.
Does anyone have any other clever solutions to this query issue? I'm sure something exists out there, but I haven't found it yet. Thanks!
month_dates = ((Time.zone.now.beginning_of_month - 6.days)..(Time.zone.now.end_of_month + 6.days)).to_a
#monthly_appointments = current_user.appointments.includes(:pet).select do |appt|
#create array of appointment dates and see if it intersects any of the monthly date array
appt_dates = (appt.start_time.to_date..appt.end_time.to_date).to_a
(month_dates & appt_dates).present?
end
Models:
Appointment(start_time, end_time, note, user_id, pet_id)
Pet
has_many appointments
User
has_many appointments
Here's an example.
carley = Pet.find(12)
Appointment.create(pet_id: carley.id, start_time: "May 15 2020 06:00:00", end_time: "July 15 2020 06:00:00"...)
When I'm looking at the June 2020 calendar, this appointment needs to show up on every single day.
from_time = '2019-05-26'.to_date
to_time = '2919-06-30'.to_date
#appointments = Appointment.where('start_time <= ? AND end_time >= ?', to_time, from_time)
The above will select all appointments that are included in or overlap the from_time and to_time range, and also appointments that start before the range and end after the range.
Let's say I have three users created on different dates. Now I want do have a graph of users progression. So I want to get something like:
{
Thu, 02 Nov 2017=>1,
Sat, 04 Feb 2017=>2,
Wed, 21 Mar 2018=>3
}
It's very similar to grouping by created_at::date, but I want to have number of all the records created before this date rather than number of items created exactly on this date.
How can I achieve this using group_by and aggregate functions in Postgresql? I need it for Ruby on Rails project, but I expect simple vanilla SQL, no maps and complex queries.
I am no rails expert and my solution requires loading a big relationship like:
#u = User.all
And then find the smallest creation date :
#start = #u.minimum("created_at")
Then you can calculate the number of days between creation and now:
#days = ((Time.now - #start)/1.day).to_i
Then you can calculate for each day the number of users already created:
#days.each do |day|
puts "day "+day.to_s
puts #u.where("created_at < ?", #start+day.day).count
end
Probably some easier solutions in SQL though. (Also you may take only a subset of users by choosing a range of dates not to exceed the server memory)
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.
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
FYI, There is some overlap in the initial description of this question with a question I asked yesterday, but the question is different.
My app has users who have seasonal products. When a user selects a product, we allow him to also select the product's season. We accomplish this by letting him select a start date and an end date for each product.
We're using date_select to generate two sets of drop-downs: one for the start date and one for the end date.
Including years doesn't make sense for our model. So we're using the option: discard_year => true
When you use discard_year => true, Rails sets a year in the database, it just doesn't appear in the views. Rails sets all the years to either 0001 or 0002 in our app. Yes, we could make it 2009 and 2010 or any other pair. But the point is that we want the months and days to function independent of a particular year. If we used 2009 and 2010, then those dates would be wrong next year because we don't expect these records to be updated every year.
My problem is that we need to dynamically evaluate the availability of products based on their relationship to the current month. For example, assume it's March 15. Regardless of the year, I need a method that can tell me that a product available from October to January is not available right now.
If we were using actual years, this would be pretty easy.
For example, in the products model, I can do this:
def is_available?
(season_start.past? && season_end.future?)
end
I can also evaluate a start_date and an end_date against current_date
However, in setup I've described above where we have arbitrary years that only make sense relative to each other, these methods don't work. For example, is_available? would return false for all my products because their end date is in the year 0001 or 0002.
What I need is a method just like the ones I used as examples above, except that they evaluate against current_month instead of current_date, and past? and future months instead of years.
I have no idea how to do this or whether Rails has any built in functionality that could help. I've gone through all the date and time methods/helpers in the API docs, but I'm not seeing anything equivalent to what I'm describing.
Thanks.
Set the year of the current date to 1000 and perform a range compare with the start and end dates.
def is_available?
t = Date.today
d = Date.new(1000, t.month, t.day)
(season_start..season_end).include?(d) or
(season_start..season_end).include?(d+1.year)
end
Second comparison above is to address the following scenario:
Lets say today Jan 15 and the season is from Oct - Feb. In our logic, we set the date to Jan 15 1000. This date will not be within Oct 1 1000 - Mar 1 1001. Hence we do the second comparison where we advance the date by a year to Jan 15 1001, which is within the range.
You might want to consider not using Date objects. What if season_start_month and season_end_month were each an integer 1-12, set with your dropdown? Then when doing your is_available? comparison, you could dynamically create the full date for season_start, doing some math for transitions over December to January. This could use some refactoring, and isn't tested, but should do the trick. Assumes that season_start_month and season_end_month are integers stored in this model:
class Product < ActiveRecord::Base
def is_available?
now = Time.now
season_start < now && now < season_end
end
def season_start
now = Time.now
current_month = now.month
season_start_year = season_start_month < current_month ? now.year : now.year - 1
Time.local(season_start_year, season_start_month)
end
def season_end
now = Time.now
current_month = now.month
season_end_year = season_end_month > current_month ? now.year : now.year + 1
Time.local(season_end_year, season_end_month)
end
end