示例#1
0
 def sample(self):
     offset = PyDuration(seconds=int(self._helperDistribution.sample()))
     beginning = self.now(Timezone(tzid=self._tzname))
     while offset:
         start, end = self._findWorkAfter(beginning)
         if end - start > offset:
             result = start + offset
             result.setMinutes(result.getMinutes() // 15 * 15)
             result.setSeconds(0)
             return result
         offset.setDuration(offset.getTotalSeconds() - (end - start).getTotalSeconds())
         beginning = end
示例#2
0
class VAlarm(Component):

    sActionMap = {
        definitions.cICalProperty_ACTION_AUDIO:
        definitions.eAction_VAlarm_Audio,
        definitions.cICalProperty_ACTION_DISPLAY:
        definitions.eAction_VAlarm_Display,
        definitions.cICalProperty_ACTION_EMAIL:
        definitions.eAction_VAlarm_Email,
        definitions.cICalProperty_ACTION_PROCEDURE:
        definitions.eAction_VAlarm_Procedure,
        definitions.cICalProperty_ACTION_URI: definitions.eAction_VAlarm_URI,
        definitions.cICalProperty_ACTION_NONE: definitions.eAction_VAlarm_None,
    }

    sActionValueMap = {
        definitions.eAction_VAlarm_Audio:
        definitions.cICalProperty_ACTION_AUDIO,
        definitions.eAction_VAlarm_Display:
        definitions.cICalProperty_ACTION_DISPLAY,
        definitions.eAction_VAlarm_Email:
        definitions.cICalProperty_ACTION_EMAIL,
        definitions.eAction_VAlarm_Procedure:
        definitions.cICalProperty_ACTION_PROCEDURE,
        definitions.eAction_VAlarm_URI: definitions.cICalProperty_ACTION_URI,
        definitions.eAction_VAlarm_None: definitions.cICalProperty_ACTION_NONE,
    }

    # Classes for each action encapsulating action-specific data
    class VAlarmAction(object):

        propertyCardinality_1 = ()
        propertyCardinality_1_Fix_Empty = ()
        propertyCardinality_0_1 = ()
        propertyCardinality_1_More = ()

        def __init__(self, type):
            self.mType = type

        def duplicate(self):
            return VAlarm.VAlarmAction(self.mType)

        def load(self, valarm):
            pass

        def add(self, valarm):
            pass

        def remove(self, valarm):
            pass

        def getType(self):
            return self.mType

    class VAlarmAudio(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ATTACH,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, speak=None):
            super(VAlarm.VAlarmAudio,
                  self).__init__(type=definitions.eAction_VAlarm_Audio)
            self.mSpeakText = speak

        def duplicate(self):
            return VAlarm.VAlarmAudio(self.mSpeakText)

        def load(self, valarm):
            # Get properties
            self.mSpeakText = valarm.loadValueString(
                definitions.cICalProperty_ACTION_X_SPEAKTEXT)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_ACTION_X_SPEAKTEXT,
                            self.mSpeakText)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(
                definitions.cICalProperty_ACTION_X_SPEAKTEXT)

        def isSpeakText(self):
            return len(self.mSpeakText) != 0

        def getSpeakText(self):
            return self.mSpeakText

    class VAlarmDisplay(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_1_Fix_Empty = (
            definitions.cICalProperty_DESCRIPTION, )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, description=None):
            super(VAlarm.VAlarmDisplay,
                  self).__init__(type=definitions.eAction_VAlarm_Display)
            self.mDescription = description

        def duplicate(self):
            return VAlarm.VAlarmDisplay(self.mDescription)

        def load(self, valarm):
            # Get properties
            self.mDescription = valarm.loadValueString(
                definitions.cICalProperty_DESCRIPTION)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_DESCRIPTION,
                            self.mDescription)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)

        def getDescription(self):
            return self.mDescription

    class VAlarmEmail(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_1_Fix_Empty = (
            definitions.cICalProperty_DESCRIPTION,
            definitions.cICalProperty_SUMMARY,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        propertyCardinality_1_More = (definitions.cICalProperty_ATTENDEE, )

        def __init__(self, description=None, summary=None, attendees=None):
            super(VAlarm.VAlarmEmail,
                  self).__init__(type=definitions.eAction_VAlarm_Email)
            self.mDescription = description
            self.mSummary = summary
            self.mAttendees = attendees

        def duplicate(self):
            return VAlarm.VAlarmEmail(self.mDescription, self.mSummary,
                                      self.mAttendees)

        def load(self, valarm):
            # Get properties
            self.mDescription = valarm.loadValueString(
                definitions.cICalProperty_DESCRIPTION)
            self.mSummary = valarm.loadValueString(
                definitions.cICalProperty_SUMMARY)

            self.mAttendees = []
            if valarm.hasProperty(definitions.cICalProperty_ATTENDEE):
                # Get each attendee
                range = valarm.getProperties().get(
                    definitions.cICalProperty_ATTENDEE, ())
                for iter in range:
                    # Get the attendee value
                    attendee = iter.getCalAddressValue()
                    if attendee is not None:
                        self.mAttendees.append(attendee.getValue())

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_DESCRIPTION,
                            self.mDescription)
            valarm.addProperty(prop)

            prop = Property(definitions.cICalProperty_SUMMARY, self.mSummary)
            valarm.addProperty(prop)

            for iter in self.mAttendees:
                prop = Property(definitions.cICalProperty_ATTENDEE, iter,
                                Value.VALUETYPE_CALADDRESS)
                valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
            valarm.removeProperties(definitions.cICalProperty_SUMMARY)
            valarm.removeProperties(definitions.cICalProperty_ATTENDEE)

        def getDescription(self):
            return self.mDescription

        def getSummary(self):
            return self.mSummary

        def getAttendees(self):
            return self.mAttendees

    class VAlarmUnknown(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self):
            super(VAlarm.VAlarmUnknown,
                  self).__init__(type=definitions.eAction_VAlarm_Unknown)

        def duplicate(self):
            return VAlarm.VAlarmUnknown()

    class VAlarmURI(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
            definitions.cICalProperty_URL,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, uri=None):
            super(VAlarm.VAlarmURI,
                  self).__init__(type=definitions.eAction_VAlarm_URI)
            self.mURI = uri

        def duplicate(self):
            return VAlarm.VAlarmURI(self.mURI)

        def load(self, valarm):
            # Get properties
            self.mURI = valarm.loadValueString(definitions.cICalProperty_URL)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_URL, self.mURI)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_URL)

        def getURI(self):
            return self.mURI

    class VAlarmNone(VAlarmAction):

        propertyCardinality_1 = (definitions.cICalProperty_ACTION, )

        def __init__(self):
            super(VAlarm.VAlarmNone,
                  self).__init__(type=definitions.eAction_VAlarm_None)

        def duplicate(self):
            return VAlarm.VAlarmNone()

    def getMimeComponentName(self):
        # Cannot be sent as a separate MIME object
        return None

    sActionToAlarmMap = {
        definitions.eAction_VAlarm_Audio: VAlarmAudio,
        definitions.eAction_VAlarm_Display: VAlarmDisplay,
        definitions.eAction_VAlarm_Email: VAlarmEmail,
        definitions.eAction_VAlarm_URI: VAlarmURI,
        definitions.eAction_VAlarm_None: VAlarmNone,
    }

    propertyValueChecks = ICALENDAR_VALUE_CHECKS

    def __init__(self, parent=None):

        super(VAlarm, self).__init__(parent=parent)

        self.mAction = definitions.eAction_VAlarm_Display
        self.mTriggerAbsolute = False
        self.mTriggerOnStart = True
        self.mTriggerOn = DateTime()

        # Set duration default to 1 hour
        self.mTriggerBy = Duration()
        self.mTriggerBy.setDuration(60 * 60)

        # Does not repeat by default
        self.mRepeats = 0
        self.mRepeatInterval = Duration()
        self.mRepeatInterval.setDuration(5 * 60)  # Five minutes

        # Status
        self.mStatusInit = False
        self.mAlarmStatus = definitions.eAlarm_Status_Pending
        self.mLastTrigger = DateTime()
        self.mNextTrigger = DateTime()
        self.mDoneCount = 0

        # Create action data
        self.mActionData = VAlarm.VAlarmDisplay("")

    def duplicate(self, parent=None):
        other = super(VAlarm, self).duplicate(parent=parent)
        other.mAction = self.mAction
        other.mTriggerAbsolute = self.mTriggerAbsolute
        other.mTriggerOn = self.mTriggerOn.duplicate()
        other.mTriggerBy = self.mTriggerBy.duplicate()
        other.mTriggerOnStart = self.mTriggerOnStart

        other.mRepeats = self.mRepeats
        other.mRepeatInterval = self.mRepeatInterval.duplicate()

        other.mAlarmStatus = self.mAlarmStatus
        if self.mLastTrigger is not None:
            other.mLastTrigger = self.mLastTrigger.duplicate()
        if self.mNextTrigger is not None:
            other.mNextTrigger = self.mNextTrigger.duplicate()
        other.mDoneCount = self.mDoneCount

        other.mActionData = self.mActionData.duplicate()
        return other

    def getType(self):
        return definitions.cICalComponent_VALARM

    def getAction(self):
        return self.mAction

    def getActionData(self):
        return self.mActionData

    def isTriggerAbsolute(self):
        return self.mTriggerAbsolute

    def getTriggerOn(self):
        return self.mTriggerOn

    def getTriggerDuration(self):
        return self.mTriggerBy

    def isTriggerOnStart(self):
        return self.mTriggerOnStart

    def getRepeats(self):
        return self.mRepeats

    def getInterval(self):
        return self.mRepeatInterval

    def added(self):
        # Added to calendar so add to calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.AddAlarm(this)

        # Do inherited
        super(VAlarm, self).added()

    def removed(self):
        # Removed from calendar so add to calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.RemoveAlarm(this)

        # Do inherited
        super(VAlarm, self).removed()

    def changed(self):
        # Always force recalc of trigger status
        self.mStatusInit = False

        # Changed in calendar so change in calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.ChangedAlarm(this)

        # Do not do inherited as this is always a sub-component and we do not
        # do top-level component changes
        # super.changed()

    def finalise(self):
        # Do inherited
        super(VAlarm, self).finalise()

        # Get the ACTION
        temp = self.loadValueString(definitions.cICalProperty_ACTION)
        if temp is not None:
            self.mAction = VAlarm.sActionMap.get(
                temp, definitions.eAction_VAlarm_Unknown)
            self.loadAction()

        # Get the trigger
        if self.hasProperty(definitions.cICalProperty_TRIGGER):
            # Determine the type of the value
            temp1 = self.loadValueDateTime(definitions.cICalProperty_TRIGGER)
            temp2 = self.loadValueDuration(definitions.cICalProperty_TRIGGER)
            if temp1 is not None:
                self.mTriggerAbsolute = True
                self.mTriggerOn = temp1
            elif temp2 is not None:
                self.mTriggerAbsolute = False
                self.mTriggerBy = temp2

                # Get the property
                prop = self.findFirstProperty(
                    definitions.cICalProperty_TRIGGER)

                # Look for RELATED parameter
                if prop.hasParameter(definitions.cICalParameter_RELATED):
                    temp = prop.getParameterValue(
                        definitions.cICalParameter_RELATED)
                    if temp == definitions.cICalParameter_RELATED_START:
                        self.mTriggerOnStart = True
                    elif temp == definitions.cICalParameter_RELATED_END:
                        self.mTriggerOnStart = False
                else:
                    self.mTriggerOnStart = True

        # Get repeat & interval
        temp = self.loadValueInteger(definitions.cICalProperty_REPEAT)
        if temp is not None:
            self.mRepeats = temp
        temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
        if temp is not None:
            self.mRepeatInterval = temp

        # Set a map key for sorting
        self.mMapKey = "%s:%s" % (
            self.mAction,
            self.mTriggerOn if self.mTriggerAbsolute else self.mTriggerBy,
        )

        # Alarm status - private to Mulberry
        status = self.loadValueString(
            definitions.cICalProperty_ALARM_X_ALARMSTATUS)
        if status is not None:
            if status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING:
                self.mAlarmStatus = definitions.eAlarm_Status_Pending
            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED:
                self.mAlarmStatus = definitions.eAlarm_Status_Completed
            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED:
                self.mAlarmStatus = definitions.eAlarm_Status_Disabled
            else:
                self.mAlarmStatus = definitions.eAlarm_Status_Pending

        # Last trigger time - private to Mulberry
        temp = self.loadValueDateTime(
            definitions.cICalProperty_ALARM_X_LASTTRIGGER)
        if temp is not None:
            self.mLastTrigger = temp

    def validate(self, doFix=False):
        """
        Validate the data in this component and optionally fix any problems, else raise. If
        loggedProblems is not None it must be a C{list} and problem descriptions are appended
        to that.
        """

        # Validate using action specific constraints
        self.propertyCardinality_1 = self.mActionData.propertyCardinality_1
        self.propertyCardinality_1_Fix_Empty = self.mActionData.propertyCardinality_1_Fix_Empty
        self.propertyCardinality_0_1 = self.mActionData.propertyCardinality_0_1
        self.propertyCardinality_1_More = self.mActionData.propertyCardinality_1_More

        fixed, unfixed = super(VAlarm, self).validate(doFix)

        # Extra constraint: both DURATION and REPEAT must be present togethe
        if self.hasProperty(
                definitions.cICalProperty_DURATION) ^ self.hasProperty(
                    definitions.cICalProperty_REPEAT):
            # Cannot fix this
            logProblem = "[%s] Properties must be present together: %s, %s" % (
                self.getType(),
                definitions.cICalProperty_DURATION,
                definitions.cICalProperty_REPEAT,
            )
            unfixed.append(logProblem)

        return fixed, unfixed

    def editStatus(self, status):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)

        # Updated cached values
        self.mAlarmStatus = status

        # Add new
        status_txt = ""
        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
        self.addProperty(
            Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS,
                     status_txt))

    def editAction(self, action, data):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ACTION)
        self.mActionData.remove(self)
        self.mActionData = None

        # Updated cached values
        self.mAction = action
        self.mActionData = data

        # Add new properties to alarm
        action_txt = VAlarm.sActionValueMap.get(
            self.mAction, definitions.cICalProperty_ACTION_PROCEDURE)

        prop = Property(definitions.cICalProperty_ACTION, action_txt)
        self.addProperty(prop)

        self.mActionData.add(self)

    def editTriggerOn(self, dt):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_TRIGGER)

        # Updated cached values
        self.mTriggerAbsolute = True
        self.mTriggerOn = dt

        # Add new
        prop = Property(definitions.cICalProperty_TRIGGER, dt)
        self.addProperty(prop)

    def editTriggerBy(self, duration, trigger_start):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_TRIGGER)

        # Updated cached values
        self.mTriggerAbsolute = False
        self.mTriggerBy = duration
        self.mTriggerOnStart = trigger_start

        # Add new (with parameter)
        prop = Property(definitions.cICalProperty_TRIGGER, duration)
        attr = Parameter(
            definitions.cICalParameter_RELATED,
            (definitions.cICalParameter_RELATED_START,
             definitions.cICalParameter_RELATED_END)[not trigger_start])
        prop.addParameter(attr)
        self.addProperty(prop)

    def editRepeats(self, repeat, interval):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_REPEAT)
        self.removeProperties(definitions.cICalProperty_DURATION)

        # Updated cached values
        self.mRepeats = repeat
        self.mRepeatInterval = interval

        # Add new
        if self.mRepeats > 0:
            self.addProperty(Property(definitions.cICalProperty_REPEAT,
                                      repeat))
            self.addProperty(
                Property(definitions.cICalProperty_DURATION, interval))

    def getAlarmStatus(self):
        return self.mAlarmStatus

    def getNextTrigger(self, dt):
        if not self.mStatusInit:
            self.initNextTrigger()
        dt.copy(self.mNextTrigger)

    def alarmTriggered(self, dt):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)

        # Updated cached values
        self.mLastTrigger.copy(dt)

        if self.mDoneCount < self.mRepeats:
            self.mNextTrigger = self.mLastTrigger + self.mRepeatInterval
            dt.copy(self.mNextTrigger)
            self.mDoneCount += 1
            self.mAlarmStatus = definitions.eAlarm_Status_Pending
        else:
            self.mAlarmStatus = definitions.eAlarm_Status_Completed

        # Add new
        self.addProperty(
            Property(definitions.cICalProperty_ALARM_X_LASTTRIGGER, dt))
        status = ""
        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
        self.addProperty(
            Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status))

        # Now update dt to the next alarm time
        return self.mAlarmStatus == definitions.eAlarm_Status_Pending

    def loadAction(self):
        # Delete current one
        self.mActionData = None
        self.mActionData = VAlarm.sActionToAlarmMap.get(
            self.mAction, VAlarm.VAlarmUnknown)()
        self.mActionData.load(self)

    def initNextTrigger(self):
        # Do not bother if its completed
        if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            return
        self.mStatusInit = True

        # Look for trigger immediately preceeding or equal to utc now
        nowutc = DateTime.getNowUTC()

        # Init done counter
        self.mDoneCount = 0

        # Determine the first trigger
        trigger = DateTime()
        self.getFirstTrigger(trigger)

        while self.mDoneCount < self.mRepeats:
            # See if next trigger is later than now
            next_trigger = trigger + self.mRepeatInterval
            if next_trigger > nowutc:
                break
            self.mDoneCount += 1
            trigger = next_trigger

        # Check for completion
        if trigger == self.mLastTrigger or (
                nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
            if self.mDoneCount == self.mRepeats:
                self.mAlarmStatus = definitions.eAlarm_Status_Completed
                return
            else:
                trigger = trigger + self.mRepeatInterval
                self.mDoneCount += 1

        self.mNextTrigger = trigger

    def getFirstTrigger(self, dt):
        # If absolute trigger, use that
        if self.isTriggerAbsolute():
            # Get the trigger on
            dt.copy(self.getTriggerOn())
        else:
            # Get the parent embedder class (must be CICalendarComponentRecur type)
            owner = self.getEmbedder()
            if owner is not None:
                # Determine time at which alarm will trigger
                trigger = (owner.getStart(),
                           owner.getEnd())[not self.isTriggerOnStart()]

                # Offset by duration
                dt.copy(trigger + self.getTriggerDuration())
示例#3
0
class VAlarm(Component):

    sActionMap = {
        definitions.cICalProperty_ACTION_AUDIO: definitions.eAction_VAlarm_Audio,
        definitions.cICalProperty_ACTION_DISPLAY: definitions.eAction_VAlarm_Display,
        definitions.cICalProperty_ACTION_EMAIL: definitions.eAction_VAlarm_Email,
        definitions.cICalProperty_ACTION_PROCEDURE: definitions.eAction_VAlarm_Procedure,
        definitions.cICalProperty_ACTION_URI: definitions.eAction_VAlarm_URI,
        definitions.cICalProperty_ACTION_NONE: definitions.eAction_VAlarm_None,
    }

    sActionValueMap = {
        definitions.eAction_VAlarm_Audio: definitions.cICalProperty_ACTION_AUDIO,
        definitions.eAction_VAlarm_Display: definitions.cICalProperty_ACTION_DISPLAY,
        definitions.eAction_VAlarm_Email: definitions.cICalProperty_ACTION_EMAIL,
        definitions.eAction_VAlarm_Procedure: definitions.cICalProperty_ACTION_PROCEDURE,
        definitions.eAction_VAlarm_URI: definitions.cICalProperty_ACTION_URI,
        definitions.eAction_VAlarm_None: definitions.cICalProperty_ACTION_NONE,
    }

    # Classes for each action encapsulating action-specific data
    class VAlarmAction(object):

        propertyCardinality_1 = ()
        propertyCardinality_1_Fix_Empty = ()
        propertyCardinality_0_1 = ()
        propertyCardinality_1_More = ()

        def __init__(self, type):
            self.mType = type

        def duplicate(self):
            return VAlarm.VAlarmAction(self.mType)

        def load(self, valarm):
            pass

        def add(self, valarm):
            pass

        def remove(self, valarm):
            pass

        def getType(self):
            return self.mType


    class VAlarmAudio(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ATTACH,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, speak=None):
            super(VAlarm.VAlarmAudio, self).__init__(type=definitions.eAction_VAlarm_Audio)
            self.mSpeakText = speak

        def duplicate(self):
            return VAlarm.VAlarmAudio(self.mSpeakText)

        def load(self, valarm):
            # Get properties
            self.mSpeakText = valarm.loadValueString(definitions.cICalProperty_ACTION_X_SPEAKTEXT)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_ACTION_X_SPEAKTEXT, self.mSpeakText)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_ACTION_X_SPEAKTEXT)

        def isSpeakText(self):
            return len(self.mSpeakText) != 0

        def getSpeakText(self):
            return self.mSpeakText


    class VAlarmDisplay(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_1_Fix_Empty = (
            definitions.cICalProperty_DESCRIPTION,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, description=None):
            super(VAlarm.VAlarmDisplay, self).__init__(type=definitions.eAction_VAlarm_Display)
            self.mDescription = description

        def duplicate(self):
            return VAlarm.VAlarmDisplay(self.mDescription)

        def load(self, valarm):
            # Get properties
            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_DESCRIPTION, self.mDescription)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)

        def getDescription(self):
            return self.mDescription


    class VAlarmEmail(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_1_Fix_Empty = (
            definitions.cICalProperty_DESCRIPTION,
            definitions.cICalProperty_SUMMARY,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        propertyCardinality_1_More = (
            definitions.cICalProperty_ATTENDEE,
        )

        def __init__(self, description=None, summary=None, attendees=None):
            super(VAlarm.VAlarmEmail, self).__init__(type=definitions.eAction_VAlarm_Email)
            self.mDescription = description
            self.mSummary = summary
            self.mAttendees = attendees

        def duplicate(self):
            return VAlarm.VAlarmEmail(self.mDescription, self.mSummary, self.mAttendees)

        def load(self, valarm):
            # Get properties
            self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
            self.mSummary = valarm.loadValueString(definitions.cICalProperty_SUMMARY)

            self.mAttendees = []
            if valarm.hasProperty(definitions.cICalProperty_ATTENDEE):
                # Get each attendee
                range = valarm.getProperties().get(definitions.cICalProperty_ATTENDEE, ())
                for iter in range:
                    # Get the attendee value
                    attendee = iter.getCalAddressValue()
                    if attendee is not None:
                        self.mAttendees.append(attendee.getValue())

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_DESCRIPTION, self.mDescription)
            valarm.addProperty(prop)

            prop = Property(definitions.cICalProperty_SUMMARY, self.mSummary)
            valarm.addProperty(prop)

            for iter in self.mAttendees:
                prop = Property(definitions.cICalProperty_ATTENDEE, iter, Value.VALUETYPE_CALADDRESS)
                valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
            valarm.removeProperties(definitions.cICalProperty_SUMMARY)
            valarm.removeProperties(definitions.cICalProperty_ATTENDEE)

        def getDescription(self):
            return self.mDescription

        def getSummary(self):
            return self.mSummary

        def getAttendees(self):
            return self.mAttendees


    class VAlarmUnknown(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self):
            super(VAlarm.VAlarmUnknown, self).__init__(type=definitions.eAction_VAlarm_Unknown)

        def duplicate(self):
            return VAlarm.VAlarmUnknown()


    class VAlarmURI(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
            definitions.cICalProperty_TRIGGER,
            definitions.cICalProperty_URL,
        )

        propertyCardinality_0_1 = (
            definitions.cICalProperty_DURATION,
            definitions.cICalProperty_REPEAT,
            definitions.cICalProperty_ACKNOWLEDGED,
        )

        def __init__(self, uri=None):
            super(VAlarm.VAlarmURI, self).__init__(type=definitions.eAction_VAlarm_URI)
            self.mURI = uri

        def duplicate(self):
            return VAlarm.VAlarmURI(self.mURI)

        def load(self, valarm):
            # Get properties
            self.mURI = valarm.loadValueString(definitions.cICalProperty_URL)

        def add(self, valarm):
            # Delete existing then add
            self.remove(valarm)

            prop = Property(definitions.cICalProperty_URL, self.mURI)
            valarm.addProperty(prop)

        def remove(self, valarm):
            valarm.removeProperties(definitions.cICalProperty_URL)

        def getURI(self):
            return self.mURI


    class VAlarmNone(VAlarmAction):

        propertyCardinality_1 = (
            definitions.cICalProperty_ACTION,
        )

        def __init__(self):
            super(VAlarm.VAlarmNone, self).__init__(type=definitions.eAction_VAlarm_None)

        def duplicate(self):
            return VAlarm.VAlarmNone()


    def getMimeComponentName(self):
        # Cannot be sent as a separate MIME object
        return None

    sActionToAlarmMap = {
        definitions.eAction_VAlarm_Audio: VAlarmAudio,
        definitions.eAction_VAlarm_Display: VAlarmDisplay,
        definitions.eAction_VAlarm_Email: VAlarmEmail,
        definitions.eAction_VAlarm_URI: VAlarmURI,
        definitions.eAction_VAlarm_None: VAlarmNone,
    }

    propertyValueChecks = ICALENDAR_VALUE_CHECKS

    def __init__(self, parent=None):

        super(VAlarm, self).__init__(parent=parent)

        self.mAction = definitions.eAction_VAlarm_Display
        self.mTriggerAbsolute = False
        self.mTriggerOnStart = True
        self.mTriggerOn = DateTime()

        # Set duration default to 1 hour
        self.mTriggerBy = Duration()
        self.mTriggerBy.setDuration(60 * 60)

        # Does not repeat by default
        self.mRepeats = 0
        self.mRepeatInterval = Duration()
        self.mRepeatInterval.setDuration(5 * 60) # Five minutes

        # Status
        self.mStatusInit = False
        self.mAlarmStatus = definitions.eAlarm_Status_Pending
        self.mLastTrigger = DateTime()
        self.mNextTrigger = DateTime()
        self.mDoneCount = 0

        # Create action data
        self.mActionData = VAlarm.VAlarmDisplay("")


    def duplicate(self, parent=None):
        other = super(VAlarm, self).duplicate(parent=parent)
        other.mAction = self.mAction
        other.mTriggerAbsolute = self.mTriggerAbsolute
        other.mTriggerOn = self.mTriggerOn.duplicate()
        other.mTriggerBy = self.mTriggerBy.duplicate()
        other.mTriggerOnStart = self.mTriggerOnStart

        other.mRepeats = self.mRepeats
        other.mRepeatInterval = self.mRepeatInterval.duplicate()

        other.mAlarmStatus = self.mAlarmStatus
        if self.mLastTrigger is not None:
            other.mLastTrigger = self.mLastTrigger.duplicate()
        if self.mNextTrigger is not None:
            other.mNextTrigger = self.mNextTrigger.duplicate()
        other.mDoneCount = self.mDoneCount

        other.mActionData = self.mActionData.duplicate()
        return other


    def getType(self):
        return definitions.cICalComponent_VALARM


    def getAction(self):
        return self.mAction


    def getActionData(self):
        return self.mActionData


    def isTriggerAbsolute(self):
        return self.mTriggerAbsolute


    def getTriggerOn(self):
        return self.mTriggerOn


    def getTriggerDuration(self):
        return self.mTriggerBy


    def isTriggerOnStart(self):
        return self.mTriggerOnStart


    def getRepeats(self):
        return self.mRepeats


    def getInterval(self):
        return self.mRepeatInterval


    def added(self):
        # Added to calendar so add to calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.AddAlarm(this)

        # Do inherited
        super(VAlarm, self).added()


    def removed(self):
        # Removed from calendar so add to calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.RemoveAlarm(this)

        # Do inherited
        super(VAlarm, self).removed()


    def changed(self):
        # Always force recalc of trigger status
        self.mStatusInit = False

        # Changed in calendar so change in calendar notifier
        # calstore::CCalendarNotifier::sCalendarNotifier.ChangedAlarm(this)

        # Do not do inherited as this is always a sub-component and we do not
        # do top-level component changes
        # super.changed()


    def finalise(self):
        # Do inherited
        super(VAlarm, self).finalise()

        # Get the ACTION
        temp = self.loadValueString(definitions.cICalProperty_ACTION)
        if temp is not None:
            self.mAction = VAlarm.sActionMap.get(temp, definitions.eAction_VAlarm_Unknown)
            self.loadAction()

        # Get the trigger
        if self.hasProperty(definitions.cICalProperty_TRIGGER):
            # Determine the type of the value
            temp1 = self.loadValueDateTime(definitions.cICalProperty_TRIGGER)
            temp2 = self.loadValueDuration(definitions.cICalProperty_TRIGGER)
            if temp1 is not None:
                self.mTriggerAbsolute = True
                self.mTriggerOn = temp1
            elif temp2 is not None:
                self.mTriggerAbsolute = False
                self.mTriggerBy = temp2

                # Get the property
                prop = self.findFirstProperty(definitions.cICalProperty_TRIGGER)

                # Look for RELATED parameter
                if prop.hasParameter(definitions.cICalParameter_RELATED):
                    temp = prop.getParameterValue(definitions.cICalParameter_RELATED)
                    if temp == definitions.cICalParameter_RELATED_START:
                        self.mTriggerOnStart = True
                    elif temp == definitions.cICalParameter_RELATED_END:
                        self.mTriggerOnStart = False
                else:
                    self.mTriggerOnStart = True

        # Get repeat & interval
        temp = self.loadValueInteger(definitions.cICalProperty_REPEAT)
        if temp is not None:
            self.mRepeats = temp
        temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
        if temp is not None:
            self.mRepeatInterval = temp

        # Set a map key for sorting
        self.mMapKey = "%s:%s" % (self.mAction, self.mTriggerOn if self.mTriggerAbsolute else self.mTriggerBy,)

        # Alarm status - private to Mulberry
        status = self.loadValueString(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
        if status is not None:
            if status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING:
                self.mAlarmStatus = definitions.eAlarm_Status_Pending
            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED:
                self.mAlarmStatus = definitions.eAlarm_Status_Completed
            elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED:
                self.mAlarmStatus = definitions.eAlarm_Status_Disabled
            else:
                self.mAlarmStatus = definitions.eAlarm_Status_Pending

        # Last trigger time - private to Mulberry
        temp = self.loadValueDateTime(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
        if temp is not None:
            self.mLastTrigger = temp


    def validate(self, doFix=False):
        """
        Validate the data in this component and optionally fix any problems, else raise. If
        loggedProblems is not None it must be a C{list} and problem descriptions are appended
        to that.
        """

        # Validate using action specific constraints
        self.propertyCardinality_1 = self.mActionData.propertyCardinality_1
        self.propertyCardinality_1_Fix_Empty = self.mActionData.propertyCardinality_1_Fix_Empty
        self.propertyCardinality_0_1 = self.mActionData.propertyCardinality_0_1
        self.propertyCardinality_1_More = self.mActionData.propertyCardinality_1_More

        fixed, unfixed = super(VAlarm, self).validate(doFix)

        # Extra constraint: both DURATION and REPEAT must be present togethe
        if self.hasProperty(definitions.cICalProperty_DURATION) ^ self.hasProperty(definitions.cICalProperty_REPEAT):
            # Cannot fix this
            logProblem = "[%s] Properties must be present together: %s, %s" % (
                self.getType(),
                definitions.cICalProperty_DURATION,
                definitions.cICalProperty_REPEAT,
            )
            unfixed.append(logProblem)

        return fixed, unfixed


    def editStatus(self, status):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)

        # Updated cached values
        self.mAlarmStatus = status

        # Add new
        status_txt = ""
        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
            status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
        self.addProperty(Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status_txt))


    def editAction(self, action, data):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ACTION)
        self.mActionData.remove(self)
        self.mActionData = None

        # Updated cached values
        self.mAction = action
        self.mActionData = data

        # Add new properties to alarm
        action_txt = VAlarm.sActionValueMap.get(self.mAction, definitions.cICalProperty_ACTION_PROCEDURE)

        prop = Property(definitions.cICalProperty_ACTION, action_txt)
        self.addProperty(prop)

        self.mActionData.add(self)


    def editTriggerOn(self, dt):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_TRIGGER)

        # Updated cached values
        self.mTriggerAbsolute = True
        self.mTriggerOn = dt

        # Add new
        prop = Property(definitions.cICalProperty_TRIGGER, dt)
        self.addProperty(prop)


    def editTriggerBy(self, duration, trigger_start):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_TRIGGER)

        # Updated cached values
        self.mTriggerAbsolute = False
        self.mTriggerBy = duration
        self.mTriggerOnStart = trigger_start

        # Add new (with parameter)
        prop = Property(definitions.cICalProperty_TRIGGER, duration)
        attr = Parameter(
            definitions.cICalParameter_RELATED,
            (
                definitions.cICalParameter_RELATED_START,
                definitions.cICalParameter_RELATED_END
            )[not trigger_start]
        )
        prop.addParameter(attr)
        self.addProperty(prop)


    def editRepeats(self, repeat, interval):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_REPEAT)
        self.removeProperties(definitions.cICalProperty_DURATION)

        # Updated cached values
        self.mRepeats = repeat
        self.mRepeatInterval = interval

        # Add new
        if self.mRepeats > 0:
            self.addProperty(Property(definitions.cICalProperty_REPEAT, repeat))
            self.addProperty(Property(definitions.cICalProperty_DURATION, interval))


    def getAlarmStatus(self):
        return self.mAlarmStatus


    def getNextTrigger(self, dt):
        if not self.mStatusInit:
            self.initNextTrigger()
        dt.copy(self.mNextTrigger)


    def alarmTriggered(self, dt):
        # Remove existing
        self.removeProperties(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
        self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)

        # Updated cached values
        self.mLastTrigger.copy(dt)

        if self.mDoneCount < self.mRepeats:
            self.mNextTrigger = self.mLastTrigger + self.mRepeatInterval
            dt.copy(self.mNextTrigger)
            self.mDoneCount += 1
            self.mAlarmStatus = definitions.eAlarm_Status_Pending
        else:
            self.mAlarmStatus = definitions.eAlarm_Status_Completed

        # Add new
        self.addProperty(Property(definitions.cICalProperty_ALARM_X_LASTTRIGGER, dt))
        status = ""
        if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
        elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
        elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
            status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
        self.addProperty(Property(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status))

        # Now update dt to the next alarm time
        return self.mAlarmStatus == definitions.eAlarm_Status_Pending


    def loadAction(self):
        # Delete current one
        self.mActionData = None
        self.mActionData = VAlarm.sActionToAlarmMap.get(self.mAction, VAlarm.VAlarmUnknown)()
        self.mActionData.load(self)


    def initNextTrigger(self):
        # Do not bother if its completed
        if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
            return
        self.mStatusInit = True

        # Look for trigger immediately preceeding or equal to utc now
        nowutc = DateTime.getNowUTC()

        # Init done counter
        self.mDoneCount = 0

        # Determine the first trigger
        trigger = DateTime()
        self.getFirstTrigger(trigger)

        while self.mDoneCount < self.mRepeats:
            # See if next trigger is later than now
            next_trigger = trigger + self.mRepeatInterval
            if next_trigger > nowutc:
                break
            self.mDoneCount += 1
            trigger = next_trigger

        # Check for completion
        if trigger == self.mLastTrigger or (nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
            if self.mDoneCount == self.mRepeats:
                self.mAlarmStatus = definitions.eAlarm_Status_Completed
                return
            else:
                trigger = trigger + self.mRepeatInterval
                self.mDoneCount += 1

        self.mNextTrigger = trigger


    def getFirstTrigger(self, dt):
        # If absolute trigger, use that
        if self.isTriggerAbsolute():
            # Get the trigger on
            dt.copy(self.getTriggerOn())
        else:
            # Get the parent embedder class (must be CICalendarComponentRecur type)
            owner = self.getEmbedder()
            if owner is not None:
                # Determine time at which alarm will trigger
                trigger = (owner.getStart(), owner.getEnd())[not self.isTriggerOnStart()]

                # Offset by duration
                dt.copy(trigger + self.getTriggerDuration())