lista iCalendar “Campo” (para o esquema de banco de dados com base no padrão iCalendar)

StackOverflow https://stackoverflow.com/questions/1054201

  •  20-08-2019
  •  | 
  •  

Pergunta

minha candidatura tem que lidar com informações de calendário (incl. Ocorrência única, recorrência, etc.). A fim de facilmente interface com outras aplicações que eu pensei que seria uma boa idéia para criar o meu esquema de banco de dados baseado no formato iCalendar (campos, relacionamentos, restrições) diretamente para que eu obter objetos compatíveis com iCalendar via ORM que eu posso facilmente expor quando necessário.

Eu sei que o RFC está disponível, mas é meio complicado por causa de toda a informação adicional no que eu não uso no momento.

Alguém poderia me aponte para uma fonte mais fácil de criar um esquema de banco de dados baseado no padrão iCal (ou seja, uma lista de campos / fieldnames e sua relação para entradas iCal)?

Obrigado!

Foi útil?

Solução

Eu fiz isso (para VEvents apenas, não suportam itens TODO ou entires Journal ou qualquer coisa assim). Minha implementação é assim (depois de remover as colunas que não são específicos para a pergunta):

-- One table for each event.  An event may have multiple rRules.
Create Table [vEvent]
    (vEventID Integer Identity(1, 1) Not Null
     Constraint [vEvent.pk]
     Primary Key
     Clustered
    ,title nVarChar(200) Not Null);

-- One table for rRules.
-- My application does NOT support the "bySetPos" rule, so that is not included.
Create Table [rRule]
    (rRuleID Integer Identity(1, 1) Not Null
     Constraint [rRule.pk]
     Primary Key
     Clustered
    ,vEventID Integer Not Null
     Constraint [fk.vEvent.rRules]
     Foreign Key
     References [vEvent] (vEventID)
     On Update Cascade
     On Delete Cascade
    ,[class]            varChar(  12) Not Null Default('public')
    ,[created]         DateTime       Not Null Default(getUTCDate())
    ,[description]     nVarChar(max)      Null
    ,[dtStart]         DateTime       Not Null
    ,[dtEnd]           DateTime           Null
    ,[duration]         varChar(  20)     Null
    ,[geoLat]          Float              Null
    ,[geoLng]          Float              Null
    ,[lastModified]    DateTime       Not Null Default(getUTCDate())
    ,[location]        nVarChar(max)      Null
    ,[organizerCN]     nVarChar(  50)     Null
    ,[organizerMailTo] nVarChar( 100)     Null
    ,[seq]             Integer        Not Null Default(0)
    ,[status]           varChar(   9) Not Null Default('confirmed')
    ,[summary]         nVarChar(  75)     Null
    ,[transparent]     Bit            Not Null Default(0)
    ,[freq]             varChar(   8) Not Null Default('daily')
    ,[until]           DateTime           Null
    ,[count]           Integer            Null
    ,[interval]        Integer        Not Null Default(1)
    ,[bySecond]         varChar( 170)     Null
    ,[byMinute]         varChar( 170)     Null
    ,[byHour]           varChar(  61)     Null
    ,[byDay]            varChar(  35)     Null
    ,[byMonthDay]       varChar( 200)     Null
    ,[byYearDay]        varChar(3078)     Null
    ,[byWeekNo]         varChar( 353)     Null
    ,[byMonth]          varChar(  29)     Null
    ,[wkSt]             Char   (   2)     Null Default('mo'));

-- Class must be one of "Confidential", "Private", or "Public"
Alter Table [rRule]
Add Constraint [rRule.ck.Class]
Check ([class] In ('confidential', 'private', 'public'));

-- Start date must come before End date
Alter Table [rRule]
Add Constraint [rRule.ck.dtStart]
Check ([dtEnd] Is Null Or [dtStart] <= [dtEnd]);

-- dtEnd and duration may not both be present
Alter Table [rRule]
Add Constraint [rRule.ck.duration]
Check (Not ([dtEnd] Is Not Null And [duration] Is Not Null));

