2009-06-28 22 views
30

ứng dụng của tôi phải xử lý thông tin lịch (bao gồm một lần xuất hiện đơn lẻ, lặp lại, v.v.). Để dễ dàng giao tiếp với các ứng dụng khác, tôi nghĩ rằng nên tạo giản đồ cơ sở dữ liệu dựa trên định dạng iCalendar (trường, mối quan hệ, ràng buộc) trực tiếp để tôi nhận các đối tượng tương thích iCalendar qua ORM mà tôi có thể dễ dàng phơi bày khi cần thiết.danh sách "Trường" iCalendar (đối với lược đồ cơ sở dữ liệu dựa trên tiêu chuẩn iCalendar)

Tôi biết rằng RFC khả dụng nhưng rất phức tạp vì tất cả thông tin bổ sung trong đó tôi không sử dụng vào lúc này.

Ai đó có thể chỉ cho tôi một nguồn dễ dàng hơn để tạo lược đồ cơ sở dữ liệu dựa trên tiêu chuẩn iCal (có nghĩa là danh sách các trường/tên trường và mối quan hệ của chúng đối với mục nhập iCal)?

Cảm ơn!

Trả lời

40

Tôi đã thực hiện việc này (chỉ đối với VEvents, không hỗ trợ các mặt hàng TODO hoặc Tạp chí hấp dẫn hoặc bất kỳ thứ gì tương tự). thực hiện của tôi trông như thế này (sau khi loại bỏ cột mà không phải là cụ thể cho các câu hỏi):

-- 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. 

Để đi cùng với điều này, tôi có một số chức năng CLR SQL Server 2005 + có thể được sử dụng để tính số ngày cho nhiều sự kiện. Tôi đã tìm thấy các biểu mẫu sau đây rất hữu ích:

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

Thực hiện các điều trên là thật thú vị để tìm ra!

+0

Tôi biết điều này đã hơn 7 tuổi, đây vẫn là cách bạn tiếp cận điều này, Và phiên bản SQL mới hơn có cung cấp cho bạn bất kỳ tùy chọn truy vấn nào tốt hơn không? –

0

iCal là một ứng dụng của Apple tuân thủ tiêu chuẩn hiện được biết đến là Icalendar (phiên bản kế thừa của Vcalendar trước đó). Tôi nghĩ rằng wikipedia entry có tất cả thông tin bạn cần cho mục đích của bạn, và ở định dạng đơn giản và dễ làm theo, nhưng, vui lòng hỏi thêm hướng dẫn nếu cần !!!

12

Có, sắp xếp. Sunbird (lịch mozilla mã nguồn mở) dựa trên sqlite và tôi vừa tải xuống và giải nén mã nguồn của họ. Nó có các tệp .sql trong đó.

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

mozilla \ lịch \ cung cấp \ lưu trữ \ schema-7.sql --this là schema mà sử dụng sunbird để làm cho file iCal hợp lệ, vì vậy nó không thể là quá xấu.

1
+2

Lưu ý đối với khách truy cập mới: Liên kết tài liệu dành cho nhà phát triển Apple không hoạt động nữa. –

+1

http: //www.filibeto.org/unix/macos/lib/dev/tài liệu/AppleApplications/Reference/SyncServicesSchemaRef/SyncServicesSchemaRef.pdf – Slartibartfast

1

Cảm ơn rất nhiều Chris Nielsen vì giải pháp tuyệt vời của mình ở trên. Tuy nhiên, tôi đã có một số vấn đề với nó vì vậy tôi đã sửa đổi nó. Xin lưu ý rằng các giải pháp trên là trong python sqlalchemy. Tôi sẽ sớm chuyển đổi nó.

My main difficulties with Chris's solution (and they might not apply to everyone) are 

(1) I didn't need many of the columns in his solution. I only needed columns which would help me with Events and Recurrences. This is the fault of the iCalendar spec, not Chris's. My solution below only considers recurrence rules in terms of their calendar restrictions and their sequence restrictions. 

(2) Certain columns -- most importantly dtStart and dtEnd -- belong to VEVENT, not to RRULE, but Chris placed them in RRULE. This was confusing to me. 
VEVENT: https://tools.ietf.org/html/rfc5545#section-3.6.1 
RRULE: https://tools.ietf.org/html/rfc5545#section-3.3.10 
(3) I also needed to figure out how to contain a schedule which might have a variety of patterns. For example, an event might happen every week on Friday from 6PM-9PM but also all day on May Day. This requires flexibility with dtStart and dtEnd. For this reason, I created a containing Table "SCHEDULE" which maintains a many-to-many relationship with EVENTS, whereas EVENTS have a containment relationship with RRULES. 

Below is my solution in sqlalchemy. I will convert this to SQL ASAP. 

=============================================================== 

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') 
+0

là bạn có thể tạo SQL cho việc này. Tôi đang làm việc trên một vấn đề tương tự và muốn một số hướng dẫn cho SQL. – aran

Các vấn đề liên quan