Recurring Events with Existing Associations - ruby-on-rails

I have an existing live application with an Event model that looks like the following:
class Event < ActiveRecord::Base
has_many :tickets
has_many :orders
has_many :products, through: :orders
has_many :rsvps, through: :orders
end
My problem is that I now need to add in the functionality that will let a user specify that the event needs to be recurring (for which they'll set a schedule). There are a couple of approaches I've thought of as follows:
Have a schedule and parent_event property on the Event model, for which I will trigger a background job after the event is created to create duplicate events in the DB with the parent_event pointing to the main event so that I can aggregate statistics on the main event (like total tickets bought, etc...). This is the easiest route as all of the models references (tickets, rsvps, orders) don't need to be altered.
Create a Schedule model that is a habtm relationship with events, and make tickets, rsvps, etc... belong to that instead. So I'd be creating one event with many Schedules attached to it for recurring events.
Option 1 seems really dirty, especially if users want to delete an event (I'd have to delete all of the duplicates then as well), but it would require the least amount of code-changes. Option 2 seems like it could be the most scalable approach, but I feel like I may be overcomplicating this. Any advice / examples would be appreciated.

I think the first option is the way to go. I have done some tests with Google Calendar and I think their model is similar to the one you are proposing. You can read Google Calendar documentation (I provide some links below). As far as I could read:
A recurring event has many instances
Instances are events
Instances have a link to their parent event (recurringEventId)
When you modify or delete a recurring event, you are asked if you want to modify or delete only this instance, this an the following instances or all instances.
Recurring events have information about the recurrence (as specified in RFC5545). Recurrence information is including in the parent event (it is omitted for single events or instances of recurring events).
An event is an object associated with a specific date or time range. Events are identified by an ID that is unique within a calendar. Besides a start and end date-time, events contain other data such as summary, description, location, status, reminders, attachments, etc.
Types of events: Google Calendar supports single and recurring events:
A single event represents a unique occurrence.
A recurring event defines multiple occurrences.
Instances
A recurring event consists of several instances: its particular occurrences at different times. These instances act as events themselves.
Recurring event modifications can either affect the whole recurring event (and all of its instances), or only individual instances. Instances that differ from their parent recurring event are called exceptions. For example, an attendee can be invited to just one instance of a recurring event.
Official information about Google Calendar:
https://developers.google.com/google-apps/calendar/concepts/events-calendars
https://developers.google.com/google-apps/calendar/v3/reference/events

Related

What is your data model for recurrent event?

For instance something like Google Calendar, where we can:
Add simple event
Add recurring event
every week
every monday/tuesday
etc.
Delete an instance of a recurring event
Change all following recurrent event
etc.
I have an events with the primary event "Event#1" for instance and its instances "Event#2" and "Event#3" that have an event_id foreign key to the primary event, e.g. 1
What do you think? Should I use the iCal spec somehow?
Two key things:
Split the event from its schedule.
Split the recurring schedule definition from its individual schedule.
By splitting the event from its schedule, instances of a recurring event can share the same event information like its description and location, while others can use an edited version.
events:
id
name
description
location
...
By splitting the recurring schedule from its individual schedule makes querying for events much simpler.
recurring_event_schedules:
id
event_id
schedule: jsonb
start_at: timestamp with time zone
This is the recurring schedule for an event.
Recurring schedules get complicated. You could have multiple tables for each type of recurring period (annual, biannual, daily, monthly, weekly, etc...) or punt and use jsonb to store the spec. The spec might be { "period": "weekly", "days": [1,2,3,4,5] } for every weekday. Reference the iCal spec and good calendaring software for what sorts of repeat events you'd want and other attributes.
jsonb is not a panacea, but this table is only for reference to generate and update events. It is not used for runtime querying. For that there's a second table.
Then generate individual scheduled events from this, out to a configurable limit.
event_schedules:
id
event_id
recurrent_event_schedule_id
schedule: tztsrange
This table is simple. What event, when, and what recurring schedule it was generated from.
Since you're using Postgres, rather than separate start/end fields, use a timestamp range. These are supported by special operators to determine if a time is inside a range, or if ranges overlap, and so on.
Rails will accept Range types, but it does not directly support the operators. For example, EventSchedule.where("duration #> ?::date", Date.new(2014, 2, 12)) finds all events happening on 2014-02-12.
If you want to edit an individual scheduled event, copy its Event, edit it, and link the EventSchedule to that new Event.
If you want to change the recurring event schedule, select the event_schedules by recurrent_event_schedule_id.
If you want to change the event for all scheduled events, edit the Event linked to the RecurringEventSchedule.
In this way you can define complex recurring events and query the simple concrete events.

Rails database relationships has_many :through but for many to many relationships

I am trying to create my first database in rails that allows me to store data about events.
Assume I have information about the event, the venue(s), and the band(s).
Every event should have at least one venue and band but could possibly have more than one of each.
Each band can be at more than one event.
Each venue can host more than one event.
It seems like "has_many :through" using the "Event" as the ":through" would be the appropriate relationship IF there were only one band/venue pair per event.
So what can I do if there can be multiple bands and multiple venues per event?
Thanks in advance.
You will have to create tables events_venues and events_bands and use has_and_belongs_to_many to build the relationship. For more details, please read the railscasts tutorial here:
Many to many

Recurring Events in Calendar - Rails

I am searching for the best way to model recurring events. I am using fullcalendar to display events. But I guess recurring events are best handled on the rails backend.
I already looked at other questions and existing example code but I didn't find anything which fits.
It should behave similar like google calendar. So it should be possible to delete/modify single events of the recurring event series. But saving all events of the event series in the database seems inefficient. Also it should be possible to create single events without any recurrence.
What would be a good model architecture?
My event model right now looks like that (without additional attributes):
# Table name: events
#
# id :integer not null, primary key
# employee_id :integer
# created_at :datetime
# updated_at :datetime
# starts_at :datetime
# ends_at :datetime
#
class Event < ActiveRecord::Base
attr_accessible :starts_at, :ends_at
end
Here is how I would model this. I haven't used Google Calendar much, so I'm basing the functionality off of iCal's recurring events.
All models should have the usual id, created_at, updated_at properties. Listed are the custom properties. If the property is another model, you will implement it an association such as has_one or belongs_to.
RecurrencePeriod
Event base_event # has_one :base_event, :class_name'Event'
Time end_date # may be nil, if it recurs forever
WeeklyRecurrence recurrence # has_one :recurrence, :as=>:recurrence
Array[OccurrenceOverride] overrides # has_many :overrides, :class_name=>'OccurrenceOverride'
The RecurrencePeriod starts on the date that its base_event starts. Also, I assume that an Event's employee_id refers to the employee that created that event. A RecurrencePeriod will also belong to the employee that created the base_event.
The model depends on how flexibly you want to be able to specify recurrences. Are you going to support "Tuesday and Thursday every two weeks from 10 AM to 11 AM and from 2 PM to 3 PM" or just "repeats weekly"? Here's a model that supports just "repeats weekly", "repeats every two weeks", etc.; you can expand it if you need to.
WeeklyRecurrence
Integer weeks_between_recurrences
RecurrencePeriod recurrence_period # belongs_to :recurrence, :polymorphic=>true
I use polymorphic associations here, because I think they might be useful if you want more than one type of recurrence, such both WeeklyRecurrence and DailyRecurrence. But I'm not sure that they're the correct way to model that, so if they turn out not to be, just use has_one :weekly_recurrence and belongs_to :recurrence_period instead.
The Ice cube library seems like it might be useful for calculating recurrences. If WeeklyRecurrence above isn't powerful enough, you might just want to store an Ice cube Schedule object in a model, replacing WeeklyRecurrence. To store a Schedule object in a model, save it as an attribute "schedule", put serialize :schedule in the model definition, and generate a text column "schedule" in the database.
OccurrenceOverride handles the case of a single instance of a recurring event being edited.
OccurrenceOverride
RecurrencePeriod recurrence_period_to_override # belongs_to :recurrence_period_to_override, :class_name=>'RecurrencePeriod'
Time original_start_time # uniquely identifies which recurrence within that RecurrencePeriod to replace
Event replacement_event # has_one :replacement_event, :class_name=>'Event'; may be nil, if that recurrence was deleted instead of edited
Instead of storing each occurrence of an event individually, generate them temporarily when you need to show them in the view. In RecurrencePeriod, create a method generate_events_in_range(start_date, end_date) that generates Events, not to save in the database, but just to pass to the view so it can show them.
When a user edits a recurrence, they should have the option to modify all occurrences, all future occurrences, or just that event. If they modify all occurrences, modify the RecurrencePeriod's base_event. If they modify all future occurrences, use a method you should implement on RecurrencePeriod that splits itself into two RecurrencePeriods on either side of a certain date, and then save the changes to just the second period. If they modify only that event, create an OccurrenceOverride for the time that they are overriding, and save the changes to the override's replacement_event.
When a user says a certain event should now recur every two weeks for the foreseeable future, you should create a new RecurrencePeriod with that event as base_event and a nil end_date. Its recurrence should be a new WeeklyRecurrence with weeks_between_recurrence=2, and it should have no OccurrenceOverrides.
In my case I did something like this :
# Holds most of my event's data; name, description, price ...
class Event < ActiveRecord::Base
has_many :schedules
has_many :occurrences
attr_accessible :started_at, :expired_at # expired_at is optional
end
# Holds my schedule object
class Schedule < ActiveRecord::Base
belongs_to :event
attr_accessible :ice_cube_rule # which returns my deserialized ice_cube object
end
# Holds generated or manually created event occurrences
class Occurrence < ActiveRecord::Base
belongs_to :event
attr_accessible :started_at, :expired_at
attr_accessible :generated # helps me tell which occurrences are out of an ice_cube generated serie
attr_accessible :canceled_at
end
From there, I used ice_cube to manage the occurrences calculation and stored the results in the occurrences table. I first tried to work without the Occurrence model, but no matter how advanced the rule engine, you'll always have exceptions, so storing the occurrences in their own model gives you flexibility.
Having an Occurrence model makes it a lot easier to display the events on a calendar or with date search filters as you just need to query for occurrences and then display the related event's data instead of gathering all the events in a given date range and then having to filter out the events where the schedule(s) don't match.
Also you can flag an event occurrence as canceled or modify it (setting the generated attribute at false so it does not get cleaned up when editing an ice_cube schedule... or whatever your business need is)
Of course if you have events that repeat indefinitely, you'll want to limit how far in the future you want those occurrences to be generated and use automated rake tasks to clean up the old ones and generate occurrences for the next year or so.
So far this pattern works pretty well for me.
Also, take a look at the recurring_select gem which is a pretty neat ice_cube form input.
Just an opinion off the top of my head, perhaps comenters will point out a problem I'm not thinking of at the moment:
I would make a RecurringEvent model (or whatever you want to call it) that has_many :events.
Let's say each event is created by an employee (based on your notes), then RecurringEvent would also belong_to :employee. You could then build a has_many :through relationship where an employee has many events and has many recurring events.
The RecurringEvent model could have a start date and a pattern, and it could initially use this pattern to create the individual occuring events. Then on any event that is part of the recurring series you could modify or delete that individual occurance, but you could also 'regenerate the series', deleting all events in the series (or all future events in the series) and rebuilding them based on a new pattern, so for instance move the meeting from "every tuesday" to "every thursday".
One other kind of nice thing about this is you could create an at-a-glance list of recurring events, which might give you some nice insight into people's major obligations.
Like I said, off the top of my head that's how I would approach it, but this is just an idea and I haven't built anything like that so I don't know if there are any big gotchas in the approach I'm suggesting.
Good luck, please post what you end up doing!
I m quite new to Rails, your solution sounds interesting. To create the schedule and associated occurences, do you use conditionnal callbacks in Event model?
In my case, users would be able to create events, weekly recurring or not. So I was thinking about a recurring boolean field in event model. So I guess you would have a first callback to create the schedule:
before_save :create_weekly_schedule, if: :recurring
and basically a second one to create the occurences:
after_save :create_occurences_if_recurring
def create_occurences_if_recurring
schedules.each do |sched|
occurences.create(start_date: sched.start_time, end_date: sched.end_time)
end
end
Does this sound logical with your solution?
Thx

Rails 3: Automatically create entries in table using preset values

Currently, I'm building a scheduling system for my workplace. It's a highly-trafficked university tour guide office, and given a schedule of the day's events and the shifts of all available tour guides, the system will assign tours to available tour guides.
But, when asking a user to build the event schedule (or list of tours) for the day, I'd prefer to have a schedule auto-generated that they can then manipulate. There are only two different types of schedules that alternate depending on the day of the week, and this head start will save a lot of time.
My question: Where do I put this 'seed' data? Should I create a YAML and then have a method read it to populate a given day with appropriate defaults?
For reference, my models are currently structured like so:
guide has_many :tours, :shifts
shifts belongs_to :guide
tour belongs_to :guide
I've considered a 'day' model but as every time is a datetime, I felt like this was too redundant to persist to the database.
Thanks! All suggestions welcome. I'm just trying to get a good grip on the proper "Rails Way" to handle this.
You should use a factory to initialize your objects. Have a look at https://github.com/thoughtbot/factory_girl

Creating a Calendar/Planner Application - Ruby on Rails

I am considering developing an application using Ruby on Rails that is a planner of sorts. I would like to give a user the ability to see a list of days, click a particular day, and then add things like: Meals, Expenses, Events, Todos, and Exercises. Really I am doing this for me and my growing family.
I am curious with how best to implement this. I can certainly see that Meals, Expenses, etc. need to belong_to :user but I am curious how to implement the belongs_to :day or something like that. Using the created_at or updated_at wouldn't necessarily allow me to provide views for future dates.
I can see how if I created a Days table and then added days through a time and date field that this would work but seems a little strange to ask people to create the actual days.
Or perhaps instead of that I could just create links to variables that search for #today, #tomorrow, but that would get messy.
I have browsed for gems/plugins but can't find one that works. Ideally a person would be able.
Anyone have any thoughts on how to implement something like this?
There are a number of existing Rails calendars, such as http://github.com/elevation/event_calendar or http://github.com/topfunky/calendar%5Fhelper.
However, to answer your specific question about dates: I don't think there's any need to have Day as a model; simply give each event a start date and time, and an end date and time. Remember that Ruby makes it easy to search based on ranges, so "give me all of the events next week" is a cinch if each event has dates and times associated with it.
I'll give it a shot...
Two tables; users and events. A user has many events and an event belongs to a user. Meal, Expenses, etc. are various types of Event. Within events, you can have fields for start and end time of the events. If needed (lets say an events last over multiple days), you could remove the day/time when events occurs into it's own table.
This way, when displaying the calendar for a user, you can find all the events for that date. If none are found, then display nothing.
What do you think?
I would add a model called "Events" and have a properties of the model to represent start date/time, end date/time. I do not think you need a Days model, you can generate your calendar view from the Date class built into ruby.
I have done same kind of project for the Event management in training institute. At there I used event_calender plug in with rails. (enter link description here)
In there we just need to create Event model only. Then we can easily work with that.

Resources