开发者

Repeating "events" in a calendar: CPU vs Database

开发者 https://www.devze.com 2023-04-02 22:52 出处:网络
I\'m building a calendar system from the ground up (requirement, as I\'m working with a special type of calendar alongside Gregorian) , and I need some help with logic. I\'m writing the application in

I'm building a calendar system from the ground up (requirement, as I'm working with a special type of calendar alongside Gregorian) , and I need some help with logic. I'm writing the application in Django and Python.

Essentially, the logical issues I'm running into is how to persist as few objects as possible as smartly as possible without running up the tab on CPU cycles. I'm feeling that polymorphism would be a solution to this, but I'm not exactly sure how to express it here.

I have two basic subsets of events, repeating events and one-shot events.

Repeating events will have subscribers, people which are notified about their changes. If, for example, a class is canceled or moved to a different address or time, people who have subscribed need to know about this. Some events simply happen every day until the end of time, won't be edited, and "just happen." The problem is that if I have one object that stores the event info and its repeating policy, then canceling or modifying one event in the series really screws things up and I'll have to account for that somehow, keeping subscribers aware of the change and keeping the series together as a logical group.

Paradox: generating unique event objects for each normal event in a series until the end of time (if it repeats indefinitely) doesn't make sense if they're all going to store the same information; however, if any change happens to a single ev开发者_开发百科ent in the series, I'll almost have to create a different object in the database to represent a cancellation.

Can someone help me with the logic here? It's really twisting my mind and I can't really think straight anymore. I'd really like some input on how to solve this issue, as repeating events isn't exactly the easiest logical thing either (repeat every other day, or every M/W/F, or on the 1st M of each month, or every 3 months, or once a year on this date, or once a week on this date, or once a month on this date, or at 9:00 am on Tuesdays and 11:00am on Thursdays, etc.) and I'd like help understanding the best route of logic for repeating events as well.

Here's a thought on how to do it:

class EventSeries(models.Model):
    series_name = models.TextField()
    series_description = models.TextField()
    series_repeat_policy = models.IHaveNoIdeaInTheWorldOnHowToRepresentThisField()
    series_default_time = models.TimeField()
    series_start_date = models.DateField()
    series_end_date = models.DateField()
    location = models.ForeignKey('Location')

class EventSeriesAnomaly(models.Model):
    event_series = models.ForeignKey('EventSeries', related_name="exceptions")
    override_name = models.TextField()
    override_description = models.TextField()
    override_time = models.TimeField()
    override_location = models.ForeignKey('Location')
    event_date = models.DateField()

class EventSeriesCancellation(models.Model):
    event_series = models.ForeignKey('EventSeries', related_name="cancellations")
    event_date = models.TimeField()
    cancellation_explanation = models.TextField()

This seems to make a bit of sense, but as stated above, this is ruining my brain right now so anything seems like it would work. (Another problem and question, if someone wants to modify all remaining events in the series, what in the heck do I do!?!? I suppose that I could change 'series_default_time' and then generate anomaly instances for all past instances to set them to the original time, but AHHHHHH!!!)

Boiling it down to three simple, concrete questions, we have:

  1. How can I have a series of repeating events, yet allow for cancellations and modifications on individual events and modifications on the rest of the series as a whole, while storing as few objects in the database as absolutely necessary, never generating objects for individual events in advance?
  2. How can I repeat events in a highly customizable way, without losing my mind, in that I can allow events to repeat in a number of ways, but again making things easy and storing as few objects as possible?
  3. How can I do all of the above, allowing for a switch on each event series to make it not happen if it falls out on a holiday?


This could become a heated discussion, as date logic usually is much harder than it first looks and everyone will have her own idea how to make things happen.

I would probably sacrifice some db space and have the models be as dumb as possible (e.g by not having to define anomalies to a series). The repeat condition could either be some simple terms which would have to be parsed (depending on your requirements) or - KISS - just the interval the next event occurs.

From this you can generate the "next" event, which will copy the repeat condition, and you generate as much events into the future as practically necessary (define some max time window into the future for which to generate events but generate them only, when somebody in fact looks at the time intervall in question). The events could have a pointer back to its parent event, so a whole series is identifiable (just like a linked list).

The model should have an indicator, whether a single event is cancelled. (The event remains in the db, to be able to copy the event into the future). Cancelling a whole series deletes the list of events.

EDIT: other answers have mentioned the dateutil package for interval building and parsing, which really looks very nice.


I want to address only question 3, about holidays.

In several reporting databases, I have found it handy to define a table, let's call it "Almanac", that has one row for each date, within a certain range. If the range spans ten years, the table will contain about 3,652 rows. That's small by today's standards. The primary key is the date.

Some other columns are things like whether the date is a holiday, a normal working day, or a weekend day. I know, I know, you could compute the weekend stuff by using a built in function. But it turns out to be convenient to include this stuff as data. It makes your joins simpler and more similar to each other.

Then you have one application program that populates the Almanac. It has all the calendar quirks built into it, including the enterprise rules for figuring out which days are holidays. You can even include columns for which "fiscal month" a given date belongs to, if that's relevant to your case. The rest of the application, both entry programs and extraction programs, all treat the Almanac like plain old data.

This may seem suboptimal because it's not minimal. But trust me, this design pattern is useful in a wide variety of situations. It's up to you to figure how it applies to your case.

The Almanac is really a subset of the principles of data warehousing and star schema design.

if you want to do the same thing inside the CPU, you could have an "Almanac" object with public features such as Almanac.holiday(date).


For an event series model I created, my solution to IHaveNoIdeaInTheWorldOnHowToRepresentThisField was to use pickled object field to save a recurrence rule (rrule) from dateutil in my event series model.


I faced the exact same problems as you a while ago. However, the initial solution did not contain any anomalies or cancellations, just repeating events. The way we modeled a set of repeating events, is to have some field indicating the interval type (like monthly/weekly/daily) and then the distances (like every 2nd day, ever 2nd week, etc.) starting from a given starting day. This simple way for repetition does not cover too many scenarios, but it was very easy to calculate the repeating dates. Other ways of repetition are also possible, for instance something in the way cronjobs are defined.

To generate the repetitions, we created a table function, that given some userid generated all the event repetitions on the fly up to like 5 years into the future on the fly using recursive SQL (so as in your approach, for a set of repetitions, only one event has to be stored). This so far works very well and the table function can be queried as if the individual repetitions were actually stored in the database. It also could be easily extended to exclude any cancelled events and to replaced changed events based on dates, also on the fly. I do not know if this is possible with your database and your ORM.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号