重复“事件”在日历中:CPU与数据库

7
我正在从头开始构建一个日历系统(要求如此,因为我正在使用一种特殊类型的日历与公历一起工作),需要一些逻辑方面的帮助。我正在使用Django和Python编写应用程序。
基本上,我遇到的逻辑问题是如何尽可能聪明地持久化尽可能少的对象,而不会在CPU周期上花费太多。我感觉多态性可能是解决这个问题的方法,但我不确定如何在这里表达它。
我有两个基本子集事件,重复事件和一次性事件。
重复事件将有订阅者,即被通知其更改的人。例如,如果班级被取消或移至不同的地址或时间,则已订阅的人需要知道此事。有些事件每天都会发生,直到永远不会被编辑,只是“发生”。问题是,如果我只有一个存储事件信息及其重复策略的对象,那么取消或修改系列中的一个事件会导致混乱,我必须以某种方式解决这个问题,使订阅者知道更改并将系列保持为逻辑组。
Paradox:对于一个重复事件,生成每个普通事件的唯一对象直到时间结束(如果它无限重复)是没有意义的,因为它们都将存储相同的信息;然而,如果系列中发生任何更改,我几乎必须创建一个不同的对象在数据库中表示取消。
有人能帮我理清这里的逻辑吗?这真的让我困惑,我不能很清晰地思考。我真的需要一些关于如何解决这个问题的意见,因为重复事件并不是最容易解决的逻辑问题(每隔一天重复,或每周一/三/五,或每月的第一个星期一,或每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',然后为所有过去的实例生成异常实例,将它们设置为原始时间,但啊啊啊啊啊啊!!!)
归纳为三个简单、具体的问题,我们有:
  1. 如何设置一系列重复事件,同时允许取消和修改单个事件以及整个系列的修改,尽可能少地在数据库中存储对象,不预先生成单个事件的对象?
  2. 如何以高度可定制的方式重复事件,而不会让自己失去理智,我可以允许事件以多种方式重复,但仍然使事情变得容易,并尽可能少地存储对象?
  3. 如何做到以上所有这些,允许在每个事件系列上切换以使其在节假日期间不发生?

6
对于这个很少见但非常有用的类models.IHaveNoIdeaInTheWorldOnHowToRepresentThisField,给它一个+1。 - mjhm
1
我经常使用那个字段。 - Naftuli Kay
4个回答

3
这可能会引起激烈的讨论,因为日期逻辑通常比看起来要难得多,每个人都有自己的想法如何使事情发生。我可能会牺牲一些db空间,并尽可能使模型变得愚蠢(例如,不必定义系列的异常)。重复条件可以是一些简单的术语,需要解析(取决于您的要求),或者- KISS-只是下一个事件发生的时间间隔。从此,您可以生成“下一个”事件,它将复制重复条件,并且您可以在未来生成尽可能多的事件(为其定义一些最大时间窗口以生成事件,但仅在实际查看时间间隔时才生成事件)。事件可以指回其父事件,因此整个系列是可识别的(就像链接列表一样)。该模型应具有指示器,表明单个事件是否已取消。(事件仍然存在于db中,以便能够将事件复制到未来)。取消整个系列会删除事件列表。

编辑:其他答案提到了dateutil包,用于间隔构建和解析,看起来非常不错。


对于日期处理,dateutil 很好用。rrulerruleset 看起来在这里也很有用。 - sandinmyjoints

1
我创建了一个事件系列模型,对于 IHaveNoIdeaInTheWorldOnHowToRepresentThisField 的解决方案是使用 pickled object field 来保存 dateutil 中的重复规则 (rrule) 到我的事件系列模型中。

1

我只想回答第三个问题,有关节假日的问题。

在几个报告数据库中,我发现定义一个表格很方便,让我们称之为“历法”,其中包含一定范围内每个日期的一行。如果该范围跨越十年,则表将包含约3,652行。按今天的标准来看,这很小。主键是日期。

其他一些列是诸如日期是否为假期、正常工作日或周末日等内容。我知道,您可以使用内置函数计算周末等信息。但事实证明,将此类信息作为数据包括在内非常方便。它使您的连接更简单,更相似。

然后,您有一个应用程序,用于填充Almanac。其中包含所有的日历怪癖,包括确定哪些日期是假期的企业规则。如果这对您的情况有意义,您甚至可以包括用于确定给定日期属于哪个“财务月”的列。整个应用程序,包括输入程序和提取程序,都像普通数据一样处理Almanac。

这种设计可能看起来不够简洁,但请相信我,这种设计模式在各种情况下都非常有用。你需要自己想办法将其应用到你的情况中。

《年鉴》实际上是数据仓库和星型模式设计原则的一个子集。

如果你想在 CPU 内部做同样的事情,你可以创建一个名为“年鉴”的对象,并公开一些特性,例如 Almanac.holiday(date)。


对于这个使用案例,这非常有意义。由于我计算的假期使用了相当复杂的逻辑(我们不是在处理公历日历),所以在这种情况下这可能是一个好主意。由于这些类型的对象不会被编辑并且可以被硬编码缓存,所以这是有意义的。我可能会创建两个表,一个用于将我的日历中的日期映射到公历日历中的日期,另一个用于将假期映射到我的日历日期,然后我会找到一种方法来合并数据并变得更加智能化。谢谢。 - Naftuli Kay
是的。你的年鉴中的一列可能是以你的日历方式表示的等效日期。然后,你可以使用普通的公历日期(就像数据库管理系统理解的那样)记录事件的日期等,并将其与年鉴连接起来,以便向数据用户展示在你的日历中的日期。 - Walter Mitty

0

我曾经和你一样遇到了完全相同的问题。然而,最初的解决方案没有包含任何异常或取消,只有重复事件。我们建模一组重复事件的方式是,有一个字段指示间隔类型(如每月/每周/每日),然后从给定的起始日期开始计算距离(如每2天、每2周等)。这种简单的重复方式并不能涵盖太多情况,但计算重复日期非常容易。还有其他的重复方式,例如类似于cronjobs的定义。

为了生成重复事件,我们创建了一个表函数,根据某个用户ID,在未来5年内使用递归SQL“即时”生成所有事件重复项(因此,对于一组重复项,只需存储一个事件)。到目前为止,这种方法运行得非常好,表函数可以像实际存储在数据库中的单个重复项一样查询。它也可以很容易地扩展,以排除任何取消的事件,并基于日期替换更改的事件,也是即时的。我不知道这是否适用于您的数据库和ORM。


这对我的使用情况来说不太可能,因为我确实需要尽可能少地持久化对象。如果我能想出一个只涉及几个对象的解决方案,那么持久化这么多对象就没有意义了。我越想,我写的数据模型(上面那个)似乎越好,但我仍然需要想出一种灵活处理复杂重复的方法,所以我认为我需要另一个模型来表示变量重复。 - Naftuli Kay
什么?你说的“probable”是什么意思?我不明白你怎么可能比这更不持久,也许我没有解释清楚。 - Janick Bernet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接