-- Check valid values for [freq]. Note that 'single' is NOT in the RFC;
-- it is an optimization for my particular iCalendar calculation engine.
-- I use it as a clue that this pattern has only a single date (dtStart),
-- and there is no need to perform extra calculations on it.
Alter Table [rRule]
Add Constraint [rRule.ck.freq]
Check ([freq] In
    ('yearly'
    ,'monthly'
    ,'weekly'
    ,'daily'
    ,'hourly'
    ,'minutely'
    ,'secondly'
    ,'single')); -- Single is NOT part of the spec!

-- If there is a latitude, there must be a longitude, and vice versa.
Alter Table [rRule]
Add Constraint [rRule.ck.geo]
Check (([geoLat] Is Null And [geoLng] Is Null)
       Or ([geoLat] Is Not Null And [geoLng] Is Not Null));

-- Interval must be positive.
Alter Table [rRule]
Add Constraint [rRule.ck.interval]
Check ([interval] > 0);

-- Status has a set of defined values.
Alter Table [rRule]
Add Constraint [rRule.ck.status]
Check ([status] In ('cancelled', 'confirmed', 'tentative'));

-- Until and Count may not coexist in the same rule.
Alter Table [rRule]
Add Constraint [rRule.ck.until and count]
Check (Not ([until] Is Not Null And [count] Is Not Null));


