Appointment scheduling in Rails? - ruby-on-rails

I have a simple application with a customer and appointment models, system admin can create customer and create appointments for that particular customer, is there a Rails plugin that handles scheduling to ensure no two appointments overlap each other? i.e no two appointments at the same time.
A plus would be if I can set up more than one schedule, i.e shop has 2 instructors for lessons, when selecting appointment we can select which instructor etc.
What's the best way to do this?
Thanks

def is conflicting
if(appointemnt1_start < appointment2_end && appointment2.start < appointment1.end)
return true
end
end

Not that I know of. It's fairly simple in theory, though. Subtract Time B from Time A, and if they're within X minutes of each other (however long an appointment takes), there's a conflict.

Related

Building an appointment booking system in Rails

I am looking to build an appointment booking app with the following characteristics:
- Users can be service providers or buyers
- Service providers set their availabilities (but can only set their availabilities at a maximum of 6 months ahead)
- Buyers can then book appointments based on those availabilities - each appointment, based on the type of service, takes a different amount of time
- Based on the appointment that a buyer selects, a different set of availabilities is shown depending on how long the service takes
What I've built is the following:
- A TimeSlot model where I create a number of generic 30 minute time slots based on a start_time and end_time attribute. In order to make these time slots extend 6 months into the future, I have a background job running each day that creates all the new time slots necessary
class TimeSlot < ActiveRecord::Base
has_many :user_time_slots
# ... more methods below
end
- A UserTimeSlots model that basically represents a service provider's availability that they can set. So when they create a user_time_slot, they are essentially saying that they are available at that time.
class UserTimeSlot < ActiveRecord::Base
belongs_to :time_slot
belongs_to :service_provider, :class_name => "User"
belongs_to :appointment
end
- An Appointment model that has many user_time_slots. It has many because an appointment belongs to a service which takes a certain amount of time (a time_required attribute on services) and it might span a number consecutive user_time_slots.
class Appointment < ActiveRecord::Base
has_many :user_time_slots
belongs_to :buyer, :class_name => "User"
belongs_to :service_provider, :class_name => "User"
belongs_to :service
end
- A Service model that has many appointments and belongs to a service provider who creates that service.
class Service < ActiveRecord::Base
has_many :appointments
belongs_to :service_provider, :class_name => "User"
end
This domain model works; however, I am wondering if there is a better way to do this because of the following:
It seems a little clunky to me to be creating TimeSlot records every day on my backend using a background job - the TimeSlots really have the sole purpose of having a start and end time and then being associated to.
Once the user (buyer) selects a service they want, I am not sure how I would efficiently find the x number of user_time_slots that are consecutive and, therefore, available for booking the appointment (for example, if I have 30 minute time slot intervals and a user selects an appointment that will take 3 hours, I would have to find 6 consecutive time slots). For example, if a user clicks on an instance of a service I would have to 1) get the time required for that service (easy enough to do) and 2) I'd have to find ALL of the user's user_time_slots and collect their associated time_slots, then compare each time slot's start and end times to one another to find consecutive ones. This just seems like way too much iteration to me and seems like this will bog down my application!
Does anyone have a better way or solution to do this (particularly around the topic of finding consecutive time slots)?
Look like a fun project. :)
I would personally not model the "existing time", i.e I would not have a background job create "empty" data.
I would try a model like this:
Where the User table?
I would not use a shared User model for the two different User types. My gut feeling is that they need to be different, if not now, most surly over time. Also it makes the model more clear, in my opinion. If there is a login or any auth, I would add a User table for that specific data and rather have Service Provider and Consumer have some relation to this.
Service Provider manages availability
The Service Provider would only need an empty calendar (or similar), listing only her already entered Service Availability, per Service.
Consumer books appointment
The Consumer would search/browse/navigate Service Availability, per Service
Depending on business logic, i.e. will a Consumer always schedule the whole defined time slot? Or could the Consumer book a preferred time slot. As long as the booked slot is within the given Service's available time slot?
Scheduling
So we only put Service Availability records in when the Service Provider manages her availability for a specific Service. Empty is simply considered not available.
We only put Appointment records in when a Consumer books an Service, based on Service Availability.
If its possible for the Consumer to book only a part of a Service Availability time slot, I would recommend to create two (or one) new Service Availability records for the time left over. Checking if time is available could be done by comparing Service Availability with Appointment, but this would not be optimal.
Better would be to just query Service Availability for a specific Service and get a availability schedule, where no record would mean no availability.
There are a lots of ifs here, depending on your specifc needs and requested business logic. But I hope this helps with some inspiration and ideas.
I would have a single model for each availability period. Each period has a start_time and end_time. They can be easily validated to not be more than 6 months in advance, and you can check if an appointment will fit. Your time slot does not need to belong to an appointment as such; the appointment would simply have a time and a service provider, and said service provider's availability would change to reflect the booked appointment.
class AvailabilityPeriod < ActiveRecord::Base
belongs_to :service_provider, :class_name => "User"
validates :end_time, :inclusion => { :in => Time.now..(Time.now + 6.months) }
end
For example, you could find all possible availabilities for a given service and provider like this:
duration = #service.duration
available_times = #provider.availability_periods.where (end_time - start_time > duration)
Booking an appointment is a bit more tricky. You need to split/shorten the availability period. For example, say a provider has the following availability:
May 30th 12:00 - 18:00
An appointment is booked for May 30th 14:00 - 16:00
The old availability needs to be removed and replaced by two new ones:
May 30th 12:00 - 14:00
May 30th 16:00 - 18:00
But this is pretty easy to do in a model method.
I like jpriebe's solution, though I would propose a slight change to the validation: use (Date.today + 6.months).end_of_day for the extreme end of the range.
The reason for my suggestion is to make the system slightly more user-friendly. If you used Time.now, and a user wants to create an AvailabilityPeriod ending at 4pm, but it is still somewhat early in the morning (say, around 10:15am), 4pm would be out of the range. To create the AvailabilityPeriod, they'd have to remember to come back later - and they might forget / are taking the lasagna out of the oven / insert distraction here.
Admittedly, there is really only one possible time in which this scenario could occur, and that is if a user is trying to create an AvailabilityPeriod exactly 6 months ahead (e.g., May 27 => Nov 27). Most of the time, I'm certain most users would not be setting up an AvailabilityPeriod that far in advance. Still, using .end_of_day would extend the acceptable time-range to the end of the appropriate day.
Rather than arbitrarily segmenting the timeslots, especially if few appointments are likely to be exactly one 30 minute interval, create Appointments with arbitrary start and stop times. Assuming Appointments will not span several days you can have a Day model that has_many Appointments. You will have to handle the overlap calculations programmatically, but you won't be beating your database to death to do so, you'll only need to join Day, Appointment and Providers and then run your calcuations to find your open slots. Appointments can be for 5 minutes or 6 hours. Doesn't matter.

