我正在从头开始构建一个日历系统(要求,因为我正在使用一种特殊类型的日历和公历),并且我需要一些逻辑方面的帮助。我正在用 Django 和 Python 编写应用程序。
本质上,我遇到的逻辑问题是如何在不占用 CPU 周期的情况下尽可能智能地保留尽可能少的对象。我觉得多态性将是一个解决方案,但我不太确定如何在这里表达它。
我有两个基本的事件子集:重复事件和一次性事件。
重复事件将会有订阅者,人们会收到有关其更改的通知。例如,如果课程被取消或转移到不同的地址或时间,订阅者需要知道这一点。有些事件每天都会发生,直到时间结束,不会被编辑,并且“只是发生”。问题是,如果我有一个对象来存储事件信息及其重复策略,那么取消或修改系列中的一个事件确实会把事情搞砸,我必须以某种方式对此进行解释,让订阅者了解更改并将该系列作为一个逻辑组保持在一起。
悖论:为一系列正常事件生成唯一的事件对象,直到时间结束(如果无限重复),如果它们都存储相同的信息,则没有意义;但是,如果该系列中的单个事件发生任何更改,我几乎必须在数据库中创建一个不同的对象来表示取消。
有人可以帮我解决这里的逻辑吗?这真的扭曲了我的思维,我无法再正常思考了。我真的很想知道如何解决这个问题,因为重复事件也不是最简单的逻辑事情(每隔一天重复一次,或者每个月/周/月重复一次,或者每个月的第一个 M,或者每 3 个月一次,或每年此日期一次,或每周此日期一次,或每月此日期一次,或周二上午 9:00 和周四上午 11:00 等),我会喜欢帮助理解也是重复事件的最佳逻辑路线。
这是关于如何做到这一点的想法:
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()
这似乎有点道理,但如上所述,这现在正在毁掉我的大脑,所以任何事情似乎都会起作用。 (另一个问题和疑问,如果有人想修改系列中的所有剩余事件,我到底该怎么做!?!?我想我可以更改“series_default_time”,然后为所有过去的实例生成异常实例来设置它们到原来的时间,但是啊啊啊!!!)
将其归结为三个简单、具体的问题,我们有:
- 如何才能拥有一系列重复事件,同时允许取消和修改单个事件和对整个系列的其余部分进行修改,同时在数据库中存储绝对必要的尽可能少的对象,从不提前为各个事件生成对象?
- 如何以高度可定制的方式重复事件而不失去理智,因为我可以允许事件以多种方式重复,但再次让事情变得简单并存储尽可能少的对象?
- 我怎样才能完成上述所有操作,允许在每个事件系列上进行切换,以使其在假期发生时不会发生?
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 event 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:
- 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?
- 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?
- 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?
发布评论
评论(4)
这可能会成为一场激烈的讨论,因为日期逻辑通常比乍看起来要困难得多,而且每个人都有自己的想法如何让事情发生。
我可能会牺牲一些数据库空间并让模型尽可能愚蠢(例如,不必为系列定义异常)。重复条件可以是一些必须解析的简单术语(取决于您的要求),也可以是 - KISS - 只是下一个事件发生的时间间隔。
由此,您可以生成“下一个”事件,该事件将复制重复条件,并且您可以根据实际需要生成尽可能多的事件(定义将来生成事件的最大时间窗口,但仅在以下情况下生成事件)事实上有人会查看所讨论的时间间隔)。这些事件可以有一个指向其父事件的指针,因此整个系列都是可识别的(就像链接列表一样)。
模型应该有一个指示器,指示单个事件是否被取消。 (事件保留在数据库中,以便能够将事件复制到未来)。取消整个系列会删除事件列表。
编辑:其他答案 提到了用于间隔构建的dateutil 包解析,看起来确实非常好。
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.
我只想解决问题 3,关于假期。
在几个报告数据库中,我发现定义一个表很方便,我们称之为“年鉴”,该表在一定范围内每个日期都有一行。如果范围跨越十年,该表将包含大约 3,652 行。以今天的标准来看,这个数字很小。主键是日期。
其他一些列例如日期是假期、正常工作日还是周末。我知道,我知道,您可以使用内置函数来计算周末的事情。但事实证明,将这些东西作为数据包含进来很方便。它使您的连接更加简单并且彼此更加相似。
然后您就有了一个填充年历的应用程序。它内置了所有日历怪癖,包括确定哪些日子是假期的企业规则。如果与您的情况相关,您甚至可以包含给定日期所属的“会计月份”列。应用程序的其余部分,无论是输入程序还是提取程序,都将年历视为普通的旧数据。
这可能看起来不是最理想的,因为它不是最小的。但请相信我,这种设计模式在多种情况下都很有用。由您决定它如何适用于您的案例。
年鉴实际上是数据仓库和星型模式设计原则的子集。
如果你想在CPU内部做同样的事情,你可以有一个具有公共功能的“Almanac”对象,例如Almanac.holiday(date)。
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).
对于我创建的事件系列模型,我的
IHaveNoIdeaInTheWorldOnHowToRepresentThisField
解决方案是使用 pickled 对象字段< /a> 从 rrule) rel="nofollow">dateutil
在我的事件系列模型中。For an event series model I created, my solution to
IHaveNoIdeaInTheWorldOnHowToRepresentThisField
was to use pickled object field to save a recurrence rule (rrule
) fromdateutil
in my event series model.不久前我遇到了和你完全相同的问题。然而,最初的解决方案不包含任何异常或取消,只是重复事件。我们对一组重复事件进行建模的方式是使用一些字段来指示间隔类型(例如每月/每周/每天),然后指示从给定起始日开始的距离(例如每两天、每两周等) 。这种简单的重复方式并没有涵盖太多场景,但是很容易计算出重复日期。其他重复方式也是可能的,例如定义cronjobs的方式。
为了生成重复,我们创建了一个表函数,给定某个用户 ID,该函数使用递归 SQL 动态生成 5 年后的所有事件重复(就像在您的方法中一样) ,对于一组重复,只需存储一个事件)。到目前为止,这工作得很好,并且可以查询表函数,就好像各个重复项实际上存储在数据库中一样。它还可以轻松扩展以排除任何已取消的事件,并根据日期替换更改的事件,也可以即时进行。我不知道你的数据库和 ORM 是否可以实现这一点。
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.