-- One table for exceptions to rRules.  In my application, this covers both
-- exDate and rDate.  I do NOT support extended rule logic here;  The RFC says
-- you should support the same sort of date calculations here as are supported
-- in rRules: exceptions can recur, etc.  I don't do that; mine is simply a
-- set of dates that are either "exceptions" (dates which don't appear, even
-- if the rule otherwise says they should) or "extras" (dates which do appear,
-- even if the rule otherwise wouldn't include them).  This has proved
-- sufficient for my application, and something that can be exported into a
-- valid iCalendar file--even if I can't import an iCalendar file that makes
-- use of recurring rules for exceptions to recurring rules.
Create Table [exDate]
    (exDateID Integer Identity(1, 1) Not Null
     Constraint [exDate.pk]
     Primary Key
     Clustered
    ,rRuleID Integer Not Null
     Constraint [fk.rRule.exDates]
     Foreign Key
     References [rRule] (rRuleID)
     On Update Cascade
     On Delete Cascade
    ,[date] DateTime Not Null
    ,[type] varChar(6) Not Null);  -- Type = "exDate" or "rDate" for me; YMMV.

Para ir junto com isso, eu tenho várias funções SQL Server 2005 + CLR que podem ser usados ??para calcular as datas para vários eventos. Eu encontrei as seguintes formas para ser muito útil:

Select * From dbo.getDatesByVEventID(@id, @startDate, @endDate)
Select * From dbo.getEventsByDateRange(@startDate, @endDate, @maxCount)

A implementação do acima é divertido danado para descobrir!

Outras dicas

Sim, mais ou menos. Sunbird (o calendário mozilla opensource) é baseada em sqlite e eu apenas o download e descompactou o seu código fonte. Tem arquivos .sql nele.

ftp://ftp.mozilla.org /pub/mozilla.org/calendar/sunbird/releases/0.9/source/

mozilla \ calendário \ fornecedores \ Storage \ schema-7.sql --este é o esquema que usos Sunbird para fazer arquivos iCal válidos, por isso não pode ser tão ruim.

Muito obrigado Chris Nielsen por sua grande solução acima. No entanto, eu tive alguns problemas com ele assim que eu modifiquei. Por favor, note que a solução acima está em sqlalchemy python. I irá convertê-lo muito em breve.

As minhas principais dificuldades com a solução de Chris (e eles podem não se aplicar a todos) são

  1. Eu não preciso de muitas das colunas em sua solução. Eu só precisava de colunas que iria me ajudar com eventos e recorrências. Esta é a culpa do iCalendar spec, não Chris. Minha solução abaixo considera apenas regras de recorrência em termos de suas restrições de calendário e suas restrições de seqüência.

  2. Algumas colunas - o mais importante dtstart e DTEND - pertencem VEVENT, não RRULE, mas Chris colocou em RRULE. Este foi confuso para mim. VEVENT: https://tools.ietf.org/html/rfc5545#section- 3.6.1 RRULE: https://tools.ietf.org/html/rfc5545#section- 3.3.10

  3. Eu também precisava descobrir como conter um cronograma que pode ter uma variedade de padrões. Por exemplo, um evento pode acontecer a cada semana na sexta-feira a partir de seis horas - nove horas, mas também durante todo o dia no dia de maio. Isso requer flexibilidade com dtstart e DTEND. Por esta razão, eu criei uma contendo "PROGRAMAÇÃO" Tabela que mantém um relacionamento muitos-para-muitos com os eventos, enquanto que os eventos têm uma relação de contenção com RRULES.

Abaixo está a minha solução em sqlalchemy. Vou converter isso em SQL mais rápido possível.

from app import db
from sqlalchemy import CheckConstraint
from sqlalchemy.ext.associationproxy import association_proxy


class Schedule(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    subtypes_relation = db.relationship('Event', secondary=schedule_event_association,
                                        backref=db.backref('Schedule', lazy='dynamic'))

schedule_event_association = db.Table(
    'schedule_event_association',
    db.Column('schedule_id', db.Integer, db.ForeignKey('schedule.id')),
    db.Column('event_id', db.Integer, db.ForeignKey('event.id')))

class Event(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    dt_start = db.Column(db.DateTime)  # start time
    dt_end = db.Column(db.DateTime) # end time
    tz_id = db.Column(db.String) # Time Zone

    recurrence_rule = db.Column('RecurrenceRule_id',  db.Integer, db.ForeignKey('RecurrenceRule.id'))

# Start date must come before End date
    CheckConstraint('dtEnd is NULL OR dtStart <= dtEnd', name='Valid: Time Period')

class RecurrenceRule(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    # Frequency Type
    freq = db.Column(db.String(8), nullable=False, default='weekly') # type of recurrence

    # Calendar-Based Rules
    byDay = db.Column(db.String(35))   # List of Day of the Week
                                        # "mo,tu,we" for weekly
                                        # "+2MO, -1MO" = second monday, last monday for yearly or monthly
    byMonthDay = db.Column(db.String(200)) # List of Day of the Month
                                            # +1,-1"
                                            # Only for Monthly or Yearly
    byYearDay = db.Column(db.String(3078)) # List Day of the Year
                                            #"+1, -1"
                                            # Only for yearly
                                            # Take care with leap years
    byWeekNo = db.Column(db.String(353)) # Which week of Mon`enter code here`th
                                            # "+5, -3" for fifth and third-to-last
                                            # Only for yearly
    byMonth = db.Column(db.String(29))   # Month of year.

    # Sequence-Based Rules
    until = db.Column(db.DateTime)   # last day of occurence
    count = db.Column(db.Integer)    # number of occurences
    interval = db.Column(db.Integer, nullable=False, default=1) # interval between recurrences
    bysetpos = db.Column(db.String()) # Specifies specific instances of recurrence


# Valid Values
    CheckConstraint(freq in ('yearly', 'monthly', 'weekly', 'daily', 'single'),
                    name='Valid: Frequency Value')
    CheckConstraint(interval > 0, name='Valid: Positive Interval')
    CheckConstraint(byDay is not None and freq in ('daily', 'yearly', 'monthly'))
    CheckConstraint(byWeekNo is not None and freq in ('yearly', 'monthly'))
    CheckConstraint(byYearDay is not None and freq == 'yearly')

# Until and Count may not coexist in the same rule.
    CheckConstraint(not (until is not None and count is not None),
                    name='Valid: Not Both Until and Count')

iCal é uma aplicação da Apple que segue o padrão atualmente conhecida como iCalendar (o sucessor do VCALENDAR anterior). Eu acho que a wikipedia entrada tem todas as informações que você precisa para suas finalidades, e de uma forma simples e fácil de seguir o formato, mas, não hesite em pedir mais orientação, se necessário !!!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top