Rails: Track Points On A Weekly Basis

In my current application, I need the ability to track points on a weekly basis so that the point totals for the user reset back to zero each week. I was planning on using the gem merit: https://github.com/tute/merit to track points.
In my users profile I have a field that is storing the points. What I have been unable to locate is how I can have rails on an auto basis for all users clear this field.
I have come across some information Rails reset single column I think this may be the answer in terms of resetting it every Sunday at a set time -- but I am uncertain on this last part and in addition where the code would go (model or controller)
Also, would welcome any suggestions if their is a better method.
You'd be better making a Point model, which belongs_to :user
This will allow you to add any points you want, and can then query the table based on the created_at column to get a .count of the points for the timespan you want
I can give you more info if you think it appropriate
Models
One principle we live by is to extend our models as much as possible
You want each model to hold only its data, thus ensuring more efficient db calls. I'm not super experienced with databases, but it's my opinion that having a lot of smaller models is more efficient than one huge model
So in your question, you wanted to assign some points to a user. The "right" way to do this is to store all the points perpetually; which can only be done with its own model
Points
#app/models/point.rb
Class Point < ActiveRecord::Base
belongs_to :user
end
#app/models/user.rb
Class User < ActiveRecord::Base
has_many :points
end
Points table could look like this:
points
id | user_id | value | created_at | updated_at
Saving
To save the points, you will literally just have to add extra records to the points table. The simplest way to achieve this will be to merge the params, like this:
#app/controllers/points_controller.rb
class PointsController < ApplicationController
def new
#points = Point.new
end
def create
#points = Point.new(points_params)
#points.save
end
private
def points_params
params.require(:points).permit(:value).merge(:user_id => current_user.id)
end
end
You can define the "number" of points by setting in the value column (which I'd set as default 1). This will be how StackOverflow gives different numbers of points; by setting the value column differently ;)
Counting
To get weekly countings, you'll have to create some sort of function which will allow you to split the points by week. Like this:
#app/models/point.rb -> THIS NEEDS MORE WORK
def self.weekly
where(:created_at => Time.now.next_week..Time.now.next_week.end_of_week)
end
That function won't work as it is
I'll sort out the function properly for you if you let me know a little more about how you'd like to record / display the weekly stats. Is it going to be operated via a cron job or something?
Based on your description, you might want to simply track the users points and the time that they got them. Then you can query for any 1 week period (or different periods if you decide you want all-time, annual, etc) and you won't lose historical data.

Where in Rails to fill in gaps in time series?

My basic set of models looks roughly like this (simplified):
[ PayPeriod ]
id
start_date
end_date
notes
[ Transaction ]
id
amount
fkPayPeriod
When a user logs in, I want to present them with a list of the last 4 pay periods. The user can select a pay period to enter notes and transactions.
I'm inclined to just do something like this in the controller:
def index
#pay_periods = PayPeriod.get_or_create_last(4)
end
The PayPeriod model itself would implement the behavior, and records would be created "on demand" as users used the app.
An alternative approach might be to have some other (background) process that combs through accounts on a daily basis to proactively create new PayPeriods. Although neither solution strikes me as particularly challenging, I'd like to get some feedback on where others feel this functionality belongs conceptually.
I'd say it depends on what a PayPeriod is, conceptually. Is it a universal description of time periods on a common calendar (i.e., the year 2011 has 26 pay periods of two weeks each)? If so, then the creation of all the PayPeriods belongs in your db/seeds.rb file.
If a PayPeriod belongs to an account, I'd say that the creation of the last four PayPeriods belongs in an after_create hook on the account in question. Going forward from there, I think the best practice would be to create new PayPeriods as your user needs them; that is, the first time I go to add notes or a transaction to a PayPeriod is probably when it should get created.
If for any reason you have a system policy in place that allows users to edit a specific window of PayPeriods (e.g., you can edit this and the next calendar year), then you might set up a Rake task and hook it up to a Cron job that runs it however often it needs to be run (e.g., if you're limiting your window by calendar year, then you could set up a Cron job to run every January 1 at 12:01am that creates the year's PayPeriods).

Rails Filtering Best-Practices

I have a web app that has the following models: Shifts, Years, Checkouts, Sales and Visitors.
A Shift has_many Checkouts, Sales, Visitors
A Year has_many Shifts
Checkouts, Sales and Visitors belong_to Shift
Shifts belong_to Year
In my app, I want to have many different years present, although only ONE is active at any given time. When I display Shifts, for example, I only want Shifts from the active year to be displayed. That's easy enough.
What I'm having a real hard time with is writing a query to get all of the checkouts from the active year. Is there a simple way to go about doing this? The active year is actually set in a session variable called session[:current_year_id]. I thought the best way to go about doing it would be to do something like:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).checkouts
But that's not working for me. What do I need to change to get that to work?
Note that checkouts, sales and visitors DO NOT have their own year_id. They have a year_id through the shift that they belong to.
Resolved:
I upvoted all the answers, since all helped. I used the for_year lambda expression. I've upvoted all the answers.
The final answer was to use:
Shift.for_year(session[:current_year_id]).collect{ |shift| shift.checkouts }
You can create a scope in Shift model that takes current year as parameter:
class Shift < ActiveRecord::Base
has_many :checkouts
scope :for_year, lambda { |current_year_id| where('year_id = ?', current_year_id) }
end
and in the controller, you can get the checkouts for current year by chaining to the scope:
Shift.for_year(session[:current_year_id]).map(&:checkouts)
Using .where returns a relation, so there are two possible answers based on your intention.
If you want to find the checkouts belonging to the first shift, do:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).first.checkouts
If you want all checkouts for all shifts, do:
#checkouts = Shift.where('year_id = ?',session[:current_year_id]).collect{|shift| shift.checkouts}
You could try:
Shift.find_all_by_year_id(session[:current_year_id])
This assumes current_year_id is in the session.
I have had problems with using .where, which I fixed by using a dynamic finder.

