1

[ UPDATED SCHEMA: https://i.stack.imgur.com/kBtno.png ]

I'm tasked with developing an appointment booking system that is designed for a small medical office. It is a Rails 3.2 app.

I'm having difficulty designing a database schema that makes sense to me.

Question: Given the following information, what is the correct relationship between doctors, patients, appointments, chairs and time_slots?

Patients need to make appointments with a doctor's office. Depending on the type of appointment, each appointment is scheduled for one or more adjacent time_slots, and whether or not there can be two appointments scheduled for time_slots with the same start_time and end_time is determined by appointment type. (Double-booking is allowed based on the type of appointments.)

App Specs:

  1. Registered users make appointments via an appointment request form on the website.
  2. Appointments take up a certain pre-set amount of adjacent time_slots. This is determined by appointment category/type. This length can be adjusted by the admin, as well as the length of each time_slot.
  3. To help speed up the request process, unavailable/already-booked times are hidden from the calendar on the appointment request form.
  4. On the admin-facing interface, administrators can confirm an appointment-request and make an appointment, and they can also update, create and delete scheduled appointments.
  5. All appointments are held in a "chair"--like a dentist's chair. An office has multiple chairs. One patient per chair for a given booked time slot.
  6. Appointments have fields for date, time of day, length appointment_type, double_bookable? (determined by appointment_type). Time_slots have a start_time and end_time, and a date.
  7. There is only one doctor in this office. But, certain types of appointments--that are less demanding of the doc's time-can be double-booked. In essence, two teeth cleanings can be booked for the same time slot, as long as they are held in **separate chairs**.

My Relationships:

Office < ActiveRecord::Base
has_many :doctors

Doctor < ActiveRecord::Base
has_many :patients
belongs_to :offices

Patient < ActiveRecord::Base
belongs_to :doctor
has_many :appointments

Appointment < ActiveRecord::Base
belongs_to :patient
has_many :filled_time_slots

FilledTimeSlot < ActiveRecord::Base
belongs_to :appointment
belongs_to :time_slot

TimeSlot < ActiveRecord::Base
has_many :filled_time_slots
belongs_to :chair

Chair < ActiveRecord::Base
has_many :time_slots
RKelley
  • 576
  • 6
  • 16
  • That link appears to have nothing to do with this question. – RKelley Aug 30 '14 at 00:51
  • Well that link is about Rails 2.3, I am using Rails 3.214. So that was unclear. Additionally, how is that relevant here? Is it somehow dangerous to develop in 3.2x now? – RKelley Aug 30 '14 at 05:41
  • Whoops, a bit of dyslexia on my part, but the point remains the same. If you're just getting started, do yourself a favor and use the most current version. It has a ton of enhancements, and 3.2 is going to stop getting security updates for severe issues soon. It already doesn't get some security updates - http://guides.rubyonrails.org/maintenance_policy.html. It sucks upgrading after the fact, particularly to a version that already existed at the time the project was started. I know this has nothing to do with your question, but you'll be glad you used the fresh stuff in a few month's time. – Brad Werth Aug 30 '14 at 05:51
  • Brad, I appreciate the tip, and I will take it in to account. – RKelley Aug 30 '14 at 09:31
  • Hi @RKelley, were you ever able get this scheduling application working? – captain awesome Aug 03 '15 at 16:36
  • @captainawesome Yes, however the source is housed in a private repo. If you'd like to check it out, email me at rskelley9@gmail.com. Additionally, I found that this database setup led to a very complicated back-end and a simpler solution could've been given to the job I was asked to do. So, I switched over to Sinatra + MongoDB + a very small cloud app. The office using this application will sync the cloud appointments with their local practice management system multiple times daily, automatically. I was also able to avoid HIPAA regulations this way. – RKelley Aug 05 '15 at 20:00
  • Also, a big lesson here was that scheduling and office management systems are extremely complicated, particularly when you add in the sensitive data element, and the element that most practices have some practice management system running on their local, closed network already and their employees are already trained to use this software (like PracticeWorks). If online appointment volume is low, the cost of developing this kind of system is just too high. Building something simpler for small offices, hacking a CRM, or leasing a more complete software suite to big offices is advised. – RKelley Aug 05 '15 at 20:10
  • Hi @RKelley, is checking out your repo still an option? Kindly please let me know. Greatly appreciate it, thanks. – captain awesome Aug 10 '15 at 22:42
  • 1
    For anyone interested I made the source for this iteration of the app available [here on GitHub](https://github.com/Greater-Than-Web-Development/dental_demo). This version got complicated quickly, but there's still a lot of a good code here. If you're interested in making contributions, comments, suggestions, please do. – RKelley Aug 21 '15 at 22:03

2 Answers2

3

I would do it like this:

Doctor
  has_many :appointments

Patient
  has_many :appointments

Office 
  has_many :chairs  

Chair
  #key-fields: office_id
  belongs_to :office
  has_many :appointments  

TimeSlot
  #key-fields: starts_at, :ends_at  

Appointment
  #key-fields: doctor_id, chair_id, time_slot_id, patient_id
  belongs_to :doctor
  belongs_to :chair
  belongs_to :time_slot  
  belongs_to :patient

The booking form is going to consist of getting all available time slots, and then, for each time slot, showing chairs which don't have an appointment in that time slot.

Max Williams
  • 32,435
  • 31
  • 130
  • 197
  • Thank you for helping. Question: If appointments can be made for multiple adjacent time_slots, shouldn't there be a many-many relationship between appointments and time_slots? I updated my question with the new schema I made late last night. Here is the schema for what you are describing: http://i.imgur.com/JkohjjU.png – RKelley Aug 29 '14 at 23:55
  • Ah yes in that case appointments and slots should be many to many. – Max Williams Aug 30 '14 at 09:32
  • Thank you very much for your answer, it helped provide clarity. I would give you an upvote If I wasn't a new member. – RKelley Sep 01 '14 at 18:39
  • You're welcome. I find the key to schema design is to keep it as common sense as possible, and make your models correspond to their real world equivalents in a common sense way. Good luck! – Max Williams Sep 02 '14 at 14:37
  • Wait, looking at the accepted answer above, do you disagree with chairs there? It made sense to me because appointments are made for one or more timeslots, and for a chair. There can only be two appointments in the same timeslots if there are different chairs. Do you still think chairs should belong to offices and not time_slot through appointment? – RKelley Sep 04 '14 at 09:17
  • Chairs are associated with offices because they are physically in an office: this has nothing to do with appointments. The appointment would be in a particular chair, and then you look up chair.office to see which office the appointment is in. A time_slot is literally just that: a start and end datetime. – Max Williams Sep 04 '14 at 11:57
  • Thank you, I updated the accepted answer to included both of your answers. – RKelley Sep 05 '14 at 02:32
3

I would add a boolean field to TimeSlot and write a custom validation allowing for conditional double-booking on TimeSlot. Associations below. (With the boolean field in your migration for TimeSlots, you could get rid of the FilledTimeSlots table.)

Office < ActiveRecord::Base
  has_many :doctors

Doctor < ActiveRecord::Base
  has_many :patients, through: :appointments
  has_many :appointments
  belongs_to :office

Patient < ActiveRecord::Base
  has_many :doctors, through: :appointments 
  has_many :appointments

Appointment < ActiveRecord::Base
  belongs_to :patient
  belongs_to :doctor
  belongs_to :chair
  belongs_to :timeslot

TimeSlot < ActiveRecord::Base
  validates_with :availability, unless: "appointments.nil?"
  validates_with :workday
  has_many :appointments
  has_many :chairs, through: :appointments 
#you could then specify a maximum of 2 chairs in your validation depending on the appointment type, something like this:

def availability
  if self.chairs.count == 0
     self.booked? == false
  elsif self.chairs.count == 2
     self.booked? == true
  elsif self.chairs.count == 1 and self.appointment.type == "cleaning"
     self.booked? == false
  else
      self.booked? == true
  end
end


Chair < ActiveRecord::Base
  has_many :appointments
  belongs_to :timeslot, through: :appointment
end

------------ Alternate Answer by Max Williams ------------------

 Doctor
   has_many :appointments

 Patient
   has_many :appointments

 Office 
   has_many :chairs  

 Chair
   #key-fields: office_id
   belongs_to :office
   has_many :appointments  

 TimeSlot
  #key-fields: starts_at, :ends_at  

Appointment
  #key-fields: doctor_id, chair_id, time_slot_id, patient_id
  belongs_to :doctor
  belongs_to :chair
  belongs_to :time_slot  
  belongs_to :patient
RKelley
  • 576
  • 6
  • 16
abcm1989
  • 81
  • 6
  • Thank you first of all. Question: So would each TimeSlot track date, start_time, end_time or would appointment? I want to be able to only run the availability validation when someone tries to make an appointment with the same time_slots as another already made appointment. Also, would it be easier to add work_days as well to help accomplish this? – RKelley Aug 30 '14 at 05:44
  • I would put start_time and end_time in my TimeSlot migration. I'll edit the above so that the availability function only runs if the time slot has already been booked once. I don't think you need to add work days, you can validate the day of the week. – abcm1989 Aug 30 '14 at 15:30
  • This is great information, I really appreciate it. I'm going to make these changes and see how it plays out. But from first examination, it all makes perfect sense. Thank you abcm1989! – RKelley Aug 31 '14 at 07:43
  • Here is the updated schema. I added a hygienists table, but that's really it. Thank you again. http://i.imgur.com/fX9CGNh.png – RKelley Sep 01 '14 at 18:36
  • Okay, so after testing it out, how can I validate chairs on Timeslots if it's TimeSlots have just one chair? It's a many to many relationship with appointments as the join table. – RKelley Sep 06 '14 at 23:00
  • That's no longer the most up-to-date schema. I now added a "bookings" table which is a many-to-many join on "appointments" and "time_slots". Each time_slot can have many appointments, while each appointment can have many time_slots. Essentially, an appointment is turned into an appointment request, and the booking is the confirmed appointment... – RKelley Aug 21 '15 at 22:07