I am creating a ROR app for an e-commerce site which handles the management of renting items for a period of time. The items will be physically delivered and picked-up. Item are always rented for 30 days.
So the problem I am facing, is I need to somehow get which days an item can be rented and is available for at least 30 days from that point. (for example, a customer couldn't rent an item today if it is reserved to be rented 10 days from now)
In my database I have a rentals table that stores the pickup and delivery date.
I will be using a jQuery datepicker, and just need to load available dates 1 month at a time (I can redo the query each time the next month button is pressed to hide unavailable dates)
What would be the best approach to performing this type of query and getting all the days in a month in which an item is available for 30 days? I could always iterate through every single day in the month and check if there are any other records within 30 days, but that seems like a surplus of queries.
Any help would be greatly appreciated.
Thanks,
Tyler
How do you know the day an item is reserved to be picked up? Make a query based on that. Perhaps your Rental model has a reserved_on attribute?
class Rental < ActiveRecord::Base
scope :available, where('rented = ? AND reserved_on > ?', false, 30.days.from_now)
end
EDIT in response to comments
If are looking at a single object you could create methods on it something like this:
def last_available_day
delivery_date && delivery_date - rental_period # 30 days
end
def is_available_on?(date)
return true unless last_available_day
date <= last_available_day
end
Related
I am developing an automated calendar for a school on AirTable, but I am struggling to add if there is a bank holiday between the start date and end date. If the bank holiday falls on a weekday, it needs to add +1 day to the end date.
I have created a "bank holiday" table, as well as a full calendar table (containing everyday of the year and what weekday it falls on).
I'd like for the user to have the end date generated automatically by only writing the start date.
For example:
I have a session starting on Monday, 7th of march. The "session type" would be "Course 1", which has a duration of 10 days (two weeks, monday to friday - I don't know if the duration should be 14 days instead?). If there's a bank holiday in the 10 days after the start date, and it's on a weekday, it needs to add +1 to the end date. If the end date falls on a sunday, it needs to add +2 so that it ends on a Monday.
The course is divided in multiple classes, it would also be awesome to have a timeline view with the full Course and all the classes.
Here are some screenshots of my tables :
I know there is a possibility to write a script in Python with an AirTable API... but is there an easier way ?
I've dealt with a problem like this before: when an event happened in relation to other events mattered.
You'd think by the use of the words "in relation" that you could deal with this in a relational way, but I see it as a rule:
If Event A happens at Time 1, and it's so much or so little time from Event B, then do with Event A...
And, again by my way of thinking, rules are expressed in code.
And if you actually even can express that with a relation, I think it'd be very convoluted.
So, for your problem, you need to encode every schedule-able day that's 10 working days or less before a Bank Holiday (BH) so that if the day is selected you know it's less than 10 days from a BH and can conditionally add another day to the end date.
I looked at your examples, and here's my solution. I have a Calendar table which has all days, and two supporting "Bank Holiday" fields: does the date fall on a BH, and if not, is the date 10 days or less from a BH. We're looking at my All view, here:
I also have the view, Weekday, not bank holiday, and that's the view that you can pick a day from to schedule an event from the Event table.
The Event table:
You pick a day from the Start day field. Start day < 10 work days to Bank holiday? is a lookup field from the Calendar table for that day. End day is this formula:
DATETIME_FORMAT(
DATEADD(
{Start date},
IF(
{Start day < 10 work days to Bank holiday?},
14,
13),
'day'),
'ddd, MMM Do')
In my view, I will be ordering a list of items by their start_time. There are instances, however, where there might be a start_time of 1:00 AM (the following day) that should show up as after a start_time of 11:00 PM (previous day). Since the Time datatype stores a default date of 2001-01-01, it considers an entry of 1:00 AM to be on 2001-01-01, not 2001-01-02.
Thus, my question is, is there a way to change the default date in the Time datatype?
I suppose an obvious solution would be to instead store the start and end times in a DateTime datatype and enter a corresponding date. For this application, however, it is customary to refer to a start time of 1:00 AM as "belonging to" the previous day and would thus be confusing to enter the following day's date. (E.g. When attending your favorite band's concert on a Friday night, their set might start at 12:30 am Saturday morning, but you would still consider the concert to be on a Friday night). Thank you for your help.
Not a direct answer to your question, but a possible solution to your problem: you could create a custom sort that sorts times < 1 AM last.
sorted_concerts = Concert.order('CASE WHEN start_time <= "2001-01-01 01:00:00" THEN 2 ELSE 1 END, start_time')
This way you can leave the db columns as is but get your expected order for concerts. Excepting for this 1 AM inversion, the times will sort normally in ascending order.
After much more research, it doesn't seem like the default date of "2001-01-01" that Ruby applies to a time stored in a Time datatype can be adjusted. I guess this answers my question - but - to solve the problem, I changed the db columns to datetime datatype and logged a default date, that would adjust if the time entry was after midnight. Controller action below:
def create
start_time = DateTime.parse("#{run_of_show_item_params[:date]} #{run_of_show_item_params[:start_time]}")
end_time = DateTime.parse("#{run_of_show_item_params[:date]} #{run_of_show_item_params[:end_time]}")
#run_of_show_item = RunOfShowItem.new(run_of_show_item_params)
#run_of_show_item.start_time = start_time
#run_of_show_item.end_time = end_time
#run_of_show_item.start_time+=1.days if run_of_show_item_params[:start_time] < "07:00"
#run_of_show_item.end_time+=1.days if run_of_show_item_params[:start_time] < "07:00"
if #run_of_show_item.save
flash[:success] = "Performance added!"
redirect_to :back
else
render 'new'
end
end
I want to create an array of the number of items created each hour, each day.
I'm tracking how people are feeling, so my model is called TrackMood It just has a column called mood and the timestamps.
If I do
TrackMood.where(mood: "good").group("hour(created_at)").count
I get something like
{11=>4, 12=>2, 13=>2, 15=>1}
I've got 2 issues here
1 How do I add the day into this so it doesn't just add the items created yesterday at 11 o'clock to the items added today at 11 o'clock?
2 How do I make sure it says 0 for hours when nothing is created?
1) Instead of grouping on just the hours part of the date you'll need to group part of the date that is relevant i.e. the date up to the hours and not including anything more specific than that. E.g.
TrackMood.where(mood: "good").group("date_format(created_at, '%Y%m%d %H')").count
2) You're always going to get a hash back from this call even if it doesn't find any groups. If you want to check how many groups there are you can call .size or .count on it.
For PostgreSQL you can use date_part
SO-post - Rails & Postgresql: how to group queries by hour?
In my app a User has_many Gestures. What is a good way to calculate how many subsequent days the User has done Gestures?
Right now I'm doing it like below, and it works as I want it to. But clearly it doesn't scale.
class User < ActiveRecord::Base
# ...
def calculate_current_streak
return 0 unless yesterday = gestures.done_on_day(Date.today-1)
i = 0
while gesture = self.gestures.done_on_day(Date.today - (i+1).days).first
i += 1
end
i += 1 if gestures.done_on_day(Date.today).first
i
end
end
Thanks! Special points to the one who can work in a way to only track business days too :)
Think about it this way:
The length of the streak is equivalent to the number of (business) days passed since the last (business) day the user didn't use gestures at all, right?
So the solution boils down to calculating most recent day that the user didn't make a gesture.
In order to do this most easily, there's a trick that lots of DB admins use: they create DB table with all dates (say, all the dates from year 2000 to year 2100). The table needs to have only date field, but you can throw in a Boolean field to mark non-working days, such as weekends and holidays. Such table can be handy in lots of queries, and you only have to create and fill it once. (Here's a more detailed article on such tables, written for MS SQL, but insightful anyway.)
Having this table (let's call it dates), you can calculate your pre-streak date using something like (pseudo-SQL):
SELECT d.date
FROM dates d
LEFT JOIN gestures g ON d.date = g.date AND g.user_id = <put_user_id_here>
WHERE d.date < TODAY() AND g.date IS NULL AND d.work_day = TRUE
ORDER BY d.date DESC
LIMIT 1
I need to grab the records for same day of the week for the preceeding X days of the week. There must be a better way to do it than this:
Transaction.find_by_sql "select * from transactions where EXTRACT(DOW from date) = 1 and organisation_id = 4 order by date desc limit 7"
It gets me what I need but is Postgres specific and not very "Rails-y". Date is a timestamp.
Anyone got suggestions?
How many days do you want to go back?
I have written a gem called by_star that has a dynamic finder suited for finding up to a certain number of days in the past. If the number of days was always a number you could use this finder:
Transaction.as_of_3_days_ago
If it was dynamic then I would recommend using something such as future or between, depending on if you have transactions in the future (i.e. time travel):
Transaction.future(params[:start_date].to_time)
Transaction.between(params[:start_date].to_time, Time.now)
AFAIK Rails has no any methods to do this by other way. So best, and faster, solution - build DOW index on date column and use your query.