Keeping the history of model associations

I have multiple models that need to have their history kept pretty much indefinitely or at least a very long time. The application I would keep track of daily attendance statistics for people in different organizations, and a couple of other similar associations. I've realized that I can't ever delete users due to the user not showing up in a query for attendance anytime before said user was deleted. The problem I'm having is finding a nice way of keep track of old associations as well as querying on those old associations.
If I have a connecting table between Users and Organizations, I could just add a new row with the new associations between a User and the Organization the user belongs to, and query on the old ones based on the date, but I really don't have any elegant way of doing this, everything just feels ugly. I was just wondering if anyone has dealt with anything like this before, and maybe had a solution they had already implemented. Thanks.
From a modeling point, the relationship sounds like the one between Employee and Employer, namely Employment. This would hold a reference to both Employee and Employer along with some notion of the TimePeriod (ie, startDate and end Date). If you wanted to query 'active' employees then they are all the ones with a startDate <= Now() && endDate >= Now(), 'terminated' employees have endDate < Now(), etc.
I can't tell in your case what the relationship is between Users and Organizations, and what makes the relationship start and end, but a concept similar to Employment, Membership, Enrollment or Contract is likely there. When you say daily attendance, is it not daily over a period of time? The time period is the basis for your queries.
Hope that helps,
Berryl
Create an is_deleted field so that you can still query those "deleted" users, but modify your code so that they will behave everywhere else as if they are deleted. Then you never need to actually delete the row and lose data.
There are a number of plugins that keep track of revisions to models, including their associations. Take a look at this search for revision-related plugins.

Resources