Index: openacs-4/packages/acs-events/acs-events.info
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/acs-events.info,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/acs-events.info 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,39 @@
+
+
+
+
+ ACS Events
+
+ f
+
+
+
+ oracle-8.1.6
+
+ W. Scott Meeks
+ Supports relationships between intervals in time, activities, and parties.
+ ArsDigita Corporation
+ The ACS events service is primarily intended for use by writers of application packages and other service packages. The service allows developers to specify and manipulate relationships (possibly recurring) between a <i>set of intervals in time</i>, an <i>activity</i>, and an arbitrary number of <i>parties</i>. An activity can be associated with an arbitrary number of ACS <i>objects</i>. Possible application domains include include calendaring, room reservation, scheduling, project management, and event registration.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: openacs-4/packages/acs-events/sql/acs-events-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/acs-events-create.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/acs-events-create.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,984 @@
+-- packages/acs-events/sql/acs-events-create.sql
+--
+-- @author smeeks@arsdigita.com
+-- @creation-date 2000-11-16
+-- @cvs-id $Id: acs-events-create.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+-- Create the objects and packages for the ACS Events service
+
+@@activity-create
+@@timespan-create
+@@recurrence-create
+
+-- Sequence for event tables that are not subclasses of acs_object
+create sequence acs_events_seq start with 1;
+
+-- The event object
+begin
+ acs_object_type.create_type (
+ supertype => 'acs_object',
+ object_type => 'acs_event',
+ pretty_name => 'ACS Event',
+ pretty_plural => 'ACS Events',
+ table_name => 'ACS_EVENTS',
+ id_column => 'EVENT_ID'
+ );
+end;
+/
+show errors
+
+-- Event attributes
+declare
+ attr_id acs_attributes.attribute_id%TYPE;
+begin
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'timespan_id',
+ pretty_name => 'Timespan',
+ pretty_plural => 'Timespans',
+ datatype => 'integer'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'activity_id',
+ pretty_name => 'Activity',
+ pretty_plural => 'Activities',
+ datatype => 'integer'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'recurrence_id',
+ pretty_name => 'Recurrence',
+ pretty_plural => 'Recurrences',
+ datatype => 'integer'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'name',
+ pretty_name => 'Name',
+ pretty_plural => 'Names',
+ datatype => 'string'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'description',
+ pretty_name => 'Description',
+ pretty_plural => 'Descriptions',
+ datatype => 'string'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'html_p',
+ pretty_name => 'HTML?',
+ pretty_plural => '',
+ datatype => 'string'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'related_link_url',
+ pretty_name => 'Related Link URL',
+ pretty_plural => 'Related Link URLs ',
+ datatype => 'string'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'related_link_text',
+ pretty_name => 'Related Link Text',
+ pretty_plural => 'Related Link Texts',
+ datatype => 'string'
+ );
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_event',
+ attribute_name => 'redirect_to_rel_link_p',
+ pretty_name => 'Redirect to Related Link?',
+ pretty_plural => '',
+ datatype => 'string'
+ );
+end;
+/
+show errors
+
+-- Events table
+
+create table acs_events (
+ event_id integer
+ constraint acs_events_fk references acs_objects(object_id) on delete cascade
+ constraint acs_events_pk primary key,
+ --
+ -- Need additional columns for attributes not inherited from activity, e.g.
+ -- activity.name = "Bootcamp" and event.name = "December Bootcamp"
+ --
+ -- The Event API supports methods to retrieve name/description from
+ -- either the event (if defined) or the underlying activity (if not defined)
+ --
+ -- acs_event.get_name()
+ -- acs_event.get_description()
+ -- acs_event.get_html_p()
+ --
+ name varchar2(255),
+ description varchar2(4000),
+ -- is the event description written in html
+ html_p char(1)
+ constraint acs_events_html_p_ck check(html_p in ('t','f')),
+ --
+ -- The following three columns encapsulate the remaining attributes of an Event:
+ -- the activity that takes place during the event, its timespan (a collection of time
+ -- intervals during which the event occurs), and an optional recurrence specification
+ -- that identifies how events repeat in time.
+ --
+ activity_id integer
+ constraint acs_events_activity_id_fk
+ references acs_activities on delete set null,
+ -- Can't reference timespans since it doesn't have a primary key
+ -- When referencing, need to call timespan.exists_p in case timespan
+ -- was deleted out from under event.
+ timespan_id integer,
+ recurrence_id integer
+ constraint acs_events_recurrence_id_fk
+ references recurrences,
+ -- a link which points to a page related to the event
+ -- this could be either additional detail or a substitution
+ -- for the link in some application view, e.g. drill-down from
+ -- calendar.
+ --
+ related_link_url varchar(4000),
+ related_link_text varchar(4000),
+ --
+ -- Interpretation of this column is application dependent, but it is
+ -- suggested that if this is 't', then related_link_{url|text} should be
+ -- used as the link in summary views in an application. Otherwise,
+ -- related_link_{url|text} should be available in a detail view
+ -- of the event.
+ --
+ redirect_to_rel_link_p char(1)
+ constraint acs_events_rdrct2rel_lnk_p_ck
+ check(redirect_to_rel_link_p in ('t','f'))
+);
+
+-- This is important to prevent locking on update of master table.
+-- See http://www.arsdigita.com/bboard/q-and-a-fetch-msg.tcl?msg_id=000KOh
+
+create index acs_events_activity_id_ids on acs_events(activity_id);
+
+-- This is useful for looking up instances of an event
+
+create index acs_events_recurrence_id_idx on acs_events(recurrence_id);
+
+comment on table acs_events is '
+ A relationship between a time span and an activity.
+';
+
+comment on column acs_events.name is '
+ The name of the event.
+';
+
+comment on column acs_events.description is '
+ The description of the event.
+';
+
+comment on column acs_events.timespan_id is '
+ The time span associated with this event.
+';
+
+comment on column acs_events.activity_id is '
+ The activity associated with this event.
+';
+
+comment on column acs_events.recurrence_id is '
+ A description of how this event recurs. If null, then this event does
+ not recur.
+';
+
+-- A table to create associations between events and parties
+
+create table acs_event_party_map (
+ event_id integer
+ constraint acs_evnt_party_map_evnt_id_fk
+ references acs_events on delete cascade,
+ party_id integer
+ constraint acs_evnt_party_map_party_id_fk
+ references parties on delete cascade,
+ constraint acs_evnt_party_map_pk primary key(event_id, party_id)
+);
+
+comment on table acs_event_party_map is '
+ Maps a many-to-many relationship between events and parties.
+';
+
+-- ACS Event Views
+
+-- This view makes the temporal information easier to access
+
+create or replace view acs_events_dates as
+select e.*,
+ start_date,
+ end_date
+from acs_events e,
+ timespans s,
+ time_intervals t
+where e.timespan_id = s.timespan_id
+and s.interval_id = t.interval_id;
+
+comment on table acs_events_dates is '
+ This view produces a separate row for each time interval in the timespan
+ associated with an event.
+';
+
+-- This view provides an alternative to the get_name and get_description
+-- functions
+
+create or replace view acs_events_activities as
+select event_id,
+ nvl(e.name, a.name) as name,
+ nvl(e.description, a.description) as description,
+ nvl(e.html_p, a.html_p) as html_p,
+ e.activity_id,
+ timespan_id,
+ recurrence_id
+from acs_events e,
+ acs_activities a
+where e.activity_id = a.activity_id;
+
+comment on table acs_events_activities is '
+ This view pulls the event name and description from the underlying
+ activity if necessary.
+';
+
+-- These views should make it easier to find recurrences that
+-- need to be populated further, e.g.
+--
+-- select recurrence_id
+-- from partially_populated_events p, acs_event_party_map m
+-- where db_populated_until < :current_date
+-- and p.event_id = m.event_id
+-- and party_id = :party_id
+-- group by recurrence_id
+--
+create or replace view partially_populated_event_ids as
+select min(event_id) as event_id,
+ db_populated_until
+from acs_events e,
+ recurrences r
+where e.recurrence_id = r.recurrence_id
+and (recur_until > db_populated_until or recur_until is null)
+group by r.recurrence_id, db_populated_until;
+
+comment on table partially_populated_event_ids is '
+ This view returns the first event_id and db_populated_until column
+ for any recurrences that have not been completely populated.
+';
+
+create or replace view partially_populated_events as
+select e.event_id,
+ timespan_id,
+ activity_id,
+ recurrence_id,
+ db_populated_until
+from acs_events e,
+ partially_populated_event_ids p
+where e.event_id = p.event_id;
+
+comment on table partially_populated_events is '
+ This view returns information about recurring events that have not been
+ completely populated (such as indefinitely recurring events.)
+';
+
+
+-- ACS Event API
+--
+-- Quick reference for the API supported for the Event object. Note that every procedure
+-- takes event_id as the first argument, we're just leave it out for compactness.
+--
+-- new (...)
+-- delete ()
+--
+-- get_name ()
+-- get_description ()
+--
+-- timespan_set (timespan_id)
+-- activity_set (activity_id)
+--
+-- party_map (party_id)
+-- party_unmap (party_id)
+--
+-- insert_instances (cutoff_date)
+--
+-- delete_all ()
+-- delete_all (recurrence_id)
+--
+-- shift (start_offset, end_offset)
+-- shift_all (start_offset, end_offset)
+-- shift_all (recurrence_id, start_offset, end_offset)
+--
+-- recurs_p ()
+
+
+create or replace package acs_event
+as
+ function new (
+ -- Creates a new event (20.10.10)
+ -- @author W. Scott Meeks
+ -- @param event_id optional id to use for new event
+ -- @param name optional Name of the new event
+ -- @param description optional Description of the new event
+ -- @param timespan_id optional initial time interval set
+ -- @param activity_id optional initial activity
+ -- @param recurrence_id optional id of recurrence information
+ -- @param object_type 'acs_event'
+ -- @param creation_date default sysdate
+ -- @param creation_user acs_object param
+ -- @param creation_ip acs_object param
+ -- @param context_id acs_object param
+ -- @return The id of the new event.
+ --
+ event_id in acs_events.event_id%TYPE default null,
+ name in acs_events.name%TYPE default null,
+ description in acs_events.description%TYPE default null,
+ timespan_id in acs_events.timespan_id%TYPE default null,
+ activity_id in acs_events.activity_id%TYPE default null,
+ recurrence_id in acs_events.recurrence_id%TYPE default null,
+ object_type in acs_object_types.object_type%TYPE default 'acs_event',
+ creation_date in acs_objects.creation_date%TYPE default sysdate,
+ creation_user in acs_objects.creation_user%TYPE default null,
+ creation_ip in acs_objects.creation_ip%TYPE default null,
+ context_id in acs_objects.context_id%TYPE default null
+ ) return acs_events.event_id%TYPE;
+
+ procedure delete (
+ -- Deletes an event (20.10.40)
+ -- Also deletes party mappings (via on delete cascade).
+ -- If this is the last instance of a recurring event, the recurrence
+ -- info is deleted as well
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to delete
+ --
+ event_id in acs_events.event_id%TYPE
+ );
+
+ procedure delete_all (
+ -- Deletes all instances of an event.
+ -- @author W. Scott Meeks
+ -- @param event_id All events with the same recurrence_id as this one will be deleted.
+ --
+ event_id in acs_events.event_id%TYPE
+ );
+
+ procedure delete_all_recurrences (
+ -- Deletes all instances of an event.
+ -- @author W. Scott Meeks
+ -- @param recurrence_id All events with this recurrence_id will be deleted.
+ --
+ recurrence_id in recurrences.recurrence_id%TYPE default null
+ );
+
+ function get_name (
+ -- Returns the name or the name of the activity associated with the event if
+ -- name is null.
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to get name for
+ --
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.name%TYPE;
+
+ function get_description (
+ -- Returns the description or the description of the activity associated
+ -- with the event if description is null.
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to get description for
+ --
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.description%TYPE;
+
+ function get_html_p (
+ -- Returns html_p or html_p of the activity associated with the event if
+ -- html_p is null.
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to get html_p for
+ --
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.html_p%TYPE;
+
+ procedure timespan_set (
+ -- Sets the time span for an event (20.10.15)
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to update
+ -- @param timespan_id new time interval set
+ --
+ event_id in acs_events.event_id%TYPE,
+ timespan_id in timespans.timespan_id%TYPE
+ );
+
+ procedure activity_set (
+ -- Sets the activity for an event (20.10.20)
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to update
+ -- @param activity_id new activity
+ --
+ event_id in acs_events.event_id%TYPE,
+ activity_id in acs_activities.activity_id%TYPE
+ );
+
+ procedure party_map (
+ -- Adds a party mapping to an event (20.10.30)
+ -- @author W. Scott Meeks
+ -- @param event_id event to add mapping to
+ -- @param party_id party to add mapping for
+ --
+ event_id in acs_events.event_id%TYPE,
+ party_id in parties.party_id%TYPE
+ );
+
+ procedure party_unmap (
+ -- Deletes a party mapping from an event (20.10.30)
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to delete mapping from
+ -- @param party_id id of party to delete mapping for
+ --
+ event_id in acs_events.event_id%TYPE,
+ party_id in parties.party_id%TYPE
+ );
+
+ function recurs_p (
+ -- Returns 't' if event recurs, 'f' otherwise (20.50.40)
+ -- @author W. Scott Meeks
+ -- @param event_id id of event to check
+ -- @return 't' or 'f'
+ --
+ event_id in acs_events.event_id%TYPE
+ ) return char;
+
+ function instances_exist_p (
+ -- Returns 't' if events with the given recurrence_id exist, 'f' otherwise
+ -- @author W. Scott Meeks
+ -- @param recurrence_id id of recurrence to check
+ -- @return 't' or 'f'
+ --
+ recurrence_id in acs_events.recurrence_id%TYPE
+ ) return char;
+
+ procedure insert_instances (
+ -- This is the key procedure creating recurring events. This procedure
+ -- uses the interval set and recurrence information referenced by the event
+ -- to insert additional information to represent the recurrences.
+ -- Events will be added up until the earlier of recur_until and
+ -- cutoff_date. The procedure enforces a hard internal
+ -- limit of adding no more than 10,000 recurrences at once to reduce the
+ -- risk of demolishing the DB because of application bugs. The date of the
+ -- last recurrence added is marked as the db_populated_until date.
+ --
+ -- The application is responsible for calling this function again if
+ -- necessary to populate to a later date.
+ --
+ -- @author W. Scott Meeks
+ -- @param event_id The id of the event to recur. If the
+ -- event's recurrence_id is null, nothing happens.
+ -- @param cutoff_date optional If provided, determines how far out to
+ -- prepopulate the DB. If not provided, then
+ -- defaults to sysdate plus the value of the
+ -- EventFutureLimit site parameter.
+ event_id in acs_events.event_id%TYPE,
+ cutoff_date in date default null
+ );
+
+ procedure shift (
+ -- Shifts the timespan of an event by the given offsets.
+ -- @author W. Scott Meeks
+ -- @param event_id Event to shift.
+ -- @param start_offset optional If provided, adds this number to the
+ -- start_dates of the timespan of the event.
+ -- No effect on any null start_date.
+ -- @param end_offset optional If provided, adds this number to the
+ -- end_dates of the timespan of the event.
+ -- No effect on any null end_date.
+ --
+ event_id in acs_events.event_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ );
+
+ procedure shift_all (
+ -- Shifts the timespan of all instances of a recurring event
+ -- by the given offsets.
+ -- @author W. Scott Meeks
+ -- @param event_id All events with the same
+ -- recurrence_id as this one will be shifted.
+ -- @param start_offset optional If provided, adds this number to the
+ -- start_dates of the timespan of the event
+ -- instances. No effect on any null start_date.
+ -- @param end_offset optional If provided, adds this number to the
+ -- end_dates of the timespan of the event
+ -- instances. No effect on any null end_date.
+ --
+ event_id in acs_events.event_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ );
+
+ procedure shift_all (
+ -- Same as above but invoked using recurrence Id
+ recurrence_id in recurrences.recurrence_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ );
+
+end acs_event;
+/
+show errors
+
+create or replace package body acs_event
+as
+ function new (
+ event_id in acs_events.event_id%TYPE default null,
+ name in acs_events.name%TYPE default null,
+ description in acs_events.description%TYPE default null,
+ timespan_id in acs_events.timespan_id%TYPE default null,
+ activity_id in acs_events.activity_id%TYPE default null,
+ recurrence_id in acs_events.recurrence_id%TYPE default null,
+ object_type in acs_object_types.object_type%TYPE default 'acs_event',
+ creation_date in acs_objects.creation_date%TYPE default sysdate,
+ creation_user in acs_objects.creation_user%TYPE default null,
+ creation_ip in acs_objects.creation_ip%TYPE default null,
+ context_id in acs_objects.context_id%TYPE default null
+ ) return acs_events.event_id%TYPE
+ is
+ new_event_id acs_events.event_id%TYPE;
+ begin
+ new_event_id := acs_object.new(
+ object_id => event_id,
+ object_type => object_type,
+ creation_date => creation_date,
+ creation_user => creation_user,
+ creation_ip => creation_ip,
+ context_id => context_id
+ );
+
+ insert into acs_events
+ (event_id, name, description, activity_id, timespan_id, recurrence_id)
+ values
+ (new_event_id, name, description, activity_id, timespan_id, recurrence_id);
+
+ return new_event_id;
+ end new;
+
+ procedure delete (
+ event_id in acs_events.event_id%TYPE
+ )
+ is
+ recurrence_id acs_events.recurrence_id%TYPE;
+ begin
+ select recurrence_id into recurrence_id
+ from acs_events
+ where event_id = acs_event.delete.event_id;
+
+ -- acs_events and acs_event_party_map deleted via on delete cascade
+ acs_object.delete(event_id);
+
+ -- Check for no more instances and delete recurrence if exists
+ if instances_exist_p(recurrence_id) = 'f' then
+ recurrence.delete(recurrence_id);
+ end if;
+ end delete;
+
+ procedure delete_all (
+ event_id in acs_events.event_id%TYPE
+ )
+ is
+ recurrence_id acs_events.recurrence_id%TYPE;
+ begin
+
+ select recurrence_id into recurrence_id
+ from acs_events
+ where event_id = delete_all.event_id;
+
+ delete_all_recurrences(recurrence_id);
+ end delete_all;
+
+ procedure delete_all_recurrences (
+ recurrence_id in recurrences.recurrence_id%TYPE default null
+ )
+ is
+ cursor event_id_cursor is
+ select event_id
+ from acs_events
+ where recurrence_id = delete_all_recurrences.recurrence_id;
+ event_id event_id_cursor%ROWTYPE;
+ begin
+ if recurrence_id is not null then
+ for event_id in event_id_cursor loop
+ acs_event.delete(event_id.event_id);
+ end loop;
+ end if;
+ end delete_all_recurrences;
+
+ -- Equivalent functionality to get_name and get_description provided by
+ -- acs_event_activity view
+
+ function get_name (
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.name%TYPE
+ is
+ name acs_events.name%TYPE;
+ begin
+ select nvl(e.name, a.name) into name
+ from acs_events e,
+ acs_activities a
+ where event_id = get_name.event_id
+ and e.activity_id = a.activity_id(+);
+
+ return name;
+ end get_name;
+
+ function get_description (
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.description%TYPE
+ is
+ description acs_events.description%TYPE;
+ begin
+ select nvl(e.description, a.description) into description
+ from acs_events e, acs_activities a
+ where event_id = get_description.event_id
+ and e.activity_id = a.activity_id(+);
+
+ return description;
+ end get_description;
+
+ function get_html_p (
+ event_id in acs_events.event_id%TYPE
+ ) return acs_events.html_p%TYPE
+ is
+ html_p acs_events.html_p%TYPE;
+ begin
+ select nvl(e.html_p, a.html_p) into html_p
+ from acs_events e, acs_activities a
+ where event_id = get_html_p.event_id
+ and e.activity_id = a.activity_id(+);
+
+ return html_p;
+ end get_html_p;
+
+ procedure timespan_set (
+ event_id in acs_events.event_id%TYPE,
+ timespan_id in timespans.timespan_id%TYPE
+ )
+ is
+ begin
+ update acs_events
+ set timespan_id = timespan_set.timespan_id
+ where event_id = timespan_set.event_id;
+ end timespan_set;
+
+ procedure activity_set (
+ event_id in acs_events.event_id%TYPE,
+ activity_id in acs_activities.activity_id%TYPE
+ )
+ as
+ begin
+ update acs_events
+ set activity_id = activity_set.activity_id
+ where event_id = activity_set.event_id;
+ end activity_set;
+
+ procedure party_map (
+ event_id in acs_events.event_id%TYPE,
+ party_id in parties.party_id%TYPE
+ )
+ is
+ begin
+ insert into acs_event_party_map
+ (event_id, party_id)
+ values
+ (event_id, party_id);
+ end party_map;
+
+ procedure party_unmap (
+ event_id in acs_events.event_id%TYPE,
+ party_id in parties.party_id%TYPE
+ )
+ is
+ begin
+ delete from acs_event_party_map
+ where event_id = party_unmap.event_id
+ and party_id = party_unmap.party_id;
+ end party_unmap;
+
+ function recurs_p (
+ event_id in acs_events.event_id%TYPE
+ ) return char
+ is
+ result char;
+ begin
+ select decode(recurrence_id, null, 'f', 't') into result
+ from acs_events
+ where event_id = recurs_p.event_id;
+
+ return result;
+ end recurs_p;
+
+ function instances_exist_p (
+ recurrence_id in acs_events.recurrence_id%TYPE
+ ) return char
+ is
+ result char;
+ begin
+ -- Only need to check if any rows exist.
+ select count(*) into result
+ from dual
+ where exists (select recurrence_id
+ from acs_events
+ where recurrence_id = instances_exist_p.recurrence_id);
+
+ if result = 0 then
+ return 'f';
+ else
+ return 't';
+ end if;
+ end instances_exist_p;
+
+ -- This function is used internally by insert_instances
+ function get_value (
+ parameter_name in apm_parameters.parameter_name%TYPE
+ ) return apm_parameter_values.attr_value%TYPE
+ is
+ package_id apm_packages.package_id%TYPE;
+ begin
+ select package_id into package_id
+ from apm_packages
+ where package_key = 'acs-events';
+
+ return apm.get_value(package_id, parameter_name);
+ end get_value;
+
+ -- This function is used internally by insert_instances
+ function new_instance (
+ event_id in acs_events.event_id%TYPE,
+ date_offset in integer
+ ) return acs_events.event_id%TYPE
+ is
+ event acs_events%ROWTYPE;
+ object acs_objects%ROWTYPE;
+ new_event_id acs_events.event_id%TYPE;
+ new_timespan_id acs_events.timespan_id%TYPE;
+ begin
+ select * into event
+ from acs_events
+ where event_id = new_instance.event_id;
+
+ select * into object
+ from acs_objects
+ where object_id = event_id;
+
+ new_timespan_id := timespan.copy(event.timespan_id, date_offset);
+
+ new_event_id := new(
+ name => event.name,
+ description => event.description,
+ timespan_id => new_timespan_id,
+ activity_id => event.activity_id,
+ recurrence_id => event.recurrence_id,
+ creation_user => object.creation_user,
+ creation_ip => object.creation_ip,
+ context_id => object.context_id
+ );
+
+ return new_event_id;
+ end new_instance;
+
+ procedure insert_instances (
+ event_id in acs_events.event_id%TYPE,
+ cutoff_date in date default null
+ )
+ is
+ event acs_events%ROWTYPE;
+ recurrence recurrences%ROWTYPE;
+ new_event_id acs_events.event_id%TYPE;
+ interval_name recurrence_interval_types.interval_name%TYPE;
+ n_intervals recurrence.every_nth_interval%TYPE;
+ days_of_week recurrence.days_of_week%TYPE;
+ last_date_done date;
+ stop_date date;
+ start_date date;
+ event_date date;
+ diff integer;
+ current_date date;
+ v_last_day date;
+ week_date date;
+ instance_count integer;
+ days_length integer;
+ days_index integer;
+ day_num integer;
+ begin
+ select * into event
+ from acs_events
+ where event_id = insert_instances.event_id;
+
+ select * into recurrence
+ from recurrences
+ where recurrence_id = event.recurrence_id;
+
+ -- Set cutoff date
+ -- EventFutureLimit is in years.
+ if cutoff_date is null then
+ stop_date := add_months(sysdate, 12 * get_value('EventFutureLimit'));
+ else
+ stop_date := cutoff_date;
+ end if;
+
+ -- Events only populated until max(cutoff_date, recur_until)
+ -- If recur_until null, then defaults to cutoff_date
+ if recurrence.recur_until < stop_date then
+ stop_date := recurrence.recur_until;
+ end if;
+
+ -- Figure out the date to start from
+ select trunc(min(start_date))
+ into event_date
+ from acs_events_dates
+ where event_id = insert_instances.event_id;
+
+ if recurrence.db_populated_until is null then
+ start_date := event_date;
+ else
+ start_date := recurrence.db_populated_until;
+ end if;
+
+ current_date := start_date;
+ last_date_done := start_date;
+ n_intervals := recurrence.every_nth_interval;
+
+ -- Case off of the interval_name to make code easier to read
+ select interval_name into interval_name
+ from recurrences r,
+ recurrence_interval_types t
+ where recurrence_id = recurrence.recurrence_id
+ and r.interval_type = t.interval_type;
+
+ -- Week has to be handled specially.
+ -- Start with the beginning of the week containing the start date.
+
+ if interval_name = 'week' then
+ current_date := NEXT_DAY(current_date - 7, 'SUNDAY');
+ days_of_week := recurrence.days_of_week;
+ days_length := LENGTH(days_of_week);
+ end if;
+
+ -- Check count to prevent runaway in case of error
+ instance_count := 0;
+ while instance_count < 10000 and (trunc(last_date_done) <= trunc(stop_date))
+ loop
+ instance_count := instance_count + 1;
+
+ -- Calculate next date based on interval type
+ if interval_name = 'day' then
+ current_date := current_date + n_intervals;
+ elsif interval_name = 'month_by_date' then
+ current_date := ADD_MONTHS(current_date, n_intervals);
+ elsif interval_name = 'month_by_day' then
+ -- Find last day of month before correct month
+ v_last_day := ADD_MONTHS(LAST_DAY(current_date), n_intervals - 1);
+ -- Find correct week and go to correct day of week
+ current_date := NEXT_DAY(v_last_day + (7 * (to_char(current_date, 'W') - 1)),
+ to_char(current_date, 'DAY'));
+ elsif interval_name = 'last_of_month' then
+ -- Find last day of correct month
+ v_last_day := LAST_DAY(ADD_MONTHS(current_date, n_intervals));
+ -- Back up one week and find correct day of week
+ current_date := NEXT_DAY(v_last_day - 7, to_char(current_date, 'DAY'));
+ elsif interval_name = 'year' then
+ current_date := ADD_MONTHS(current_date, 12 * n_intervals);
+ -- Deal with custom function
+ elsif interval_name = 'custom' then
+ execute immediate 'current_date := ' ||
+ recurrence.custom_func || '(' || current_date || ', ' || n_intervals || ');';
+ end if;
+
+ -- Check to make sure we're not going past Trunc because dates aren't integral
+ exit when trunc(current_date) > trunc(stop_date);
+
+ -- Have to handle week specially
+ if interval_name = 'week' then
+ -- loop over days_of_week extracting each day number
+ -- add day number and insert
+ days_index := 1;
+ week_date := current_date;
+ while days_index <= days_length loop
+ day_num := SUBSTR(days_of_week, days_index, 1);
+ week_date := current_date + day_num;
+ if trunc(week_date) > trunc(start_date) and trunc(week_date) <= trunc(stop_date) then
+ -- This is where we add the event
+ new_event_id := new_instance(
+ event_id,
+ trunc(week_date) - trunc(event_date)
+ );
+ last_date_done := week_date;
+ elsif trunc(week_date) > trunc(stop_date) then
+ -- Gone too far
+ exit;
+ end if;
+ days_index := days_index + 2;
+ end loop;
+
+ -- Now move to next week with repeats.
+ current_date := current_date + 7 * n_intervals;
+ else
+ -- All other interval types
+ -- This is where we add the event
+ new_event_id := new_instance(
+ event_id,
+ trunc(current_date) - trunc(event_date)
+ );
+ last_date_done := current_date;
+ end if;
+ end loop;
+
+ update recurrences
+ set db_populated_until = last_date_done
+ where recurrence_id = recurrence.recurrence_id;
+
+ end insert_instances;
+
+
+ procedure shift (
+ event_id in acs_events.event_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ )
+ is
+ begin
+ update acs_events_dates
+ set start_date = start_date + start_offset,
+ end_date = end_date + end_offset
+ where event_id = shift.event_id;
+ end shift;
+
+ procedure shift_all (
+ event_id in acs_events.event_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ )
+ is
+ begin
+ update acs_events_dates
+ set start_date = start_date + start_offset,
+ end_date = end_date + end_offset
+ where recurrence_id = (select recurrence_id
+ from acs_events
+ where event_id = shift_all.event_id);
+ end shift_all;
+
+ procedure shift_all (
+ recurrence_id in recurrences.recurrence_id%TYPE default null,
+ start_offset in number default 0,
+ end_offset in number default 0
+ )
+ is
+ begin
+ update acs_events_dates
+ set start_date = start_date + start_offset,
+ end_date = end_date + end_offset
+ where recurrence_id = shift_all.recurrence_id;
+ end shift_all;
+
+end acs_event;
+/
+show errors
+
Index: openacs-4/packages/acs-events/sql/acs-events-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/acs-events-drop.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/acs-events-drop.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,26 @@
+-- packages/acs-events/sql/acs-events-drop.sql
+--
+-- $Id: acs-events-drop.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+drop package acs_event;
+
+drop view partially_populated_events;
+drop view partially_populated_event_ids;
+drop view acs_events_activities;
+drop view acs_events_dates;
+
+drop table acs_event_party_map;
+drop index acs_events_recurrence_id_idx;
+drop table acs_events;
+
+begin
+ acs_object_type.drop_type ('acs_event');
+end;
+/
+show errors
+
+drop sequence acs_events_seq;
+
+@@recurrence-drop
+@@timespan-drop
+@@activity-drop
\ No newline at end of file
Index: openacs-4/packages/acs-events/sql/activity-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/activity-create.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/activity-create.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,284 @@
+-- packages/acs-events/sql/activity-create.sql
+--
+-- @author W. Scott Meeks
+-- @author Gary Jin (gjin@arsdigita.com)
+-- $Id: activity-create.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+--
+-- The activity object
+
+begin
+ acs_object_type.create_type (
+ supertype => 'acs_object',
+ object_type => 'acs_activity',
+ pretty_name => 'Activity',
+ pretty_plural => 'Activities',
+ table_name => 'ACS_ACTIVITIES',
+ id_column => 'ACTIVITY_ID'
+ );
+end;
+/
+show errors
+
+declare
+ attr_id acs_attributes.attribute_id%TYPE;
+begin
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_activity',
+ attribute_name => 'name',
+ pretty_name => 'Name',
+ pretty_plural => 'Names',
+ datatype => 'string'
+ );
+
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_activity',
+ attribute_name => 'description',
+ pretty_name => 'Description',
+ pretty_plural => 'Descriptions',
+ datatype => 'string'
+ );
+
+ attr_id := acs_attribute.create_attribute (
+ object_type => 'acs_activity',
+ attribute_name => 'html_p',
+ pretty_name => 'HTML?',
+ pretty_plural => 'HTML?',
+ datatype => 'string'
+ );
+end;
+/
+show errors
+
+-- The activities table
+
+create table acs_activities (
+ activity_id integer
+ constraint acs_activities_fk
+ references acs_objects(object_id)
+ on delete cascade
+ constraint acs_activities_pk
+ primary key,
+ name varchar2(255) not null,
+ description varchar2(4000),
+ -- is the activity description written in html?
+ html_p char(1)
+ constraint acs_activities_html_p_ck
+ check(html_p in ('t','f'))
+);
+
+comment on table acs_activities is '
+ Represents what happens during an event
+';
+
+create table acs_activity_object_map (
+ activity_id integer
+ constraint acs_act_obj_mp_activity_id_fk
+ references acs_activities on delete cascade,
+ object_id integer
+ constraint acs_act_obj_mp_object_id_fk
+ references acs_objects(object_id) on delete cascade,
+ constraint acs_act_obj_mp_pk
+ primary key(activity_id, object_id)
+);
+
+comment on table acs_activity_object_map is '
+ Maps between an activity and multiple ACS objects.
+';
+
+create or replace package acs_activity
+as
+ function new (
+ -- Create a new activity
+ -- @author W. Scott Meeks
+ -- @param activity_id optional id to use for new activity
+ -- @param name Name of the activity
+ -- @param description optional description of the activity
+ -- @param object_type 'acs_activity'
+ -- @param creation_date default sysdate
+ -- @param creation_user acs_object param
+ -- @param creation_ip acs_object param
+ -- @param context_id acs_object param
+ -- @return The id of the new activity.
+ --
+ activity_id in acs_activities.activity_id%TYPE default null,
+ name in acs_activities.name%TYPE,
+ description in acs_activities.description%TYPE default null,
+ html_p in acs_activities.html_p%TYPE default 'f',
+ object_type in acs_object_types.object_type%TYPE default 'acs_activity',
+ creation_date in acs_objects.creation_date%TYPE default sysdate,
+ creation_user in acs_objects.creation_user%TYPE default null,
+ creation_ip in acs_objects.creation_ip%TYPE default null,
+ context_id in acs_objects.context_id%TYPE default null
+ ) return acs_activities.activity_id%TYPE;
+
+ function name (
+ -- name method
+ -- @author gjin@arsdigita.com
+ -- @param activity_id
+ --
+ activity_id in acs_activities.activity_id%TYPE
+
+ ) return acs_activities.name%TYPE;
+
+ procedure delete (
+ -- Deletes an activity
+ -- @author W. Scott Meeks
+ -- @param activity_id id of activity to delete
+ activity_id in acs_activities.activity_id%TYPE
+ );
+
+
+ -- NOTE: can't use update
+
+ procedure edit (
+ -- Update the name or description of an activity
+ -- @author W. Scott Meeks
+ -- @param activity_id activity to update
+ -- @param name optional New name for this activity
+ -- @param description optional New description for this activity
+ -- @param html_p optional New value of html_p for this activity
+ activity_id in acs_activities.activity_id%TYPE,
+ name in acs_activities.name%TYPE default null,
+ description in acs_activities.description%TYPE default null,
+ html_p in acs_activities.html_p%TYPE default null
+ );
+
+ procedure object_map (
+ -- Adds an object mapping to an activity
+ -- @author W. Scott Meeks
+ -- @param activity_id id of activity to add mapping to
+ -- @param object_id id of object to add mapping for
+ --
+ activity_id in acs_activities.activity_id%TYPE,
+ object_id in acs_objects.object_id%TYPE
+ );
+
+ procedure object_unmap (
+ -- Deletes an object mapping from an activity
+ -- @author W. Scott Meeks
+ -- @param activity_id activity to delete mapping from
+ -- @param object_id object to delete mapping for
+ --
+ activity_id in acs_activities.activity_id%TYPE,
+ object_id in acs_objects.object_id%TYPE
+ );
+
+end acs_activity;
+/
+show errors
+
+create or replace package body acs_activity
+as
+ function new (
+ activity_id in acs_activities.activity_id%TYPE default null,
+ name in acs_activities.name%TYPE,
+ description in acs_activities.description%TYPE default null,
+ html_p in acs_activities.html_p%TYPE default 'f',
+ object_type in acs_object_types.object_type%TYPE default 'acs_activity',
+ creation_date in acs_objects.creation_date%TYPE default sysdate,
+ creation_user in acs_objects.creation_user%TYPE default null,
+ creation_ip in acs_objects.creation_ip%TYPE default null,
+ context_id in acs_objects.context_id%TYPE default null
+ ) return acs_activities.activity_id%TYPE
+ is
+ new_activity_id acs_activities.activity_id%TYPE;
+ begin
+ new_activity_id := acs_object.new(
+ object_id => activity_id,
+ object_type => object_type,
+ creation_date => creation_date,
+ creation_user => creation_user,
+ creation_ip => creation_ip,
+ context_id => context_id
+ );
+
+ insert into acs_activities
+ (activity_id, name, description)
+ values
+ (new_activity_id, name, description);
+
+ return new_activity_id;
+ end new;
+
+
+ function name (
+ -- name method
+ -- @author gjin@arsdigita.com
+ -- @param activity_id
+ --
+ activity_id in acs_activities.activity_id%TYPE
+
+ ) return acs_activities.name%TYPE
+
+ is
+ new_activity_name acs_activities.name%TYPE;
+
+ begin
+ select name
+ into new_activity_name
+ from acs_activities
+ where activity_id = name.activity_id;
+
+ return new_activity_name;
+ end;
+
+
+ procedure delete (
+ activity_id in acs_activities.activity_id%TYPE
+ )
+ is
+ begin
+ -- Cascade will cause delete from acs_activities
+ -- and acs_activity_object_map
+
+ acs_object.delete(activity_id);
+ end delete;
+
+ -- NOTE: can't use update
+
+ procedure edit (
+ activity_id in acs_activities.activity_id%TYPE,
+ name in acs_activities.name%TYPE default null,
+ description in acs_activities.description%TYPE default null,
+ html_p in acs_activities.html_p%TYPE default null
+ )
+ is
+ begin
+ update acs_activities
+ set name = nvl(edit.name, name),
+ description = nvl(edit.description, description),
+ html_p = nvl(edit.html_p, html_p)
+ where activity_id = edit.activity_id;
+ end edit;
+
+ procedure object_map (
+ activity_id in acs_activities.activity_id%TYPE,
+ object_id in acs_objects.object_id%TYPE
+ )
+ is
+ begin
+ insert into acs_activity_object_map
+ (activity_id, object_id)
+ values
+ (activity_id, object_id);
+ end object_map;
+
+ procedure object_unmap (
+ activity_id in acs_activities.activity_id%TYPE,
+ object_id in acs_objects.object_id%TYPE
+ )
+ is
+ begin
+ delete from acs_activity_object_map
+ where activity_id = object_unmap.activity_id
+ and object_id = object_unmap.object_id;
+ end object_unmap;
+
+end acs_activity;
+/
+show errors
+
+
+
+
+
Index: openacs-4/packages/acs-events/sql/activity-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/activity-drop.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/activity-drop.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,16 @@
+-- packages/acs-events/sql/activity-drop.sql
+--
+-- $Id: activity-drop.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+drop package acs_activity;
+drop table acs_activity_object_map;
+drop table acs_activities;
+
+begin
+ acs_object_type.drop_type ('acs_activity');
+end;
+/
+show errors
+
+
+
Index: openacs-4/packages/acs-events/sql/recurrence-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/recurrence-create.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/recurrence-create.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,206 @@
+-- packages/acs-events/sql/recurrence-create.sql
+--
+-- Support for temporal recurrences
+--
+-- $Id: recurrence-create.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+-- These columns describe how an event recurs. The are modeled on the Palm DateBook.
+-- The interval_type 'custom' indicates that the PL/SQL function referenced in
+-- custom_func should be used to generate the recurrences.
+
+-- Sequence for recurrence tables
+
+create sequence recurrence_seq start with 1;
+
+create table recurrence_interval_types (
+ interval_type integer
+ constraint recurrence_interval_type_pk primary key,
+ interval_name varchar2(50) not null
+ constraint rit_interval_name_un unique
+);
+
+set feedback off;
+
+insert into recurrence_interval_types values (1,'day');
+insert into recurrence_interval_types values (2,'week');
+insert into recurrence_interval_types values (3,'month_by_date');
+insert into recurrence_interval_types values (4,'month_by_day');
+insert into recurrence_interval_types values (5,'last_of_month');
+insert into recurrence_interval_types values (6,'year');
+insert into recurrence_interval_types values (7,'custom');
+
+set feedback on;
+
+create table recurrences (
+ recurrence_id integer
+ constraint recurrences_pk primary key,
+ --
+ -- Indicate the interval type for recurrence (see above)
+ --
+ interval_type constraint recurs_interval_type_fk
+ references recurrence_interval_types not null,
+ --
+ -- Indicates how many of the given intervals between recurrences.
+ -- Must be a positive number!
+ --
+ every_nth_interval integer
+ constraint recurs_every_nth_interval_ck
+ check(every_nth_interval > 0),
+ --
+ -- If recurring on a weekly basis (interval_type = 'week')
+ -- indicates which days of the week the event recurs on.
+ -- This is represented as a space separated list of numbers
+ -- corresponding to days of the week, where 0 corresponds to
+ -- Sunday, 1 to Monday, and so on. Null indicates no days are set.
+ -- So for example, '1' indicates recur on Mondays, '3 5' indicates
+ -- recur on Wednesday and Friday.
+ --
+ days_of_week varchar2(20),
+ --
+ -- Indicates when this event should stop recurring. Null indicates
+ -- recur indefinitely.
+ --
+ recur_until date,
+ --
+ -- Recurring events can be only partially populated if fully populating
+ -- the events would require inserting too many instances. This
+ -- column indicates up to what date this event has recurred. This
+ -- allows further instances to be added if the user attempts to view
+ -- a date beyond db_populated_until. If recur_until is not null,
+ -- then this column will always be prior to or the same as recur_until.
+ -- This column will be null until some recurrences have been added.
+ --
+ db_populated_until date,
+ --
+ -- This column holds the name of a PL/SQL function that will be called
+ -- to generate dates of recurrences if interval_type is 'custom'
+ --
+ custom_func varchar2(255)
+);
+
+-- This is important to prevent locking on update of master table.
+-- See http://www.arsdigita.com/bboard/q-and-a-fetch-msg.tcl?msg_id=000KOh
+create index recurrences_interval_type_idx on recurrences(interval_type);
+
+comment on table recurrences is '
+ Desribes how an event recurs.
+';
+
+comment on column recurrences.interval_type is '
+ One of day, week, month_by_date, month_by_day, last_of_month, year, custom.
+';
+
+comment on column recurrences.every_nth_interval is '
+ Indicates how many of the given intervals between recurrences.
+';
+
+comment on column recurrences.days_of_week is '
+ For weekly recurrences, stores which days of the week the event recurs on.
+';
+
+comment on column recurrences.recur_until is '
+ Indicates when this event should stop recurring. Null indicates
+ recur indefinitely.
+';
+
+comment on column recurrences.db_populated_until is '
+ Indicates the date of the last recurrence added. Used to determine if more
+ recurrences need to be added.
+';
+
+comment on column recurrences.custom_func is '
+ Stores the name of a PL/SQL function that can be called to generate dates
+ for special recurrences.
+';
+
+-- Recurrence API
+--
+-- Currently supports only new and delete methods.
+--
+
+create or replace package recurrence
+as
+ function new (
+ -- Creates a new recurrence
+ -- @author W. Scott Meeks
+ -- @param interval_type Sets interval_type of new recurrence
+ -- @param every_nth_interval Sets every_nth_interval of new recurrence
+ -- @param days_of_week optional If provided, sets days_of_week
+ -- of new recurrence
+ -- @param recur_until optional If provided, sets recur_until
+ -- of new recurrence
+ -- @param custom_func optional If provided, set name of
+ -- custom recurrence function
+ -- @return id of new recurrence
+ --
+ interval_type in recurrence_interval_types.interval_name%TYPE,
+ every_nth_interval in recurrences.every_nth_interval%TYPE,
+ days_of_week in recurrences.days_of_week%TYPE default null,
+ recur_until in recurrences.recur_until%TYPE default null,
+ custom_func in recurrences.custom_func%TYPE default null
+ ) return recurrences.recurrence_id%TYPE;
+
+ procedure delete (
+ -- Deletes the recurrence
+ -- @author W. Scott Meeks
+ -- @param recurrence_id id of recurrence to delete
+ --
+ recurrence_id in recurrences.recurrence_id%TYPE
+ );
+
+end recurrence;
+/
+show errors
+
+create or replace package body recurrence
+as
+ function new (
+ interval_type in recurrence_interval_types.interval_name%TYPE,
+ every_nth_interval in recurrences.every_nth_interval%TYPE,
+ days_of_week in recurrences.days_of_week%TYPE default null,
+ recur_until in recurrences.recur_until%TYPE default null,
+ custom_func in recurrences.custom_func%TYPE default null
+ ) return recurrences.recurrence_id%TYPE
+ is
+ recurrence_id recurrences.recurrence_id%TYPE;
+ interval_type_id recurrence_interval_types.interval_type%TYPE;
+ begin
+ select recurrence_seq.nextval into recurrence_id from dual;
+
+ select interval_type
+ into interval_type_id
+ from recurrence_interval_types
+ where interval_name = new.interval_type;
+
+ insert into recurrences
+ (recurrence_id,
+ interval_type,
+ every_nth_interval,
+ days_of_week,
+ recur_until,
+ custom_func)
+ values
+ (recurrence_id,
+ interval_type_id,
+ every_nth_interval,
+ days_of_week,
+ recur_until,
+ custom_func);
+
+ return recurrence_id;
+ end new;
+
+
+ -- Note: this will fail if there are any events_with this recurrence
+ procedure delete (
+ recurrence_id in recurrences.recurrence_id%TYPE
+ )
+ is
+ begin
+ delete from recurrences
+ where recurrence_id = recurrence.delete.recurrence_id;
+ end delete;
+
+end recurrence;
+/
+show errors
Index: openacs-4/packages/acs-events/sql/recurrence-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/recurrence-drop.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/recurrence-drop.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,10 @@
+-- packages/acs-events/sql/recurrence-drop.sql
+--
+-- $Id: recurrence-drop.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+drop package recurrence;
+
+drop table recurrences;
+drop table recurrence_interval_types;
+
+drop sequence recurrence_seq;
Index: openacs-4/packages/acs-events/sql/timespan-create.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/timespan-create.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/timespan-create.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,782 @@
+-- packages/acs-events/sql/timespan-create.sql
+--
+-- This script defines the data models for both time_interval and timespan.
+--
+-- API:
+--
+-- new (start_date, end_date)
+-- delete ()
+--
+-- edit (start_date, end_date)
+--
+-- shift (start_offset, end_offset)
+--
+-- overlaps_p (interval_id)
+-- overlaps_p (start_date, end_date)
+--
+-- $Id: timespan-create.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+-- Table for storing time intervals. Note that time intervals can be open on
+-- either end. This is represented by a null value for start_date or end_date.
+-- Applications can determine how to interpret null values. However, this is
+-- the default interpretation used by the overlaps_p functions. A null value
+-- for start_date is treated as extending to the beginning of time. A null
+-- value for end_date is treated as extending to the end of time. The net effect
+-- is that an interval with an open start overlaps any interval whose start
+-- is before the end of the interval with the open start. Likewise, an interval
+-- with an open end overlaps any interval whose end is after the start of the
+-- interval with the open end.
+
+-- Sequence for timespan tables
+create sequence timespan_seq start with 1;
+
+create table time_intervals (
+ interval_id integer
+ constraint time_intervals_pk
+ primary key,
+ start_date date,
+ end_date date,
+ constraint time_interval_date_order_ck
+ check(start_date <= end_date)
+);
+
+comment on table time_intervals is '
+ A time interval is represented by two points in time.
+';
+
+create or replace package time_interval
+as
+ function new (
+ -- Creates a new time interval
+ -- @author W. Scott Meeks
+ -- @param start_date optional Sets this as start_date of new interval
+ -- @param end_date optional Sets this as end_date of new interval
+ -- @return id of new time interval
+ --
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return time_intervals.interval_id%TYPE;
+
+ procedure delete (
+ -- Deletes the given time interval
+ -- @author W. Scott Meeks
+ -- @param interval_id id of the interval to delete
+ --
+ interval_id in time_intervals.interval_id%TYPE
+ );
+
+ -- NOTE: update is reserved and cannot be used for PL/SQL procedure names
+
+ procedure edit (
+ -- Updates the start_date or end_date of an interval
+ -- @author W. Scott Meeks
+ -- @param interval_id id of the interval to update
+ -- @param start_date optional If provided, sets this as the new
+ -- start_date of the interval.
+ -- @param end_date optional If provided, sets this as the new
+ -- start_date of the interval.
+ --
+ interval_id in time_intervals.interval_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ );
+
+ procedure shift (
+ -- Updates the start_date or end_date of an interval based on offsets of
+ -- fractional days.
+ -- @author W. Scott Meeks
+ -- @param interval_id The interval to update.
+ -- @param start_offset optional If provided, adds this number to the
+ -- start_date of the interval. No effect if
+ -- start_date is null.
+ -- @param end_offset optional If provided, adds this number to the
+ -- end_date of the interval. No effect if
+ -- end_date is null.
+ --
+ interval_id in time_intervals.interval_id%TYPE,
+ start_offset in number default 0,
+ end_offset in number default 0
+ );
+
+ function overlaps_p (
+ -- Returns 't' if the two intervals overlap, 'f' otherwise.
+ -- @author W. Scott Meeks
+ -- @param interval_1_id
+ -- @param interval_2_id
+ -- @return 't' or 'f'
+ --
+ interval_1_id in time_intervals.interval_id%TYPE,
+ interval_2_id in time_intervals.interval_id%TYPE
+ ) return char;
+
+ function overlaps_p (
+ -- Returns 't if the interval bounded by the given start_date or
+ -- end_date overlaps the given interval, 'f' otherwise.
+ -- @author W. Scott Meeks
+ -- @param start_date optional If provided, see if it overlaps
+ -- the interval.
+ -- @param end_date optional If provided, see if it overlaps
+ -- the interval.
+ -- @return 't' or 'f'
+ --
+ interval_id in time_intervals.interval_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return char;
+
+ function overlaps_p (
+ start_1 in time_intervals.start_date%TYPE,
+ end_1 in time_intervals.end_date%TYPE,
+ start_2 in time_intervals.start_date%TYPE,
+ end_2 in time_intervals.end_date%TYPE
+ ) return char;
+
+ function eq (
+ -- Checks if two intervals are equivalent
+ -- @author W. Scott Meeks
+ -- @param interval_1_id First interval
+ -- @param interval_2_id Second interval
+ -- @return boolean
+ --
+ interval_1_id in time_intervals.interval_id%TYPE,
+ interval_2_id in time_intervals.interval_id%TYPE
+ ) return boolean;
+
+ function copy (
+ -- Creates a new copy of a time interval, offset by optional offset
+ -- @author W. Scott Meeks
+ -- @param interval_id Interval to copy
+ -- @param offset optional If provided, interval is
+ -- offset by this number of days.
+ -- @return interval_id
+ --
+ interval_id in time_intervals.interval_id%TYPE,
+ offset in integer default 0
+ ) return time_intervals.interval_id%TYPE;
+
+end time_interval;
+/
+show errors
+
+create or replace package body time_interval
+as
+ function new (
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return time_intervals.interval_id%TYPE
+ is
+ interval_id time_intervals.interval_id%TYPE;
+ begin
+ select timespan_seq.nextval into interval_id from dual;
+
+ insert into time_intervals
+ (interval_id, start_date, end_date)
+ values
+ (interval_id, start_date, end_date);
+
+ return interval_id;
+ end new;
+
+ procedure delete (
+ interval_id in time_intervals.interval_id%TYPE
+ )
+ is
+ begin
+ delete time_intervals
+ where interval_id = time_interval.delete.interval_id;
+ end delete;
+
+ procedure edit (
+ interval_id in time_intervals.interval_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ )
+ is
+ begin
+ -- Null for start_date or end_date means don't change.
+ if start_date is not null and end_date is not null then
+ update time_intervals
+ set start_date = edit.start_date,
+ end_date = edit.end_date
+ where interval_id = edit.interval_id;
+ elsif start_date is not null then
+ update time_intervals
+ set start_date = edit.start_date
+ where interval_id = edit.interval_id;
+ elsif end_date is not null then
+ update time_intervals
+ set end_date = edit.end_date
+ where interval_id = edit.interval_id;
+ end if;
+ end edit;
+
+ procedure shift (
+ interval_id in time_intervals.interval_id%TYPE,
+ start_offset in number default 0,
+ end_offset in number default 0
+ )
+ is
+ begin
+ update time_intervals
+ set start_date = start_date + start_offset,
+ end_date = end_date + end_offset
+ where interval_id = shift.interval_id;
+ end shift;
+
+ function overlaps_p (
+ interval_1_id in time_intervals.interval_id%TYPE,
+ interval_2_id in time_intervals.interval_id%TYPE
+ ) return char
+ is
+ start_1 date;
+ start_2 date;
+ end_1 date;
+ end_2 date;
+ begin
+ -- Pull out the start and end dates and call the main overlaps_p.
+ select start_date,
+ end_date
+ into start_1,
+ end_1
+ from time_intervals
+ where interval_id = interval_1_id;
+
+ select start_date,
+ end_date
+ into start_2,
+ end_2
+ from time_intervals
+ where interval_id = interval_2_id;
+
+ return overlaps_p(start_1, end_1, start_2, end_2);
+ end overlaps_p;
+
+ function overlaps_p (
+ interval_id in time_intervals.interval_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return char
+ is
+ interval_start time_intervals.start_date%TYPE;
+ interval_end time_intervals.end_date%TYPE;
+ begin
+ -- Pull out the start and end date and call the main overlaps_p.
+ select start_date,
+ end_date
+ into interval_start,
+ interval_end
+ from time_intervals
+ where interval_id = overlaps_p.interval_id;
+
+ return overlaps_p(interval_start, interval_end, start_date, end_date);
+ end overlaps_p;
+
+ function overlaps_p (
+ start_1 in time_intervals.start_date%TYPE,
+ end_1 in time_intervals.end_date%TYPE,
+ start_2 in time_intervals.start_date%TYPE,
+ end_2 in time_intervals.end_date%TYPE
+ ) return char
+ is
+ begin
+ if start_1 is null then
+ -- No overlap if 2nd interval starts after 1st ends
+ if end_1 < start_2 then
+ return 'f';
+ else
+ return 't';
+ end if;
+ elsif start_2 is null then
+ -- No overlap if 2nd interval ends before 1st starts
+ if end_2 < start_1 then
+ return 'f';
+ else
+ return 't';
+ end if;
+ -- Okay, both start dates are not null
+ elsif start_1 <= start_2 then
+ -- 1st starts before 2nd
+ if end_1 < start_2 then
+ -- No overlap if 1st ends before 2nd starts
+ return 'f';
+ else
+ -- No overlap or at least one null
+ return 't';
+ end if;
+ else
+ -- 1st starts after 2nd
+ if end_2 < start_1 then
+ -- No overlap if 2nd ends before 1st starts
+ return 'f';
+ else
+ -- No overlap or at least one null
+ return 't';
+ end if;
+ end if;
+ end overlaps_p;
+
+ function eq (
+ -- Checks if two intervals are equivalent
+ interval_1_id in time_intervals.interval_id%TYPE,
+ interval_2_id in time_intervals.interval_id%TYPE
+ ) return boolean
+ is
+ interval_1 time_intervals%ROWTYPE;
+ interval_2 time_intervals%ROWTYPE;
+ begin
+ select * into interval_1
+ from time_intervals
+ where interval_id = interval_1_id;
+
+ select * into interval_2
+ from time_intervals
+ where interval_id = interval_2_id;
+
+ if interval_1.start_date = interval_2.start_date and
+ interval_1.end_date = interval_2.end_date then
+ return true;
+ else
+ return false;
+ end if;
+ end eq;
+
+ function copy (
+ interval_id in time_intervals.interval_id%TYPE,
+ offset in integer default 0
+ ) return time_intervals.interval_id%TYPE
+ is
+ interval time_intervals%ROWTYPE;
+ begin
+ select * into interval
+ from time_intervals
+ where interval_id = copy.interval_id;
+
+ return new(interval.start_date + offset, interval.end_date + offset);
+ end copy;
+
+end time_interval;
+/
+show errors
+
+-- Create the timespans table.
+
+create table timespans (
+ -- Can't be primary key because of the one to many relationship with
+ -- interval_id, but we can declare it not null and index it.
+ timespan_id integer not null,
+ interval_id integer
+ constraint tm_ntrvl_sts_interval_id_fk
+ references time_intervals on delete cascade
+);
+
+create index timespans_idx on timespans(timespan_id);
+
+-- This is important to prevent locking on update of master table.
+-- See http://www.arsdigita.com/bboard/q-and-a-fetch-msg.tcl?msg_id=000KOh
+create index timespans_interval_id_idx on timespans(interval_id);
+
+comment on table timespans is '
+ Establishes a relationship between timespan_id and multiple time
+ intervals. Represents a range of moments at which an event can occur.
+';
+
+-- TimeSpan API
+--
+-- Quick reference for the API supported for timespans. All procedures take timespan_id
+-- as the first argument (not shown explicitly):
+--
+-- new (interval_id)
+-- new (start_date, end_date)
+-- delete ()
+--
+-- Methods to join additional time intervals with an existing timespan:
+--
+-- join (timespan_id)
+-- join_interval (interval_id)
+-- join (start_date, end_date)
+--
+-- interval_delete (interval_id)
+-- interval_list ()
+--
+-- Tests for overlap:
+--
+-- overlaps_p (timespan_id)
+-- overlaps_p (interval_id)
+-- overlaps_p (start_date, end_date)
+--
+-- Info:
+--
+-- exists_p ()
+-- multi_interval_p ()
+
+create or replace package timespan
+as
+ function new (
+ -- Creates a new timespan (20.20.10)
+ -- given a time_interval
+ -- Copies the interval so the caller is responsible for deleting it
+ interval_id in time_intervals.interval_id%TYPE default null
+ ) return timespans.timespan_id%TYPE;
+
+ function new (
+ -- Creates a new timespan (20.20.10)
+ -- given a start_date and end_date
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return timespans.timespan_id%TYPE;
+
+ procedure delete (
+ -- Deletes the timespan and any contained intervals
+ -- @author W. Scott Meeks
+ -- @param timespan_id id of timespan to delete
+ timespan_id in timespans.timespan_id%TYPE
+ );
+
+ -- Join a new timespan or time interval to an existing timespan
+
+ procedure join (
+ -- timespan_1_id is modified, timespan_2_id is not
+ timespan_1_id in timespans.timespan_id%TYPE,
+ timespan_2_id in timespans.timespan_id%TYPE
+ );
+
+ -- Unfortunately, Oracle can't distinguish the signature of this function
+ -- with the previous because the args have the same underlying types
+ --
+ procedure join_interval (
+ -- interval is copied to the timespan
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE,
+ copy_p in boolean default true
+ );
+
+ procedure join (
+ timespan_id in timespans.timespan_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ );
+
+
+ procedure interval_delete (
+ -- Deletes an interval from the given timespan
+ -- @author W. Scott Meeks
+ -- @param timespan_id timespan to delete from
+ -- @param interval_id delete this interval from the set
+ --
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE
+ );
+
+ -- Information
+
+ function exists_p (
+ -- If its contained intervals are all deleted, then a timespan will
+ -- automatically be deleted. This checks a timespan_id to make sure it's
+ -- still valid.
+ -- @author W. Scott Meeks
+ -- @param timespan_id id of timespan to check
+ -- @return 't' or 'f'
+ timespan_id in timespans.timespan_id%TYPE
+ ) return char;
+
+ function multi_interval_p (
+ -- Returns 't' if timespan contains more than one interval,
+ -- 'f' otherwise (
+ -- @author W. Scott Meeks
+ -- @param timespan_id id of set to check
+ -- @return 't' or 'f'
+ timespan_id in timespans.timespan_id%TYPE
+ ) return char;
+
+
+ function overlaps_p (
+ -- Checks to see if a given interval overlaps any of the intervals
+ -- in the given timespan.
+ timespan_1_id in timespans.timespan_id%TYPE,
+ timespan_2_id in timespans.timespan_id%TYPE
+ ) return char;
+
+ -- Unfortunately, Oracle can't distinguish the signature of this function
+ -- with the previous because the args have the same underlying types
+ --
+ function overlaps_interval_p (
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE
+ ) return char;
+
+ function overlaps_p (
+ timespan_id in timespans.timespan_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return char;
+
+ function copy (
+ -- Creates a new copy of a timespan, offset by optional offset
+ -- @author W. Scott Meeks
+ -- @param timespan_id Timespan to copy
+ -- @param offset optional If provided, all dates in timespan
+ -- are offset by this number of days.
+ -- @return timespan_id
+ --
+ timespan_id in timespans.timespan_id%TYPE,
+ offset in integer default 0
+ ) return timespans.timespan_id%TYPE;
+
+end timespan;
+/
+show errors
+
+create or replace package body timespan
+as
+ function new (
+ interval_id in time_intervals.interval_id%TYPE
+ ) return timespans.timespan_id%TYPE
+ is
+ timespan_id timespans.timespan_id%TYPE;
+ new_interval_id time_intervals.interval_id%TYPE;
+ begin
+ select timespan_seq.nextval into timespan_id from dual;
+
+ new_interval_id := time_interval.copy(interval_id);
+
+ insert into timespans
+ (timespan_id, interval_id)
+ values
+ (timespan_id, new_interval_id);
+
+ return timespan_id;
+ end new;
+
+ function new (
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return timespans.timespan_id%TYPE
+ is
+ begin
+ return new(time_interval.new(start_date, end_date));
+ end new;
+
+ procedure delete (
+ timespan_id in timespans.timespan_id%TYPE
+ )
+ is
+ begin
+ -- Delete intervals, corresponding timespan entries deleted by
+ -- cascading constraints
+ delete from time_intervals
+ where interval_id in (select interval_id
+ from timespans
+ where timespan_id = timespan.delete.timespan_id);
+ end delete;
+
+ --
+ -- Join a new timespan or time interval to an existing timespan
+ --
+ procedure join (
+ timespan_1_id in timespans.timespan_id%TYPE,
+ timespan_2_id in timespans.timespan_id%TYPE
+ )
+ is
+ cursor timespan_cursor is
+ select *
+ from timespans
+ where timespan_id = timespan_2_id;
+ timespan_val timespan_cursor%ROWTYPE;
+ begin
+ -- Loop over intervals in 2nd timespan, join with 1st.
+ for timespan_val in timespan_cursor
+ loop
+ join_interval(timespan_1_id, timespan_val.interval_id);
+ end loop;
+ end join;
+
+ -- Optional argument to copy interval
+ procedure join_interval (
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE,
+ copy_p in boolean default true
+ )
+ is
+ new_interval_id time_intervals.interval_id%TYPE;
+ begin
+ if copy_p then
+ new_interval_id := time_interval.copy(interval_id);
+ else
+ new_interval_id := interval_id;
+ end if;
+
+ insert into timespans
+ (timespan_id, interval_id)
+ values
+ (timespan_id, new_interval_id);
+ end join_interval;
+
+ procedure join (
+ timespan_id in timespans.timespan_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ )
+ is
+ begin
+ join_interval(
+ timespan_id => timespan_id,
+ interval_id => time_interval.new(start_date, end_date),
+ copy_p => false
+ );
+ end join;
+
+ procedure interval_delete (
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE
+ )
+ is
+ begin
+ delete from timespans
+ where timespan_id = interval_delete.timespan_id
+ and interval_id = interval_delete.interval_id;
+ end interval_delete;
+
+ -- Information
+
+ function exists_p (
+ timespan_id in timespans.timespan_id%TYPE
+ ) return char
+ is
+ result integer;
+ begin
+ -- Only need to check if any rows exist.
+ select count(*)
+ into result
+ from dual
+ where exists (select timespan_id
+ from timespans
+ where timespan_id = exists_p.timespan_id);
+ if result = 0 then
+ return 'f';
+ else
+ return 't';
+ end if;
+ end exists_p;
+
+ function multi_interval_p (
+ timespan_id in timespans.timespan_id%TYPE
+ ) return char
+ is
+ result char;
+ begin
+ -- 'f' if 0 or 1 intervals, 't' otherwise
+ select decode(count(timespan_id), 0, 'f', 1, 'f', 't')
+ into result
+ from timespans
+ where timespan_id = multi_interval_p.timespan_id;
+
+ return result;
+ end multi_interval_p;
+
+
+ function overlaps_p (
+ -- Checks to see if any intervals in a timespan overlap any of the intervals
+ -- in the second timespan.
+ timespan_1_id in timespans.timespan_id%TYPE,
+ timespan_2_id in timespans.timespan_id%TYPE
+ ) return char
+ is
+ result char;
+ cursor timespan_cursor is
+ select *
+ from timespans
+ where timespan_id = timespan_2_id;
+ timespan_val timespan_cursor%ROWTYPE;
+ begin
+ -- Loop over 2nd timespan, checking each interval against 1st
+ for timespan_val in timespan_cursor
+ loop
+ result := overlaps_interval_p
+ (timespan_1_id,
+ timespan_val.interval_id
+ );
+ if result = 't' then
+ return 't';
+ end if;
+ end loop;
+ return 'f';
+ end overlaps_p;
+
+ function overlaps_interval_p (
+ timespan_id in timespans.timespan_id%TYPE,
+ interval_id in time_intervals.interval_id%TYPE default null
+ ) return char
+ is
+ start_date date;
+ end_date date;
+ begin
+ select start_date, end_date
+ into start_date, end_date
+ from time_intervals
+ where interval_id = overlaps_interval_p.interval_id;
+
+ return overlaps_p(timespan_id, start_date, end_date);
+ end overlaps_interval_p;
+
+ function overlaps_p (
+ timespan_id in timespans.timespan_id%TYPE,
+ start_date in time_intervals.start_date%TYPE default null,
+ end_date in time_intervals.end_date%TYPE default null
+ ) return char
+ is
+ result char;
+ cursor timespan_cursor is
+ select *
+ from timespans
+ where timespan_id = overlaps_p.timespan_id;
+ timespan_val timespan_cursor%ROWTYPE;
+ begin
+ -- Loop over each interval in timespan, checking against dates.
+ for timespan_val in timespan_cursor
+ loop
+ result := time_interval.overlaps_p(
+ timespan_val.interval_id,
+ start_date,
+ end_date
+ );
+
+ if result = 't' then
+ return 't';
+ end if;
+ end loop;
+ return 'f';
+ end overlaps_p;
+
+ function copy (
+ timespan_id in timespans.timespan_id%TYPE,
+ offset in integer default 0
+ ) return timespans.timespan_id%TYPE
+ is
+ cursor timespan_cursor is
+ select *
+ from timespans
+ where timespan_id = copy.timespan_id;
+ timespan_val timespan_cursor%ROWTYPE;
+ new_interval_id timespans.interval_id%TYPE;
+ new_timespan_id timespans.timespan_id%TYPE;
+ begin
+ new_timespan_id := null;
+
+ -- Loop over each interval in timespan, creating a new copy
+ for timespan_val in timespan_cursor
+ loop
+ new_interval_id := time_interval.copy(timespan_val.interval_id, offset);
+
+ if new_timespan_id is null then
+ new_timespan_id := new(new_interval_id);
+ else
+ join_interval(new_timespan_id, new_interval_id);
+ end if;
+ end loop;
+ return new_timespan_id;
+ end copy;
+
+end timespan;
+/
+show errors
+
Index: openacs-4/packages/acs-events/sql/timespan-drop.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/Attic/timespan-drop.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/timespan-drop.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,12 @@
+-- packages/acs-events/sql/timespan-drop.sql
+--
+-- $Id: timespan-drop.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+drop package timespan;
+drop index timespans_idx;
+drop table timespans;
+
+drop package time_interval;
+drop table time_intervals;
+
+drop sequence timespan_seq;
Index: openacs-4/packages/acs-events/sql/test/acs-events-test.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/test/Attic/acs-events-test.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/test/acs-events-test.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,509 @@
+--
+-- acs-events/sql/test/acs-events-test.sql
+--
+-- PL/SQL regression tests for ACS Events
+--
+-- Note: These tests use the utPLSQL regression package available at:
+-- ftp://ftp.oreilly.com/published/oreilly/oracle/utplsql/utInstall.zip
+--
+-- @author W. Scott Meeks (smeeks@arsdigita.com)
+--
+-- @creation-date 2000-11-29
+--
+-- @cvs-id $Id: acs-events-test.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+-- In order for utPLSQL to work, you need to grant
+-- specific permissions to your user:
+---
+-- grant create public synonym to servicename;
+-- grant drop public synonym to servicename;
+-- grant execute on dbms_pipe to servicename;
+-- grant drop any table to servicename;
+-- grant create any table to servicename;
+--
+-- In order to execute the test, you need to set things up
+-- in your SQL*PLUS session. First type:
+--
+-- set serveroutput on size 1000000 format wrapped
+--
+-- Now, if you have the UTL_FILE PL/SQL package installed, type:
+--
+-- exec utplsql.setdir('/web/servicename/packages/acs-events/sql/test');
+--
+-- Otherwise, you'll have to disable autocompilation and manually
+-- compile:
+--
+-- exec utplsql.autocompile (false);
+-- @acs-events-test
+--
+-- To actually execute the test, type:
+--
+-- exec utplsql.test('acs_event');
+
+set serveroutput on size 1000000 format wrapped
+exec utplsql.autocompile (false);
+exec utplsql.setdir('/web/servicename/packages/acs-events/sql/test');
+
+-- we need these here or else the PL/SQL won't compile.
+
+drop table ut_acs_events;
+create table ut_acs_events as select * from acs_events;
+
+drop table ut_acs_event_party_map;
+create table ut_acs_event_party_map as select * from acs_event_party_map;
+
+-- Template created with exec utGen.testpkg('acs_event');
+
+CREATE OR REPLACE PACKAGE ut_acs_event
+IS
+ PROCEDURE ut_setup;
+ PROCEDURE ut_teardown;
+
+ -- For each program to test...
+-- PROCEDURE ut_ACTIVITY_SET;
+-- PROCEDURE ut_DELETE;
+-- PROCEDURE ut_DELETE_ALL;
+-- PROCEDURE ut_DELETE_ALL_RECURRENCES;
+-- PROCEDURE ut_GET_DESCRIPTION;
+-- PROCEDURE ut_GET_NAME;
+ PROCEDURE ut_INSERT_INSTANCES;
+-- PROCEDURE ut_INSTANCES_EXIST_P;
+-- PROCEDURE ut_NEW;
+-- PROCEDURE ut_PARTY_MAP;
+-- PROCEDURE ut_PARTY_UNMAP;
+-- PROCEDURE ut_RECURS_P;
+-- PROCEDURE ut_SHIFT;
+-- PROCEDURE ut_SHIFT_ALL1;
+-- PROCEDURE ut_SHIFT_ALL2;
+-- PROCEDURE ut_TIMESPAN_SET;
+END ut_acs_event;
+/
+CREATE OR REPLACE PACKAGE BODY ut_acs_event
+IS
+ date1 date;
+ date2 date;
+
+ PROCEDURE ut_setup
+ IS
+ BEGIN
+ ut_teardown;
+ dbms_output.put_line('Setting up...');
+ -- create copies of the tables
+ execute immediate 'create table ut_acs_events as
+ select * from acs_events';
+ execute immediate 'create table ut_acs_event_party_map as
+ select * from acs_event_party_map';
+
+ END ut_setup;
+
+ PROCEDURE ut_teardown
+ IS
+ BEGIN
+ dbms_output.put_line('Tearing down...');
+ -- clean out the test tables
+ begin
+ execute immediate 'drop table ut_acs_events cascade constraints';
+ execute immediate 'drop table ut_acs_event_party_map cascade constraints';
+ exception
+ when others
+ then
+ null;
+ end;
+ END;
+
+ -- For each program to test...
+-- PROCEDURE ut_ACTIVITY_SET IS
+-- BEGIN
+-- ACS_EVENT.ACTIVITY_SET (
+-- EVENT_ID => ''
+-- ,
+-- ACTIVITY_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of ACTIVITY_SET',
+-- ''
+-- );
+-- END ut_ACTIVITY_SET;
+
+-- PROCEDURE ut_DELETE IS
+-- BEGIN
+-- ACS_EVENT.DELETE (
+-- EVENT_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of DELETE',
+-- ''
+-- );
+-- END ut_DELETE;
+
+-- PROCEDURE ut_DELETE_ALL IS
+-- BEGIN
+-- ACS_EVENT.DELETE_ALL (
+-- EVENT_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of DELETE_ALL',
+-- ''
+-- );
+-- END ut_DELETE_ALL;
+
+-- PROCEDURE ut_DELETE_ALL_RECURRENCES IS
+-- BEGIN
+-- ACS_EVENT.DELETE_ALL_RECURRENCES (
+-- RECURRENCE_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of DELETE_ALL_RECURRENCES',
+-- ''
+-- );
+-- END ut_DELETE_ALL_RECURRENCES;
+
+-- PROCEDURE ut_GET_DESCRIPTION IS
+-- BEGIN
+-- utAssert.this (
+-- 'Test of GET_DESCRIPTION',
+-- ACS_EVENT.GET_DESCRIPTION(
+-- EVENT_ID => ''
+-- )
+-- );
+-- END ut_GET_DESCRIPTION;
+
+-- PROCEDURE ut_GET_NAME IS
+-- BEGIN
+-- utAssert.this (
+-- 'Test of GET_NAME',
+-- ACS_EVENT.GET_NAME(
+-- EVENT_ID => ''
+-- )
+-- );
+-- END ut_GET_NAME;
+
+ -- The test of insert_instances has been augmented to test
+ -- other routines. Specifically new, delete, delete_all,
+ -- timespan_set, activity_set, get_name, get_description,
+ -- party_map, party_unmap, recurs_p, instances_exist_p
+ PROCEDURE ut_INSERT_INSTANCES IS
+
+ timespan_id acs_events.timespan_id%TYPE;
+ activity_id acs_events.activity_id%TYPE;
+ recurrence_id acs_events.recurrence_id%TYPE;
+ event_id acs_events.event_id%TYPE;
+ instance_count integer;
+ cursor event_cursor is
+ select * from acs_events_dates
+ where recurrence_id = ut_INSERT_INSTANCES.recurrence_id;
+ events event_cursor%ROWTYPE;
+ BEGIN
+ dbms_output.put_line('Testing INSERT_INSTANCES...');
+ -- Create event components
+ timespan_id := timespan.new(date1, date2);
+
+ activity_id := acs_activity.new(
+ name => 'Testing',
+ description => 'Making sure the code works'
+ );
+
+ -- Recurrence
+ recurrence_id := recurrence.new(
+ interval_type => 'week',
+ every_nth_interval => 1,
+ days_of_week => '1 3',
+ recur_until => to_date('2000-02-01')
+ );
+
+ -- Create event
+ event_id := acs_event.new();
+
+ -- Do some testing while we're here
+ utAssert.eq (
+ 'Test of INSTANCES_EXIST_P f within INSERT_INSTANCES',
+ acs_event.instances_exist_p(recurrence_id),
+ 'f'
+ );
+
+ insert into ut_acs_events (event_id)
+ values (event_id);
+
+ utAssert.eqtable (
+ 'Test of NEW within INSERT_INSTANCES',
+ 'ut_acs_events',
+ 'acs_events'
+ );
+
+ utAssert.isnull (
+ 'Test of GET_NAME null within INSERT_INSTANCES',
+ acs_event.get_name(event_id)
+ );
+
+ utAssert.isnull (
+ 'Test of GET_DESCRIPTION null within INSERT_INSTANCES',
+ acs_event.get_description(event_id)
+ );
+
+ utAssert.eq (
+ 'Test of RECURS_P f within INSERT_INSTANCES',
+ acs_event.recurs_p(event_id),
+ 'f'
+ );
+
+ acs_event.timespan_set(event_id, timespan_id);
+ acs_event.activity_set(event_id, activity_id);
+
+ update acs_events
+ set recurrence_id = ut_insert_instances.recurrence_id
+ where event_id = ut_insert_instances.event_id;
+
+ update ut_acs_events
+ set timespan_id = ut_insert_instances.timespan_id,
+ activity_id = ut_insert_instances.activity_id,
+ recurrence_id = ut_insert_instances.recurrence_id
+ where event_id = ut_insert_instances.event_id;
+
+ utAssert.eqtable (
+ 'Test of SET procedures within INSERT_INSTANCES',
+ 'ut_acs_events',
+ 'acs_events'
+ );
+
+ utAssert.eq (
+ 'Test of GET_NAME from activity within INSERT_INSTANCES',
+ acs_event.get_name(event_id),
+ 'Testing'
+ );
+
+ utAssert.eq (
+ 'Test of GET_DESCRIPTION from activity within INSERT_INSTANCES',
+ acs_event.get_description(event_id),
+ 'Making sure the code works'
+ );
+
+ update acs_events
+ set name = 'Further Testing',
+ description = 'Making sure the code works correctly.'
+ where event_id = ut_insert_instances.event_id;
+
+ utAssert.eq (
+ 'Test of GET_NAME from event within INSERT_INSTANCES',
+ acs_event.get_name(event_id),
+ 'Further Testing'
+ );
+
+ utAssert.eq (
+ 'Test of GET_DESCRIPTION from event within INSERT_INSTANCES',
+ acs_event.get_description(event_id),
+ 'Making sure the code works correctly.'
+ );
+
+ -- Insert instances
+ acs_event.insert_instances (
+ event_id => event_id
+ ,
+ cutoff_date => to_date('2000-02-02')
+ );
+
+ -- Test for instances
+ utAssert.eq (
+ 'Test of RECURS_P t within INSERT_INSTANCES',
+ acs_event.recurs_p(event_id),
+ 't'
+ );
+
+ utAssert.eq (
+ 'Test of INSTANCES_EXIST_P t within INSERT_INSTANCES',
+ acs_event.instances_exist_p(recurrence_id),
+ 't'
+ );
+
+ -- Count instances
+ select count(*)
+ into instance_count
+ from acs_events
+ where recurrence_id = ut_insert_instances.recurrence_id;
+
+ dbms_output.put_line('Instances: ' || instance_count);
+
+ utAssert.eqquery (
+ 'Test count of instances in INSERT_INSTANCES',
+ 'select count(*) from acs_events
+ where recurrence_id = ' || recurrence_id,
+ 'select 9 from dual'
+ );
+
+ -- Check that instances match except for dates
+ utAssert.eqquery (
+ 'Test instances in INSERT_INSTANCES',
+ 'select count(*) from (select name, description, activity_id
+ from acs_events
+ where recurrence_id = ' || recurrence_id ||
+ 'group by name, description, activity_id)',
+ 'select 1 from dual'
+ );
+
+ -- Check dates
+ -- Just print 'em out and eyeball 'em for now.
+ for events in event_cursor
+ loop
+ dbms_output.put_line(events.name || ' - ' ||
+ to_char(events.start_date, 'YYYY-MM-DD HH24:MI'));
+ end loop;
+
+ -- Clean up
+ acs_event.delete_all(event_id);
+ recurrence.delete(recurrence_id);
+ acs_activity.delete(activity_id);
+ timespan.delete(timespan_id);
+ END ut_INSERT_INSTANCES;
+
+-- PROCEDURE ut_INSTANCES_EXIST_P IS
+-- BEGIN
+-- utAssert.this (
+-- 'Test of INSTANCES_EXIST_P',
+-- ACS_EVENT.INSTANCES_EXIST_P(
+-- RECURRENCE_ID => ''
+-- )
+-- );
+-- END ut_INSTANCES_EXIST_P;
+
+-- PROCEDURE ut_NEW IS
+-- BEGIN
+-- utAssert.this (
+-- 'Test of NEW',
+-- ACS_EVENT.NEW(
+-- EVENT_ID => ''
+-- ,
+-- NAME => ''
+-- ,
+-- DESCRIPTION => ''
+-- ,
+-- TIMESPAN_ID => ''
+-- ,
+-- ACTIVITY_ID => ''
+-- ,
+-- RECURRENCE_ID => ''
+-- ,
+-- OBJECT_TYPE => ''
+-- ,
+-- CREATION_DATE => ''
+-- ,
+-- CREATION_USER => ''
+-- ,
+-- CREATION_IP => ''
+-- ,
+-- CONTEXT_ID => ''
+-- )
+-- );
+-- END ut_NEW;
+
+-- PROCEDURE ut_PARTY_MAP IS
+-- BEGIN
+-- ACS_EVENT.PARTY_MAP (
+-- EVENT_ID => ''
+-- ,
+-- PARTY_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of PARTY_MAP',
+-- ''
+-- );
+-- END ut_PARTY_MAP;
+
+-- PROCEDURE ut_PARTY_UNMAP IS
+-- BEGIN
+-- ACS_EVENT.PARTY_UNMAP (
+-- EVENT_ID => ''
+-- ,
+-- PARTY_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of PARTY_UNMAP',
+-- ''
+-- );
+-- END ut_PARTY_UNMAP;
+
+-- PROCEDURE ut_RECURS_P IS
+-- BEGIN
+-- utAssert.this (
+-- 'Test of RECURS_P',
+-- ACS_EVENT.RECURS_P(
+-- EVENT_ID => ''
+-- )
+-- );
+-- END ut_RECURS_P;
+
+-- PROCEDURE ut_SHIFT IS
+-- BEGIN
+-- ACS_EVENT.SHIFT (
+-- EVENT_ID => ''
+-- ,
+-- START_OFFSET => ''
+-- ,
+-- END_OFFSET => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of SHIFT',
+-- ''
+-- );
+-- END ut_SHIFT;
+
+-- PROCEDURE ut_SHIFT_ALL1 IS
+-- BEGIN
+-- ACS_EVENT.SHIFT_ALL (
+-- EVENT_ID => ''
+-- ,
+-- START_OFFSET => ''
+-- ,
+-- END_OFFSET => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of SHIFT_ALL',
+-- ''
+-- );
+-- END ut_SHIFT_ALL1;
+
+-- PROCEDURE ut_SHIFT_ALL2 IS
+-- BEGIN
+-- ACS_EVENT.SHIFT_ALL (
+-- RECURRENCE_ID => ''
+-- ,
+-- START_OFFSET => ''
+-- ,
+-- END_OFFSET => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of SHIFT_ALL',
+-- ''
+-- );
+-- END ut_SHIFT_ALL2;
+
+-- PROCEDURE ut_TIMESPAN_SET IS
+-- BEGIN
+-- ACS_EVENT.TIMESPAN_SET (
+-- EVENT_ID => ''
+-- ,
+-- TIMESPAN_ID => ''
+-- );
+
+-- utAssert.this (
+-- 'Test of TIMESPAN_SET',
+-- ''
+-- );
+-- END ut_TIMESPAN_SET;
+
+ begin
+ date1 := to_date('2000-01-03 13:00', 'YYYY-MM-DD HH24:MI');
+ date2 := to_date('2000-01-03 14:00', 'YYYY-MM-DD HH24:MI');
+
+END ut_acs_event;
+/
+show errors
+
+
Index: openacs-4/packages/acs-events/sql/test/timespan-test.sql
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/sql/test/Attic/timespan-test.sql,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/sql/test/timespan-test.sql 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,1003 @@
+--
+-- acs-events/sql/test/-test.sql
+--
+-- PL/SQL regression tests for
+--
+-- Note: These tests use the utPLSQL regression package available at:
+-- ftp://ftp.oreilly.com/published/oreilly/oracle/utplsql/utInstall.zip
+--
+-- @author W. Scott Meeks (smeeks@arsdigita.com)
+--
+-- @creation-date 2000-11-29
+--
+-- @cvs-id $Id: timespan-test.sql,v 1.1 2001/06/12 03:56:20 nsadmin Exp $
+
+-- In order for utPLSQL to work, you need to grant
+-- specific permissions to your user:
+---
+-- grant create public synonym to servicename;
+-- grant drop public synonym to servicename;
+-- grant execute on dbms_pipe to servicename;
+-- grant drop any table to servicename;
+-- grant create any table to servicename;
+--
+-- In order to execute the test, you need to set things up
+-- in your SQL*PLUS session. First type:
+--
+-- set serveroutput on size 1000000 format wrapped
+--
+-- Now, if you have the UTL_FILE PL/SQL package installed, type:
+--
+-- exec utplsql.setdir('/web/servicename/packages/acs-events/sql/test');
+--
+-- Otherwise, you'll have to disable autocompilation and manually
+-- compile:
+--
+-- exec utplsql.autocompile (false);
+-- @timespan-test
+--
+-- To actually execute the tests, type:
+--
+-- exec utplsql.test('time_interval');
+-- exec utplsql.test('timespan');
+
+set serveroutput on size 1000000 format wrapped
+exec utplsql.autocompile (false);
+exec utplsql.setdir('/web/servicename/packages/acs-events/sql/test');
+
+-- we need these here or else the PL/SQL won't compile.
+
+drop table ut_time_intervals;
+create table ut_time_intervals as select * from time_intervals;
+
+drop table ut_interval_ids;
+create table ut_interval_ids as select interval_id from time_intervals;
+
+-- Note: this package was created by hand
+create or replace package ut_time_interval
+as
+ procedure ut_setup;
+
+ procedure ut_teardown;
+
+ procedure ut_copy;
+
+ procedure ut_overlaps_p;
+
+ procedure ut_shift;
+
+ procedure ut_edit;
+
+ procedure ut_delete;
+
+ procedure ut_new;
+
+ procedure ut_eq;
+end ut_time_interval;
+/
+show errors
+
+create or replace package body ut_time_interval
+as
+ -- Common dates for testing
+ date1 date;
+ date2 date;
+ date3 date;
+ date4 date;
+
+ procedure ut_setup
+ is
+ begin
+ ut_teardown;
+ dbms_output.put_line('Setting up...');
+ -- create copy of the table
+ execute immediate 'create table ut_time_intervals as
+ select * from time_intervals';
+ -- Intervals to be saved during cleanup
+ execute immediate 'create table ut_interval_ids as
+ select interval_id from time_intervals';
+ utassert.eqtable (
+ msg_in => 'Comparing copied data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+ end ut_setup;
+
+ procedure ut_teardown
+ is
+ begin
+ dbms_output.put_line('Tearing down...');
+ -- clean out the test tables
+ begin
+ -- Delete intervals added by tests
+ delete time_intervals
+ where interval_id not in (select interval_id
+ from ut_interval_ids);
+ -- Drop test tables
+ execute immediate 'drop table ut_time_intervals cascade constraints';
+ execute immediate 'drop table ut_interval_ids cascade constraints';
+ exception
+ when others
+ then
+ null;
+ end;
+ end ut_teardown;
+
+ procedure ut_new
+ is
+ new_interval_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing new...');
+ -- Tests just the common functionality of the API.
+
+ -- create a time interval
+ utassert.isnotnull (
+ msg_in => 'Creating a new test time interval',
+ check_this_in => time_interval.new(date1, date2)
+ );
+
+ -- Verify that the API does the correct insert.
+ select timespan_seq.currval into new_interval_id from dual;
+ insert into ut_time_intervals(interval_id, start_date, end_date)
+ values(new_interval_id, date1, date2);
+
+ utassert.eqtable (
+ msg_in => 'Comparing created data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ end ut_new;
+
+ procedure ut_delete
+ is
+ new_interval_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing delete...');
+
+ new_interval_id := time_interval.new(date1, date2);
+
+ -- delete the row.
+ time_interval.delete(interval_id => new_interval_id);
+
+ -- verify time interval not there.
+ utassert.eqtable (
+ msg_in => 'Delete verification',
+ check_this_in => 'ut_time_intervals',
+ against_this_in => 'time_intervals'
+ );
+
+ end ut_delete;
+
+ procedure ut_eq
+ is
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ interval_3_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing eq...');
+
+ interval_1_id := time_interval.new(date1, date2);
+ interval_2_id := time_interval.new(date1, date2);
+ interval_3_id := time_interval.new(date2, date3);
+
+ utAssert.this (
+ 'Comparing equivalent dates',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ utAssert.eq (
+ 'Comparing different dates',
+ time_interval.eq(interval_1_id, interval_3_id),
+ false
+ );
+
+ -- Clean up
+ time_interval.delete(interval_1_id);
+ time_interval.delete(interval_2_id);
+ time_interval.delete(interval_3_id);
+ end ut_eq;
+
+ procedure ut_edit
+ is
+ interval_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing edit...');
+
+ -- create a new time interval to edit;
+ interval_id := time_interval.new(date1, date2);
+
+ -- Edit the time interval
+ time_interval.edit(interval_id => interval_id,
+ start_date => date2,
+ end_date => date3);
+
+ -- Verify
+ insert into ut_time_intervals(interval_id, start_date, end_date)
+ values(interval_id, date2, date3);
+
+ utassert.eqtable (
+ msg_in => 'Comparing edited data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ -- Edit the time interval
+ time_interval.edit(interval_id => interval_id,
+ start_date => date1);
+
+ -- Verify
+ update ut_time_intervals
+ set start_date = date1
+ where interval_id = ut_edit.interval_id;
+
+ utassert.eqtable (
+ msg_in => 'Comparing edited data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ -- Edit the time interval
+ time_interval.edit(interval_id => interval_id,
+ end_date => date2);
+
+ -- Verify
+ update ut_time_intervals
+ set end_date = date2
+ where interval_id = ut_edit.interval_id;
+
+ utassert.eqtable (
+ msg_in => 'Comparing edited data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ -- Edit the time interval
+ time_interval.edit(interval_id => interval_id);
+
+ -- Verify
+ utassert.eqtable (
+ msg_in => 'Comparing edited data for time interval',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ end ut_edit;
+
+ procedure ut_shift
+ is
+ interval_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing shift...');
+
+ -- create a new time interval to shift;
+ interval_id := time_interval.new(date1, date2);
+
+ -- Shift the time interval
+ time_interval.shift(interval_id, 1, 2);
+
+ -- Verify
+ insert into ut_time_intervals (interval_id, start_date, end_date)
+ values (interval_id, date2, date4);
+
+ -- create a new time interval to shift;
+ interval_id := time_interval.new(date1);
+
+ -- Shift the time interval
+ time_interval.shift(
+ interval_id => interval_id,
+ end_offset => 2
+ );
+
+ -- Verify
+ insert into ut_time_intervals (interval_id, start_date, end_date)
+ values (interval_id, date1, null);
+
+ utassert.eqtable (
+ msg_in => 'Comparing shifted data for time intervals',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ end ut_shift;
+
+ procedure ut_overlaps_p
+ is
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ begin
+ -- Note: not yet 100% branch coverage....
+
+ dbms_output.put_line('Testing overlaps_p...');
+
+ -- create new time intervals to test;
+ interval_1_id := time_interval.new();
+ interval_2_id := time_interval.new(date1, date2);
+
+ -- Test the time interval
+ utassert.eq (
+ msg_in => 'Null interval overlaps',
+ check_this_in =>
+ time_interval.overlaps_p(interval_1_id, interval_2_id),
+ against_this_in => 't'
+ );
+
+ -- Update 1st interval
+ time_interval.edit(
+ interval_id => interval_1_id,
+ start_date => date2
+ );
+
+ -- Test the time intervals
+ utassert.eq (
+ msg_in => 'Null start_2 overlaps',
+ check_this_in =>
+ time_interval.overlaps_p (
+ interval_id => interval_1_id,
+ start_date => null,
+ end_date => date3
+ ),
+ against_this_in => 't'
+ );
+ utassert.eq (
+ msg_in => 'Null start_2 no overlap',
+ check_this_in =>
+ time_interval.overlaps_p (
+ interval_id => interval_1_id,
+ start_date => null,
+ end_date => date1
+ ),
+ against_this_in => 'f'
+ );
+
+ utassert.eq (
+ msg_in => 'No nulls, no overlap',
+ check_this_in =>
+ time_interval.overlaps_p (
+ interval_id => interval_2_id,
+ start_date => date3,
+ end_date => date4
+ ),
+ against_this_in => 'f'
+ );
+ utassert.eq (
+ msg_in => 'No nulls, overlap 1 before 2',
+ check_this_in =>
+ time_interval.overlaps_p (
+ start_1 => date1,
+ end_1 => date3,
+ start_2 => date2,
+ end_2 => date4
+ ),
+ against_this_in => 't'
+ );
+ utassert.eq (
+ msg_in => 'No nulls, overlap 2 before 1',
+ check_this_in =>
+ time_interval.overlaps_p (
+ start_1 => date2,
+ end_1 => date4,
+ start_2 => date1,
+ end_2 => date3
+ ),
+ against_this_in => 't'
+ );
+
+ -- Delete the test intervals
+ time_interval.delete(interval_1_id);
+ time_interval.delete(interval_2_id);
+
+ end ut_overlaps_p;
+
+ procedure ut_copy
+ is
+ interval_id time_intervals.interval_id%TYPE;
+ new_interval_id time_intervals.interval_id%TYPE;
+ begin
+ dbms_output.put_line('Testing copy...');
+
+ -- create a new time interval to copy;
+ interval_id := time_interval.new(date1, date2);
+
+ -- Copy the time interval
+ new_interval_id := time_interval.copy(interval_id);
+
+ -- Insert for testing
+ insert into ut_time_intervals (interval_id, start_date, end_date)
+ values (interval_id, date1, date2);
+ insert into ut_time_intervals (interval_id, start_date, end_date)
+ values (new_interval_id, date1, date2);
+
+ -- Verify copies
+ utassert.eqtable (
+ msg_in => 'Comparing copied data for time intervals',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ -- Copy the time interval with offset
+ new_interval_id := time_interval.copy(interval_id, 1);
+
+ -- Insert for testing
+ insert into ut_time_intervals (interval_id, start_date, end_date)
+ values (new_interval_id, date2, date3);
+
+ -- Verify copies
+ utassert.eqtable (
+ msg_in => 'Comparing copied and shifted data for time intervals',
+ check_this_in => 'time_intervals',
+ against_this_in => 'ut_time_intervals'
+ );
+
+ end ut_copy;
+
+ begin
+ date1 := '2000-01-01';
+ date2 := '2000-01-02';
+ date3 := '2000-01-03';
+ date4 := '2000-01-04';
+
+end ut_time_interval;
+/
+show errors
+
+-- we need these here or else the PL/SQL won't compile.
+
+drop table ut_timespans;
+create table ut_timespans as select * from timespans;
+
+drop table ut_timespan_ids;
+create table ut_timespan_ids as select timespan_id from timespans;
+
+-- Note: this package was created starting from
+-- utGen.testpkg('timespan');
+
+CREATE OR REPLACE PACKAGE ut_timespan
+IS
+ PROCEDURE ut_setup;
+ PROCEDURE ut_teardown;
+
+ -- For each program to test...
+ PROCEDURE ut_COPY;
+ PROCEDURE ut_DELETE;
+ PROCEDURE ut_EXISTS_P;
+ PROCEDURE ut_INTERVAL_DELETE;
+ PROCEDURE ut_JOIN1;
+ PROCEDURE ut_JOIN2;
+ PROCEDURE ut_JOIN_INTERVAL;
+ PROCEDURE ut_MULTI_INTERVAL_P;
+ PROCEDURE ut_NEW1;
+ PROCEDURE ut_NEW2;
+ PROCEDURE ut_OVERLAPS_INTERVAL_P;
+ PROCEDURE ut_OVERLAPS_P1;
+ PROCEDURE ut_OVERLAPS_P2;
+END ut_timespan;
+/
+
+CREATE OR REPLACE PACKAGE BODY ut_timespan
+IS
+ -- Common dates for testing
+ date1 date;
+ date2 date;
+ date3 date;
+ date4 date;
+ date5 date;
+
+ PROCEDURE ut_setup
+ IS
+ BEGIN
+ ut_teardown;
+ dbms_output.put_line('Setting up...');
+ -- create copy of the table
+ execute immediate 'create table ut_timespans as
+ select * from timespans';
+ -- Intervals to be saved during cleanup
+ execute immediate 'create table ut_timespan_ids as
+ select timespan_id from timespans';
+ utassert.eqtable (
+ msg_in => 'Comparing copied data for timespan',
+ check_this_in => 'timespans',
+ against_this_in => 'ut_timespans'
+ );
+ END ut_setup;
+
+ PROCEDURE ut_teardown
+ IS
+ BEGIN
+ dbms_output.put_line('Tearing down...');
+ -- clean out the test tables
+ begin
+ -- Delete intervals added by tests
+ delete time_intervals
+ where interval_id in
+ (select interval_id
+ from timespans
+ where timespan_id not in (select timespan_id
+ from ut_timespan_ids));
+ -- Drop test tables
+ execute immediate 'drop table ut_timespans cascade constraints';
+ execute immediate 'drop table ut_timespan_ids cascade constraints';
+ exception
+ when others
+ then
+ null;
+ end;
+ END ut_teardown;
+
+ -- For each program to test...
+ PROCEDURE ut_COPY IS
+ timespan_1_id timespans.timespan_id%TYPE;
+ timespan_2_id timespans.timespan_id%TYPE;
+ timespan_copy_id timespans.timespan_id%TYPE;
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing COPY...');
+
+ timespan_1_id := timespan.new(date1, date2);
+
+ select interval_id
+ into interval_1_id
+ from time_intervals
+ where interval_id = (select interval_id
+ from timespans
+ where timespan_id = timespan_1_id);
+
+ timespan_2_id := timespan.new(date2, date3);
+
+ timespan_copy_id := timespan.copy(timespan_1_id);
+
+ select interval_id
+ into interval_2_id
+ from time_intervals
+ where interval_id = (select interval_id
+ from timespans
+ where timespan_id = timespan_copy_id);
+
+ utAssert.eq (
+ 'Test of COPY no offset',
+ time_interval.eq(interval_1_id, interval_2_id),
+ true
+ );
+
+ timespan.delete(timespan_copy_id);
+
+ timespan_copy_id := timespan.copy(timespan_1_id, 1);
+
+ select interval_id
+ into interval_1_id
+ from time_intervals
+ where interval_id = (select interval_id
+ from timespans
+ where timespan_id = timespan_2_id);
+ select interval_id
+ into interval_2_id
+ from time_intervals
+ where interval_id = (select interval_id
+ from timespans
+ where timespan_id = timespan_copy_id);
+
+ utAssert.eq (
+ 'Test of COPY w/ offset',
+ time_interval.eq(interval_1_id, interval_2_id),
+ true
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_1_id);
+ timespan.delete(timespan_2_id);
+ timespan.delete(timespan_copy_id);
+ END ut_COPY;
+
+ PROCEDURE ut_DELETE IS
+ timespan_id timespans.timespan_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing DELETE...');
+ timespan_id := timespan.new(date1, date2);
+
+ TIMESPAN.DELETE (
+ TIMESPAN_ID => timespan_id
+ );
+
+ utAssert.eqtable (
+ 'Test of DELETE',
+ 'ut_timespans',
+ 'timespans'
+ );
+ END ut_DELETE;
+
+ PROCEDURE ut_EXISTS_P IS
+ timespan_id timespans.timespan_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing EXISTS_P...');
+ timespan_id := timespan.new(date1, date2);
+
+ utAssert.eq (
+ 'Test of EXISTS_P true',
+ TIMESPAN.EXISTS_P(
+ TIMESPAN_ID => timespan_id
+ ),
+ 't'
+ );
+
+ timespan.delete(timespan_id);
+
+ utAssert.eq (
+ 'Test of EXISTS_P false',
+ TIMESPAN.EXISTS_P(
+ TIMESPAN_ID => timespan_id
+ ),
+ 'f'
+ );
+ END ut_EXISTS_P;
+
+ PROCEDURE ut_INTERVAL_DELETE IS
+ timespan_id timespans.timespan_id%TYPE;
+ interval_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing INTERVAL_DELETE...');
+
+ timespan_id := timespan.new(date1, date2);
+
+ select interval_id into interval_id
+ from timespans
+ where timespan_id = ut_INTERVAL_DELETE.timespan_id;
+
+
+ TIMESPAN.INTERVAL_DELETE (
+ TIMESPAN_ID => timespan_id
+ ,
+ INTERVAL_ID => interval_id
+ );
+
+ utAssert.eq (
+ 'Test of INTERVAL_DELETE',
+ timespan.exists_p(timespan_id),
+ 'f'
+ );
+ END ut_INTERVAL_DELETE;
+
+ PROCEDURE ut_JOIN1 IS
+ timespan_1_id timespans.timespan_id%TYPE;
+ timespan_2_id timespans.timespan_id%TYPE;
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing JOIN1...');
+ timespan_1_id := timespan.new(date1, date2);
+ timespan_2_id := timespan.new(date3, date4);
+
+ select interval_id into interval_1_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ timespan.join(timespan_1_id, timespan_2_id);
+
+ utAssert.eqquery (
+ 'JOIN1: interval count = 2',
+ 'select count(*)
+ from timespans
+ where timespan_id = ' || timespan_1_id,
+ 'select 2 from dual'
+ );
+
+ select min(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN1: match 1st interval',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ select interval_id into interval_1_id
+ from timespans
+ where timespan_id = timespan_2_id;
+
+ select max(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN1: match 2nd interval',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_1_id);
+ timespan.delete(timespan_2_id);
+ END ut_JOIN1;
+
+ PROCEDURE ut_JOIN2 IS
+ timespan_1_id timespans.timespan_id%TYPE;
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing JOIN2...');
+ timespan_1_id := timespan.new(date1, date2);
+
+ select interval_id into interval_1_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ timespan.join(timespan_1_id, date3, date4);
+
+ utAssert.eqquery (
+ 'JOIN2: interval count = 2',
+ 'select count(*)
+ from timespans
+ where timespan_id = ' || timespan_1_id,
+ 'select 2 from dual'
+ );
+
+ select min(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN2: match 1st interval',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ interval_1_id := time_interval.new(date3, date4);
+
+ select max(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN2: match 2nd interval',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_1_id);
+ time_interval.delete(interval_1_id);
+ END ut_JOIN2;
+
+ PROCEDURE ut_JOIN_INTERVAL IS
+ timespan_1_id timespans.timespan_id%TYPE;
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ interval_3_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing JOIN_INTERVAL...');
+ timespan_1_id := timespan.new(date1, date2);
+ interval_3_id := time_interval.new(date3, date4);
+
+ select interval_id into interval_1_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ timespan.join_interval(timespan_1_id, interval_3_id);
+
+ utAssert.eqquery (
+ 'JOIN_INTERVAL: interval count = 2',
+ 'select count(*)
+ from timespans
+ where timespan_id = ' || timespan_1_id,
+ 'select 2 from dual'
+ );
+
+ select min(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN_INTERVAL: match 1st interval',
+ time_interval.eq(interval_1_id, interval_2_id)
+ );
+
+ select max(interval_id) into interval_2_id
+ from timespans
+ where timespan_id = timespan_1_id;
+
+ utAssert.this (
+ 'JOIN1: match 2nd interval',
+ time_interval.eq(interval_2_id, interval_3_id)
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_1_id);
+ time_interval.delete(interval_3_id);
+ END ut_JOIN_INTERVAL;
+
+ PROCEDURE ut_MULTI_INTERVAL_P IS
+ timespan_id timespans.timespan_id%TYPE;
+ interval_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing MULTI_INTERVAL_P...');
+
+ timespan_id := timespan.new(date1, date2);
+ interval_id := time_interval.new(date1, date2);
+
+ utAssert.eq (
+ 'Test of MULTI_INTERVAL_P 1',
+ TIMESPAN.MULTI_INTERVAL_P(
+ TIMESPAN_ID => timespan_id
+ ),
+ 'f'
+ );
+
+ timespan.join_interval(timespan_id, interval_id);
+
+ utAssert.eq (
+ 'Test of MULTI_INTERVAL_P 2',
+ TIMESPAN.MULTI_INTERVAL_P(
+ TIMESPAN_ID => timespan_id
+ ),
+ 't'
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_id);
+ time_interval.delete(interval_id);
+ END ut_MULTI_INTERVAL_P;
+
+ PROCEDURE ut_NEW1 IS
+ interval_id time_intervals.interval_id%TYPE;
+ timespan_id timespans.timespan_id%TYPE;
+ new_interval_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing NEW1...');
+ interval_id := time_interval.new(date1, date2);
+ timespan_id := TIMESPAN.NEW(
+ INTERVAL_ID => interval_id
+ );
+ select interval_id into new_interval_id
+ from timespans
+ where timespan_id = ut_NEW1.timespan_id;
+
+ utAssert.this (
+ 'Test of NEW w/ interval',
+ time_interval.eq(interval_id, new_interval_id)
+ );
+
+ -- Cleanup
+ time_interval.delete(interval_id);
+ timespan.delete(timespan_id);
+ END ut_NEW1;
+
+ PROCEDURE ut_NEW2 IS
+ timespan_id timespans.timespan_id%TYPE;
+ interval time_intervals%ROWTYPE;
+ BEGIN
+ dbms_output.put_line('Testing NEW2...');
+ timespan_id := TIMESPAN.NEW(
+ START_DATE => date1
+ ,
+ END_DATE => date2
+ );
+
+ utAssert.eqquery (
+ 'Test of NEW w/ dates',
+ 'select start_date, end_date
+ from time_intervals
+ where interval_id = (select interval_id
+ from timespans
+ where timespan_id = ' || timespan_id || ')',
+ 'select to_date(''' || date1 || '''), to_date(''' || date2 || ''') from dual'
+ );
+ -- Cleanup
+ timespan.delete(timespan_id);
+ END ut_NEW2;
+
+ PROCEDURE ut_OVERLAPS_INTERVAL_P IS
+ timespan_id timespans.timespan_id%TYPE;
+ interval_1_id time_intervals.interval_id%TYPE;
+ interval_2_id time_intervals.interval_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing OVERLAPS_INTERVAL_P...');
+
+ timespan_id := timespan.new(date1, date3);
+ interval_1_id := time_interval.new(date2, date4);
+ interval_2_id := time_interval.new(date4, date5);
+
+ utAssert.eq (
+ 'Test of OVERLAPS_INTERVAL_P t',
+ TIMESPAN.OVERLAPS_INTERVAL_P(
+ TIMESPAN_ID => timespan_id
+ ,
+ INTERVAL_ID => interval_1_id
+ ),
+ 't'
+ );
+
+ utAssert.eq (
+ 'Test of OVERLAPS_INTERVAL_P f',
+ TIMESPAN.OVERLAPS_INTERVAL_P(
+ TIMESPAN_ID => timespan_id
+ ,
+ INTERVAL_ID => interval_2_id
+ ),
+ 'f'
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_id);
+ time_interval.delete(interval_1_id);
+ time_interval.delete(interval_2_id);
+ END ut_OVERLAPS_INTERVAL_P;
+
+ PROCEDURE ut_OVERLAPS_P1 IS
+ timespan_1_id timespans.timespan_id%TYPE;
+ timespan_2_id timespans.timespan_id%TYPE;
+ timespan_3_id timespans.timespan_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing OVERLAPS_P1...');
+
+ timespan_1_id := timespan.new(date1, date3);
+ timespan_2_id := timespan.new(date2, date4);
+ timespan_3_id := timespan.new(date4, date5);
+
+ utAssert.eq (
+ 'Test of OVERLAPS_P t',
+ TIMESPAN.OVERLAPS_P(
+ TIMESPAN_1_ID => timespan_1_id
+ ,
+ TIMESPAN_2_ID => timespan_2_id
+ ),
+ 't'
+ );
+
+ utAssert.eq (
+ 'Test of OVERLAPS_P f',
+ TIMESPAN.OVERLAPS_P(
+ TIMESPAN_1_ID => timespan_1_id
+ ,
+ TIMESPAN_2_ID => timespan_3_id
+ ),
+ 'f'
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_1_id);
+ timespan.delete(timespan_2_id);
+ timespan.delete(timespan_3_id);
+ END ut_OVERLAPS_P1;
+
+ PROCEDURE ut_OVERLAPS_P2 IS
+ timespan_id timespans.timespan_id%TYPE;
+ BEGIN
+ dbms_output.put_line('Testing OVERLAPS_P2...');
+
+ timespan_id := timespan.new(date1, date3);
+
+ utAssert.eq (
+ 'Test of OVERLAPS_P t',
+ TIMESPAN.OVERLAPS_P(
+ TIMESPAN_ID => timespan_id
+ ,
+ START_DATE => date2
+ ,
+ END_DATE => date4
+ ),
+ 't'
+ );
+
+ utAssert.eq (
+ 'Test of OVERLAPS_P f',
+ TIMESPAN.OVERLAPS_P(
+ TIMESPAN_ID => timespan_id
+ ,
+ START_DATE => date4
+ ,
+ END_DATE => date5
+ ),
+ 'f'
+ );
+
+ -- Cleanup
+ timespan.delete(timespan_id);
+ END ut_OVERLAPS_P2;
+
+ begin
+ date1 := '2000-01-01';
+ date2 := '2000-01-02';
+ date3 := '2000-01-03';
+ date4 := '2000-01-04';
+ date5 := '2000-01-05';
+
+END ut_timespan;
+/
+show errors
Index: openacs-4/packages/acs-events/www/doc/design.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/www/doc/design.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/www/doc/design.html 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,433 @@
+
+
+ACS Events Design Documentation
+
+
+
+
+ACS Events Design Documentation
+by W. Scott Meeks
+
+
+
+I. Essentials
+
+
+ - Tcl script directory (link to the API browser page for the package)
+ - PL/SQL file (link to the API browser page for the package)
+ - Data model: acs-events-create.sql
+
+
- Requirements document
+ - ER diagram
+ - Transaction flow diagram
+
+
+
+II. Introduction
+
+
+The ACS events service is primarily intended for use by writers of
+application packages and other service packages. The service allows
+developers to specify and manipulate relationships (possibly
+recurring) between a set of intervals in time, an
+activity, and an arbitrary number of parties. An
+activity can be associated with an arbitrary number of ACS
+objects.
+
+
+The package doesn't provide for any interpretation of events, leaving
+that up to the applications that use the service. In particular, the
+package assumes that permissioning, and the related concept of
+approval, will be handled by the application. Similarly, notification
+is also the responsibility of the application (but probably via
+another service package.) Likewise, the package provides no UI support.
+
+
+Possible application domains include include calendaring, room
+reservation, scheduling, project management, and event registration.
+
+
+The requirements break the functionality into four main areas: events,
+time intervals, activities, and recurrences. The package meets the
+requirements for each of these areas in the following ways:
+
+
+Events: The service creates a new subtype of acs_object:
+acs_event. It creates an auxiliary table for mapping events to
+parties. It provides an API for manipulating and querying events and their
+associated time interval sets, activities, recurrences, and parties.
+
+
+Time Intervals: The service creates tables for storing time
+intervals and sets of time intervals. It provides an API for
+manipulating and querying time intervals and time interval sets.
+
+
+Activities: The service creates a new subtype of acs_object:
+acs_activity. It creates an auxiliary table for mapping activities to
+objects. It provides an API for manipulating activities, their
+properties, and their associated objects.
+
+
+Recurrences: The service creates a table for storing
+information on how an event recurs, including how the event recurs and
+when it stops recurring. It provides an API for manipulating
+recurrence information and recurring events. This includes a function
+to insert event recurrences in such a way as to reasonably limit the
+amount of information stored in the DB for a particular event. This
+is done by only partially populating the recurrences for certain
+events. The service also provides a view which simplifies querying to
+find partially populated recurring events that need recurrences added
+to the DB.
+
+
+
+III. Historical Considerations
+
+
+There are number of historical considerations surrounding the
+design of recurring events. Much of the current design can be traced
+pack to the original ACS 3.4
+Calendar Package design, though the design has been
+cleaned up, modified to fit with the new events data model and slightly
+expanded.
+
+
+One key consideration is exactly how recurring events are supported.
+There are two main choices. One
+choice is to insert only a single row for each recurring event,
+regardless of the number of times it will recur. This row contains
+all the information necessary to compute whether or not that event
+would recur on a particular day. The alternative is to insert a row
+for each recurrence.
+
+
+I favored the second approach for the following reasons. First, one
+tradeoff is time vs. space. Computation, particularly if it might
+need to be done in Tcl and not solely in the database, is relatively
+expensive compared to storing additional information in the database.
+In many cases, the only information that will need to be stored for
+recurrences is the date and time of the recurrence.
+
+
+I think it may be faster in Oracle even with a stored proc, at least
+with the month view and possibly the week view as well. This is
+because with 1 row per recurrence, the month and week view queries can
+pull all the relevant items out at once and can take advantage of the
+index on the start_date column to optimize the query. With the stored
+proc, it would be necessary to iterate over each day (up to 42 in the
+month view), calling the check repeat proc for each base repeating
+item who's repeat_until date was still relevant, and then effectively
+constructing the item to be displayed.
+
+
+Another reason is that the first approach, to insert only a single
+row, seems to require a significantly more complex design. Thus the
+design, implementation and eventual maintenance time would be greater.
+It becomes even more complex when you allow exceptions. Now you need to
+maintain a separate table of exceptions and it becomes necessary to check
+through the exceptions table every time the check repeat proc is called.
+It the worst case, every recurrence is an exception, so you're
+essentially back to 1 row per recurrence, plus all the added complexity
+of using the check repeat proc.
+
+
+This is not an unreasonable possibility and is in fact how Sloan
+operates. Each class is represented as a recurring item and it is very
+common for each instance to have a different set of files attached to it.
+
+
+However, there are drawbacks to this approach. First, it will be more
+difficult to handle events that recur indefinitely. Second (but
+related) is that safeguards will need to be put in place to prevent
+pathological (accidental or intentional) cases from swamping the
+database.
+
+
+In the ACS 3.4 Calendar Package, this was partially resolved in the
+following way. Users are limited to looking no more than 10 years in
+the past or 10 years in the future. (Actually, this is a system
+parameter and can be set more or less restrictive, but the default is
+10 years.) This seemed reasonable given that other systems seem to
+have arbitrary, implementation driven limits. Yahoo and Excite have
+arbitrary limits between about 1970 and 2030. Palm seems to have no
+lower limit, but an upper limit of 2031.
+
+
+The 4.0 ACS Events service doesn't enforce a particular policy to
+prevent problems, but it does provide mechanisms that a well-designed
+application can use. The keys are the event_recurrence.insert_events
+procedure and the partially_populated_events view.
+
+
+insert_events takes either an event_id or a recurrence_id and a
+cutoff date. It either uses the recurrence_id, or gets it from the
+event_id, to retrieve the information needed to generate the dates of
+the recurrences. When inserting a recurring event for the first time,
+the application will need to call insert_events with a
+reasonable populate_until date. For calendar, for example, this could
+be sysdate + the lookahead limit.
+
+
+It is the application's responsibility to determine if additional
+events need to be inserted into the DB to support the date being used
+in a query to view events. The application can do this by querying on
+partially_populated_events, using the date in question and any other
+limiting conditions to determine if there are any recurrences that
+might recur on the date in question which have not been populated up
+to that date. To insure reasonable performance, the application needs
+to be clever about tracking the current date viewed and the maximum
+date viewed so as to minimize the number of times this query is
+performed. The application should also pick a date reasonably far in
+the future for insert additional instances.
+
+
+Another historical consideration is the choice of values for
+event_recurrence.interval_type. The original choice for the 3.4
+calendar was based on the Palm DateBook which seemed fairly inclusive
+(covering both Yahoo Calendar and Excite Planner) though it didn't
+capture some of the more esoteric cases covered by Outlook or
+(particuarly) Lotus Notes. The Events service maintains the original
+choices, but adds an additional choice, 'custom', which, when combined
+with the custom_func column, allows an application to generate an
+arbitrary recurrence function. The function must take a date and a
+number of intervals as arguments and return a new date greater than
+the given date. The number of intervals is guaranteed to be a
+positive integer.
+
+
+For the days_of_week column, the representation chosen, a
+space-delimited list of integers, has a number of advantages. First,
+it is easy and reasonably efficient to generate the set of dates
+corresponding to the recurrences. insert_events takes each
+number in the list in turn and adds it to the date of the beginning of
+the week. Second, the Tcl and Oracle representations are equivalent
+and the translations to and from UI are straightforward. In
+particular, the set of checkboxes corresponding to days of the week
+are converted directly into a Tcl list which can be stored directly
+into the DB.
+
+
+IV. Competitive Analysis
+
+
+Since this is a low level service package, there is no direct competition.
+
+
+
+V. Design Tradeoffs
+
+
+Because this is a service package, tradeoffs were made only in areas
+of interest to developers. Indeed, the main design tradeoff was made at the
+very beginning, namely that this would be a narrowly-focussed service
+package. This had consequences in the following areas:
+
+
+Maintainability
+
+To simplify the package as much as possible, a number of possible
+features were left to be handled by other services or by the
+applications using the events package. This includes controlling
+access to events via permissions, providing an approval process, and
+providing support for notification. permissions app dependent,
+approval via workflow, separate notification service package
+
+
+There was one significant, fairly complex feature that was
+included, namely the support for recurrences. It could have been left
+to the application developers or another service package. However,
+because the 3.4 Calendar package already had a model for recurring
+calendar items, it was straightforward to adapt this model for the
+rest of the events data model. The advantage of this is that this
+code is now in one place with no need for applications to reinvent
+the wheel. It also means that there is a consistent model across the
+toolkit.
+
+
+Reusability
+
+
+Much thought was given to the needs of applications most likely to use
+this service, such as calendar, events, and room reservations. This
+has led to a well defined API which should be reusable by most
+applications that are concerned by events.
+
+
+Testability
+
+
+Because the API consists of well defined PL/SQL functions, it should
+be fairly easy to build a test suite using the PL/SQL testing tools.
+
+
+VI. Data Model and API Discussion
+
+
+The data model and PL/SQL API encapsulate the four main abstractions
+defined in the ACS Events service: events, time interval sets,
+activities, and recurrences. At present, there is no Tcl API, but if
+desired one could be added consisting primarily of wrappers around
+PL/SQL functions and procedures.
+
+
+Events
+
+
+This is the main abstraction in the package. acs_event is a
+subtype of acs_object. In addition to the
+acs_events table, there is an acs_event_party_map
+table which maps between parties and events. The acs_event
+package defines new, delete, various procedures to
+set attributes and recurs_p indicating whether or not a
+particular event recurs.
+
+
+Time Interval Sets
+
+
+Because time interval sets are so simple, there is no need to make
+them a subtype of acs_object. Interval sets are represented
+with one table to represent time intervals, and a second table which
+groups intervals into sets, with corresponding PL/SQL packages
+defining new, delete, and additional manipulation functions.
+
+
+Activities
+
+
+This is the secondary abstraction in the package. acs_activity is a
+subtype of acs_object. In addition to the
+acs_activities table, there is an acs_activity_object_map
+table which maps between objects and activities. The acs_activity
+package defines new, delete, and various procedures to
+set attributes and mappings.
+
+
+Recurrences
+
+
+Since recurrences are always associated with events, there seemed to
+be no need to make them objects. The information that determines how
+an event recurs is stored in the event_recurrences table.
+
+
+The event_recurrence package defines new,
+delete, and other procedures related to recurrences. The key
+procedure is insert_events.
+
+
+A view, partially_populated_events, is created which hides
+some of the details of retrieving recurrences that need to populated further.
+
+
+
+
+VIII. User Interface
+
+
+This package does not provide a UI.
+
+
+IX. Configuration/Parameters
+
+
+There are no parameters for this package.
+
+
+
+
+X. Future Improvements/Areas of Likely Change
+
+
+If the system presently lacks useful/desirable features, note details
+here. You could also comment on non-functional improvements to the
+package, such as usability.
+
+
+
+Note that a careful treatment of the earlier "competitive analysis"
+section can greatly facilitate the documenting of this section.
+
+
+
+XI. Authors
+
+
+
+
+XII. Revision History
+
+
+
+ Document Revision # |
+ Action Taken, Notes |
+ When? |
+ By Whom? |
+
+
+
+ 0.1 |
+ Creation |
+ 11/20/2000 |
+ W. Scott Meeks |
+
+
+
+
+
+
Index: openacs-4/packages/acs-events/www/doc/index.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/www/doc/index.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/www/doc/index.html 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,21 @@
+
+ACS Events Documentation
+
+ACS Events Documentation
+
+by wsmeeks.arsdigita.com
+
+Engineering Docs
+
+
+Release Notes
+
+Please file bugs in the SDM.
+
+
+wsmeeks@arsdigita.com
+
Index: openacs-4/packages/acs-events/www/doc/requirements.html
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/packages/acs-events/www/doc/requirements.html,v
diff -u
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openacs-4/packages/acs-events/www/doc/requirements.html 12 Jun 2001 03:56:20 -0000 1.1
@@ -0,0 +1,504 @@
+
+
+ACS Events Service Requirements
+
+
+
+
+ACS Events Service Requirements
+
+by W. Scott Meeks
+
+
+
+I. Introduction
+
+
+This document records the requirements for a new ACS Events
+service package. This package is intended to provide basic
+functionality which can be used in a consistent manner by other
+service and application packages. The events service
+specifies relationships (possibly recurring) between a set of
+time intervals time, an activity, and an arbitrary number of parties. An
+activity can be associated with an arbitrary number of ACS objects.
+
+
+II. Vision Statement
+
+
+The ACS Events package will provide support for other services and
+applications that require representing this sort of relationship
+between time, parties, activities, and objects. Such applications include
+the generation of calendar objects, room reservations, event registration, and possibly
+workflow.
+
+
+The service needs to support recurring events. Many
+applications need to represent blocks of time within a given day that are
+intended to be be repeated regularly on subsequent days. The service should
+support representing the most common types of recurrences: daily, weekly, monthly, et cetera. It should
+also provide for custom recurrences.
+
+Having a single service for this functionality provides a number of
+business advantages:
+
+- It saves work and increases quality; applications that deal with
+time don't have to "re-invent the wheel" but instead can use a common,
+tested code base.
+
- It improves consistency; the same API will be used in different
+applications.
+
- It simplifies integration; since a common data model is used to represent
+events, different applications can more easily share related
+information.
+
+
+
+For example, the events service could support a room reservation
+application that is integrated with an application which maintains users's personal and group calendars. Suppose
+Stephanie uses the room reservation application to reserve the Boston
+1st floor conference room on 11/11 from 1pm to 2pm for Ern, Allen, and
+Alan. The events service will add a new event for this time
+slot, add a mapping to Ern, Allen, and Alan, and an activity for the
+meeting. This activity will map to
+the conference room. Now to get a calendar item to appear on Ern,
+Allen, and Alan's calendars, the reservation application can simply
+pass the event to the calendar application which adds a mapping
+between the activity and a new calendar item.
+
+
+III. ACS Events Package Overview
+
+
+There are four main areas of functionality in the events
+package: events, time intervals, activities, and
+recurrences. The service depends upon the ACS object and parties systems.
+
+
+III.A Events
+
+
+An event is an activity associated with a temporal interval or
+several such intervals. Events may have additional attributes as
+well. Examples of events include: "hitchhiking from 4pm to 5pm",
+"attending the InSync concert from 11pm to 1am at the Enormodome", et
+cetera. Events are represented by designating the associated
+activity together with a set of time intervals indicating when that
+activity is to occur.
+
+
+An event can optionally be mapped to a set of parties representing
+groups or individuals that have some connection to the event.
+
+
+The service provides an API for manipulating events.
+
+
+An event is a relationship which maps parties and an activity to a set
+of time intervals. The relationship between a particular event can be
+one to many with parties. Time intervals can be open ended.
+
+
+Activities contain a name, a description, and an optional link to
+related information. Ativites can be mapped one to many to ACS objects. The
+object mapped to a particular activity can be another activity or event.
+
+
+III.B Time Interval Sets
+
+A time interval set is a range of moments at which an event can
+occur. A single time interval is of the form "from 3:00pm to 3:17pm
+on 11/20/2000". A time interval set is of the form "from
+3:00pm to 3:17pm and from 4:30pm to 4:45pm on 11/20/2000". A
+set of time intervals has two advantages: (i) it
+allows for the representation of temporally gappy events such as
+conferences, which end one day and pick up again the next, and (ii) if
+implemented properly, it allows a simplification of the above account
+of events, as now an event can be identified with a pair of an
+activity together with a time interval set.
+
+
+The service provides an API for manipulating time interval sets.
+
+
+III.C Activities
+
+
+An activity is a thing that a person or people do, usually represented
+by a gerundic phrase, such as "biking", "reserving a room",
+"travelling to Bhutan to achieve enlightenment", et cetera. Activities
+are represented via a name and a description. An activity can
+optionally be mapped to a set of ACS objects.
+
+The service provides an API for manipulating activities.
+
+
+III.D Recurring Events
+
+
+Consider an event, say, an activity A performed on day D at time T. The ACS Events service allows applications to generate new events which are the same activity A performed on different days in the future, but at the same time of day T; such events are said to be recurrences of the primary event. Recurrences can happen on a daily,
+weekly, monthly, yearly or custom basis. The start and end dates of
+recurrences can be uniformly offset.
+
+
III.E Dependencies
+
+
+The service depends on the ACS object model and on our parties system. Event
+is a subtype of acs_object. The ACS Events service maps between the
+event object, a time interval set, an activity, and an arbitrary
+number of parties.
+
+
+IV. Use-cases and User-scenarios
+
+
+Determine the types or classes of users who would use the
+system, and what their experience would be like at a high-level.
+Sketch what their experience would be like and what actions they would
+take, and how the system would support them.
+
+
+
+V. Related Links
+
+
+
+
+VI.A Data Model Requirements
+
+
+10.10 Events
+
+
+10.10.10 The data model represents activities associated with sets
+of time intervals.
+
+
+10.10.20 Events can optionally be associated with parties.
+
+
+10.10.30> Events can optionally recur.
+
+
+10.20 Time Interval Sets
+
+
+10.20.10 A time interval consists of a start time and an end time.
+
+
+10.20.20 A time interval set consists of a set of associated time
+intervals.
+
+
+10.20.30 Individual time intervals can be open ended. That is, the
+beginning time, ending time, or both may be null. The exact meaning
+of a null time is application dependent. However, as a suggestion,
+null end time could indicate events such as holidays or birthdays that
+have no particular start time associated with them. Null start time
+could indicate a due date. Both times null could indicate some item
+that needs to be scheduled in the future but does not yet have a set
+time.
+
+
+10.30 Activities
+
+
+10.30.10 An activity has a name and a description.
+
+
+10.30.20 An activity can be associated with a set of ACS objects.
+
+
+10.30.30 An event object can be a valid target for an activity.
+This could indicate time dependencies, e.g. for workflow or project
+management.
+
+
+10.50 Recurring Events
+
+
+10.50.10 The data model provides a table which describes how to generate
+recurrences from a base event.
+
+
+10.50.20 Recurring on a daily basis should be supported.
+
+10.50.30 Recurring on a weekly basis should be supported. For
+weekly recurrences, it should be possible to specify exactly which
+days of the week.
+
+10.50.40 Recurring every month on a particular date should be
+supported.
+
+10.50.50 Recurring every month on a particular day of a
+particular week should be supported.
+
+10.50.60 If a date in the 4th or 5th week of a month has been
+selected, then an option should be presented allowing an item to recur
+on a particular day of the last week of a month.
+
+10.50.70 Recurring yearly on a particular date should be supported.
+
+10.50.80 The data model should allow an application to provide a
+custom recurrence function.
+
+10.50.90 It should be possible to specify an end date for
+recurrences.
+
+
+10.50.100 It should be possible to specify no end date for recurrences.
+
+
+10.50.110 The service should enforce reasonable limits on the
+amount of data used to represent recurring events. In other words,
+it should not be possible to fill the DB with thousands of rows
+representing a single recurring event, even if it recurs
+indefinitely.
+
+
+10.50.120 The service should provide a view for querying on
+those recurrences that aren't fully populated in the DB.
+
+
+VI.B API Requirements
+
+
+20.10 Event API
+
+
+20.10.10 The service supports adding an event.
+
+
+20.10.15 The service supports setting the time interval set of
+an event.
+
+
+20.10.20 The service supports setting the activity of an event.
+
+
+20.10.30 The service supports adding or deleting a party
+mapping to an event.
+
+
+20.10.40 The service supports deleting a complete event.
+
+
+
+20.20 Time Interval Set API
+
+
+20.20.10 The service supports adding a time interval set.
+
+
+20.20.20 The service supports adding a time interval to a set.
+
+
+20.20.30 The service supports updating the start or end dates
+of a time interval.
+
+
+20.20.40 The service supports deleting a time interval from a
+set.
+
+
+20.20.50 The service supports counting the number of time
+intervals in a set.
+
+
+20.20.60 The service supports determining if a given interval
+overlaps a particular time interval set.
+
+
+
+20.30 Activity API
+
+
+20.30.10 The service supports creating an activity.
+
+
+20.30.20 The service supports deleting an activity.
+
+
+20.30.30 The service supports updating the name of an activity.
+
+
+20.30.40 The service supports updating the description of an activity.
+
+
+20.30.50 The service supports adding or deleting an object
+mapping to an event.
+
+
+
+20.50 Recurrence API
+
+
+20.50.10 The service supports adding recurrences of an event.
+
+
+20.50.20 The service supports deleting recurrences of an event.
+
+
+20.50.30 The service supports uniformly offsetting the start or
+end times of time intervals of recurrences of an event.
+
+
+20.50.40 The service supports determining if an event recurs.
+
+
+VII. Design and Implementation Notes
+
+VII.A 3.4 Calendar Package
+
+
+The 3.4 calendar
+package provides some ideas for the design and implementation of the
+events service. One way to look at the service is as a
+distillation of the components of the calendar data model and implementation
+which would be common to any event-based application. In particular, I
+anticipate the table for recurring information will be very similar to
+the calendar data model for recurring items.
+
+
+VII.B Problem Set 2
+
+
+Another way to look at this events service is as an
+elaboration of the scheduling service in Problem Set
+2 revised for ACS 4.0. The main differences are allowing multiple
+time intervals, and a one to many relationship with parties and
+objects. Thus the data model will have the core event_id, and
+repeat_id in the event subtype of acs_object. Time Intervals will be
+in a separate table. The parties column and object column will be
+split out into separate mapping tables.
+
+
+VII.C Recurring Events
+
+
+There is a very important tradeoff to be made in the implementation of
+recurring events.
+Calendar
+Design Tradeoffs details this tradeoff as applied to the 3.4
+calendar package.
+
+
+There are two main choices for supporting recurring events. One
+choice is to insert only a single row for each recurring event,
+regardless of the number of times it will recur. This row contains
+all the information necessary to compute whether or not that event
+would recur on a particular day. The alternative is to insert a row
+for each recurrence.
+
+
+I favor the second approach for the following reasons. First, one
+tradeoff is time vs. space. Computation, particularly if it might
+need to be done in Tcl and not solely in the database, is relatively
+expensive compared to storing additional information in the database.
+In many cases, the only information that will need to be stored for
+recurrences is the date and time of the recurrence.
+
+
+Another reason is that the first approach, to insert only a single
+row, seems to require a significantly more complex design. Thus the
+design, implementation and eventual maintenance time would be greater.
+
+
+This approach will also make it much easier to handle
+exceptions to recurrences and individualizing the objects associated
+with instances of events.
+
+However, there are drawbacks to this approach. First, it will be more
+difficult to handle events that recur indefinitely. Second (but
+related) is that safeguards will need to be put in place to prevent
+pathological (accidental or intentional) cases from swamping the
+database.
+
+
+Another issue is that when populating the DB with recurring event
+instances, there is an application-level choice that the service
+needs to support. This is the decision as to whether the new event
+instances are mapped to the same object or to newly created objects.
+For example, for the reservation application, the instances should be
+mapped to the same room object. Alternately, for the calendar
+application, the instances should be mapped to new calendar events
+so that each instance can be modified individually.
+
+
+
+VIII. Revision History
+
+
+
+ Document Revision # |
+ Action Taken, Notes |
+ When? |
+ By Whom? |
+
+
+
+ 0.1 |
+ Creation |
+ 11/13/2000 |
+ W. Scott Meeks |
+
+
+
+ 0.2 |
+ Revision, remove timezones, add multiple timespans |
+ 11/14/2000 |
+ W. Scott Meeks |
+
+
+
+ 0.3 |
+ Rename "scheduling" to "event handling". Add activities. Renaming and updating requirements. |
+ 11/15/2000 |
+ W. Scott Meeks |
+
+
+
+ 0.4 |
+ Remove approval in favor of requiring applications to use acs-workflow. |
+ 11/17/2000 |
+ W. Scott Meeks |
+
+
+
+ 0.5 |
+ Name of package changes from "Event Handling" to "ACS Events". |
+ 11/17/2000 |
+ W. Scott Meeks |
+
+
+
+ 0.6 |
+ Clean up, clarification, rewording |
+ 12/08/2000 |
+ Joshua Finkler |
+
+
+
+
+
+
+
+smeeks@arsdigita.com
+
+Last modified: $Date: 2001/06/12 03:56:20 $
+
+
+