Changeset 47410

Show
Ignore:
Timestamp:
07/25/06 15:56:06 (4 years ago)
Author:
ogrisel
Message:

#1708: no more 'NONE' attachement on import + export the document attribute as ATTACH value (in ical)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • Python/CalCore/trunk/CHANGES

    r45829 r47410  
    77Bug fixes: 
    88~~~~~~~~~~ 
    9 
     9- #1708: no more 'NONE' attachement on import + export the document attribute 
     10  as ATTACH value (in ical) 
    1011New internal features: 
    1112~~~~~~~~~~~~~~~~~~~~~~ 
  • Python/CalCore/trunk/src/calcore/cal.py

    r31372 r47410  
    4949    """ 
    5050    implements(IEventSpecification) 
    51      
     51 
    5252    def __init__(self, dtstart, duration, 
    5353                 title='', 
     
    100100                o.setParticipationRole(attendee, role) 
    101101                o._setParticipationStatus(attendee, status) 
    102                  
    103                  
     102 
     103 
    104104class StorageManagerBase: 
    105      
     105 
    106106    implements(IStorageManager) 
    107107 
    108108    def __init__(self): 
    109109        self._storage = None 
    110            
     110 
    111111    # MANIPULATORS 
    112112 
    113113    def setStorage(self, storage): 
    114114        self._storage = storage 
    115          
     115 
    116116    def createEvent(self, unique_id=None, **kw): 
    117117        return self._storage.createEvent(unique_id, EventSpecification(**kw)) 
     
    119119    def createEventFromSpecification(self, unique_id, spec): 
    120120        return self._storage.createEvent(unique_id, spec) 
    121      
     121 
    122122    def deleteEvent(self, event): 
    123123        self._storage.deleteEvent(event) 
    124          
     124 
    125125    # ACCESSORS 
    126126 
     
    134134        except KeyError: 
    135135            return False 
    136          
     136 
    137137    def getEvents(self, period, search_criteria=None): 
    138138        return self._storage.getEvents(period, search_criteria) 
     
    144144        return segmentOccurrences(period, 
    145145                                  self.getOccurrences(period, search_criteria)) 
    146      
     146 
    147147    def getBlockedPeriods(self, attendees, period, time_period): 
    148148        # XXX need to check for events that an attendee is interested 
     
    155155            for occ in self.getOccurrencesSegmented(period, search_criteria): 
    156156                # transparent or canceled events don't count 
    157                 if (occ.original.transparent or  
     157                if (occ.original.transparent or 
    158158                    occ.original.status == 'CANCELED'): 
    159159                    continue 
    160160                # And neither does events you are not going to: 
    161                 if (occ.original.getParticipationStatus(attendee) in  
     161                if (occ.original.getParticipationStatus(attendee) in 
    162162                    ['DECLINED', 'DELEGATED']): 
    163163                    continue 
     
    191191        return util.removeOverlaps(blocked_periods) 
    192192 
    193      
     193 
    194194    def getFreePeriods(self, attendees, period, time_period, 
    195195                     minimal_duration=None): 
     
    204204        if ends > combine(ends.date(), time_ends): 
    205205            ends = combine(ends.date(), time_ends) 
    206          
     206 
    207207        last_block_begins = begins 
    208208        for dtstart, dtend in blocked_periods: 
     
    217217                free_periods.append((last_block_begins, ends)) 
    218218        return free_periods 
    219      
     219 
    220220class StorageManager(StorageManagerBase): 
    221221    pass 
    222      
     222 
    223223class StorageBase: 
    224224    """Minimalistic implementation of a storage. 
    225225    """ 
    226226    implements(IStorage) 
    227      
     227 
    228228    def __init__(self, storage_id, hostname=None): 
    229229        self._storage_id = storage_id 
    230230        self._events = self._initEvents() 
    231231        self._hostname = hostname or socket.getfqdn() 
    232          
     232 
    233233    def _initEvents(self): 
    234234        raise NotImplementedError 
     
    236236    def _eventFactory(self, event_id, spec): 
    237237        raise NotImplementedError 
    238                                    
     238 
    239239    # MANIPULATORS 
    240240    def createEvent(self, unique_id, spec): 
     
    246246        self._events[unique_id] = event 
    247247        return event 
    248      
     248 
    249249    def deleteEvent(self, event): 
    250250        del self._events[event.unique_id] 
    251      
     251 
    252252    # ACCESSORS 
    253253 
     
    257257    def getEvent(self, event_id): 
    258258        return self._events[event_id] 
    259              
     259 
    260260    def getEvents(self, period, search_criteria): 
    261261        events = self._getMatchingEvents(search_criteria) 
     
    291291class SearchCriteria: 
    292292    implements(ISearchCriteria) 
    293      
     293 
    294294    def __init__(self, 
    295295                 attendees=None, 
     
    298298                 categories=None, 
    299299                 organizer=None): 
    300         if (attendees is not None and  
     300        if (attendees is not None and 
    301301           not isinstance(attendees,(ListType, TupleType))): 
    302302            attendees = [attendees] 
     
    309309    def clone(self, attendees=None, participation_status=None, 
    310310              participation_role=None, categories=None, organizer=None): 
    311         if (attendees is not None and  
     311        if (attendees is not None and 
    312312           not isinstance(attendees,(ListType, TupleType))): 
    313313            attendees = [attendees] 
     
    337337        if self.attendees is None: 
    338338            return True 
    339          
     339 
    340340        for attendee in self.attendees: 
    341341            if not event.hasAttendee(attendee): 
    342342                continue 
    343              
     343 
    344344            if (self.participation_status is not None and 
    345345                self.participation_status != 
    346346                event.getParticipationStatus(attendee)): 
    347347                continue 
    348                          
     348 
    349349            if (self.participation_role is not None and 
    350350                self.participation_role != 
    351351                event.getParticipationRole(attendee)): 
    352352                continue 
    353          
     353 
    354354            #This attendee matched 
    355355            return True 
    356          
     356 
    357357        # No attendee matched 
    358358        return False 
     
    362362    def __init__(self): 
    363363        SearchCriteria.__init__(self) 
    364      
     364 
    365365class EventBase: 
    366366    implements(IInvitableCalendarEvent) 
    367      
     367 
    368368    def __init__(self, unique_id, spec): 
    369369        self.unique_id = unique_id 
     
    372372 
    373373        spec.setOnObject(self) 
    374          
     374 
    375375        if self.allday: 
    376376            self.alldayAdjust() 
    377              
     377 
    378378    def _initParticipationState(self): 
    379379        raise NotImplementedError 
     
    390390                self.setParticipationRole(attendee, 'REQ-PARTICIPANT') 
    391391                attendee.on_invite(self) 
    392          
     392 
    393393    def setParticipationStatus(self, attendee, status): 
    394394        assert status in [None, 'NEEDS-ACTION', 'ACCEPTED', 'DECLINED', 
     
    426426                days += 1 
    427427            self.duration = timedelta(days=days) 
    428          
     428 
    429429    # ACCESSORS 
    430      
     430 
    431431    def getOrganizerId(self): 
    432432        return self._organizer_id 
    433          
     433 
    434434    def inCategory(self, category): 
    435435        return category in self.categories 
    436      
     436 
    437437    def getAttendeeIds(self, 
    438438                       participation_status=None, participation_role=None): 
     
    458458        return self._participation_role.get( 
    459459            attendee.getAttendeeId()) 
    460      
     460 
    461461    def __hash__(self): 
    462462        return hash(self.unique_id) 
    463      
     463 
    464464    def __cmp__(self, other): 
    465465        # sort first on start datetime, then on public id 
     
    469469            (self.dtstart, self.unique_id), 
    470470            (other.dtstart, self.unique_id)) 
    471      
     471 
    472472    def expand(self, period): 
    473473        """Returns ICalendarOccurrences for this event in period.""" 
     
    491491class Timed: 
    492492    implements(ITimed) 
    493      
     493 
    494494    def __init__(self, dtstart, duration): 
    495495        self.dtstart = dtstart 
    496496        self.duration = duration 
    497          
     497 
    498498class Event(EventBase): 
    499499    def _initParticipationState(self): 
     
    513513class AttendeeBase: 
    514514    implements(IAttendee) 
    515      
     515 
    516516    def __init__(self, attendee_id, name, attendee_type, on_invite): 
    517517        self._attendee_id = attendee_id 
     
    519519        self._attendee_type = attendee_type 
    520520        self._on_invite = on_invite 
    521      
     521 
    522522    # MANIPULATORS 
    523523 
     
    525525        kw['organizer'] = self 
    526526        return self._getStorageManager().createEvent(unique_id=None, **kw) 
    527      
     527 
    528528    def on_invite(self, event): 
    529529        if self._on_invite is not None: 
     
    532532    def on_status_change(self, event, from_status, to_status): 
    533533        pass 
    534              
     534 
    535535    # ACCESSORS 
    536536 
     
    540540    def getAttendeeType(self): 
    541541        return self._attendee_type 
    542      
     542 
    543543    def getEvents(self, period, search_criteria=None): 
    544544        if search_criteria is None: 
     
    556556        return self._getStorageManager().getOccurrences( 
    557557            period, search_criteria) 
    558      
     558 
    559559    def getOrganizedEvents(self, search_criteria=None): 
    560560        if search_criteria is None: 
     
    567567    def _getStorageManager(self): 
    568568        raise NotImplementedError 
    569      
     569 
    570570class Attendee(AttendeeBase): 
    571571    def __init__(self, storage_manager, attendee_id, 
     
    574574            self, attendee_id, name, attendee_type, on_invite) 
    575575        self._storage_manager = storage_manager 
    576      
     576 
    577577    def _getStorageManager(self): 
    578578        return self._storage_manager 
     
    582582class CalendarBase: 
    583583    implements(ICalendar) 
    584      
     584 
    585585    def __init__(self): 
    586586        self._attendees = self._initAttendees() 
     
    590590 
    591591    # MANIPULATORS 
    592      
     592 
    593593    def addAttendee(self, attendee): 
    594594        self._attendees[attendee.getAttendeeId()] = None 
     
    603603        source = self._getAttendeeSource() 
    604604        return source.getAttendee(attendee_id) 
    605          
    606     def import_(self, text, period=(None, None), search_criteria=None,  
     605 
     606    def import_(self, text, period=(None, None), search_criteria=None, 
    607607                synchronize=0): 
    608608        """Given iCalendar text, import events. 
    609609 
    610610        This overwrites existing event data where necessary, 
    611         and creates new events. If synchronize is set to 1,  
    612         it also removes existing events. This is used when the  
    613         iCalendar client is assumed to have retrieved the calendar  
     611        and creates new events. If synchronize is set to 1, 
     612        it also removes existing events. This is used when the 
     613        iCalendar client is assumed to have retrieved the calendar 
    614614        first, as when you are using it via WebDAV. 
    615615        """ 
     
    651651        spec = self._importEventSpecification(e) 
    652652        spec.setOnObject(event) 
    653          
     653 
    654654##         # XXX this (like the edit forms) implicitly updates events 
    655655##         # this may not work for non ZODB storages 
     
    667667##         else: 
    668668##             event.recurrence = None 
    669     
     669 
    670670    def _importNewEvent(self, uid, e): 
    671671        m = self._getStorageManager() 
     
    677677        """ 
    678678        asrc = self._getAttendeeSource() 
    679          
     679 
    680680        kw = {} 
    681681        kw['dtstart'], kw['duration'], kw['allday'] =\ 
    682                        self._getDtstartDuration(e)             
     682                       self._getDtstartDuration(e) 
    683683 
    684684        kw['title'] = e.decoded('SUMMARY', '') 
     
    692692        else: 
    693693            organizer = None 
    694              
     694 
    695695        if organizer is None: 
    696696            organizer = self.getMainAttendee() 
    697697 
    698698        kw['organizer'] = organizer 
    699          
    700          
     699 
     700 
    701701        if e.has_key('ATTENDEE'): 
    702702            attendees = [] 
     
    710710                attendees.append((attendee, role, status)) 
    711711            kw['attendees'] = attendees 
    712              
     712 
    713713        kw['categories'] = self._getCategories(e) 
    714714        kw['transparent'] = e.decoded('TRANSP', 'OPAQUE') == 'TRANSPARENT' 
    715715        kw['access'] = e.decoded('CLASS', 'PUBLIC') 
    716         kw['document'] = e.decoded('ATTACH', 'NONE'
     716        kw['document'] = e.decoded('ATTACH', None
    717717        rrule = e.decoded('RRULE', None) 
    718718        if rrule is not None: 
     
    721721            kw['recurrence'] = None 
    722722        return EventSpecification(**kw) 
    723          
     723 
    724724    def _deleteEvent(self, uid): 
    725725        self._getStorageManager().deleteEvent(uid) 
    726      
     726 
    727727    def _getDtstartDuration(self, component): 
    728728        dtstart = component.decoded('DTSTART', None) 
     
    807807                categories = Set(categories) 
    808808        return categories 
    809   
     809 
    810810    def _repairText(self, text): 
    811811        # repair text to have \r\n if necessary 
     
    816816                text = '\r\n'.join(lines) 
    817817        return text 
    818      
     818 
    819819    # ACCESSORS 
    820      
     820 
    821821    def getEvent(self, event_id): 
    822822        # XXX should check whether event applies to any of the 
     
    827827    def hasEvent(self, event_id): 
    828828        return self._getStorageManager().hasEvent(event_id) 
    829      
     829 
    830830    def getEvents(self, period, search_criteria=None): 
    831831        result = {} 
     
    845845            search_criteria  = SearchCriteria(attendees=self.getAttendees()) 
    846846        return self._getStorageManager().getOccurrences(period, search_criteria) 
    847      
     847 
    848848    def getOccurrencesSegmented(self, period, search_criteria=None): 
    849849        # first get occurrences 
     
    852852        # borders (day and start and end of period) 
    853853        return segmentOccurrences(period, occurrences) 
    854      
     854 
    855855    def getEventsInDay(self, date, search_criteria=None): 
    856856        start = datetime(date.year, date.month, date.day) 
     
    858858        period = (start, end) 
    859859        return self.getEvents(period, search_criteria) 
    860      
     860 
    861861    def getOccurrencesInDay(self, date, search_criteria=None): 
    862862        """Returns all occurrences in day. 
     
    879879    def getCurrentYear(self): 
    880880        return self.getToday().year 
    881      
     881 
    882882    def getToday(self): 
    883883        return datetime.today() 
     
    929929                e.set_inline('categories', list(event.categories)) 
    930930            e.add('class', event.access) 
     931            if event.document: 
     932                e.add('attach', event.document) 
    931933            if event.recurrence is not None: 
    932934                r = event.recurrence 
     
    947949    def _getAttendeeSource(self): 
    948950        raise NotImplementedError 
    949      
     951 
    950952class Calendar(CalendarBase): 
    951953    def __init__(self, storage_manager, attendee_source): 
     
    953955        self._storage_manager = storage_manager 
    954956        self._attendee_source = attendee_source 
    955          
     957 
    956958    def _getStorageManager(self): 
    957959        return self._storage_manager 
     
    966968    """ 
    967969    implements(IAttendeeSource) 
    968      
     970 
    969971    def __init__(self, storage_manager): 
    970972        self._storage_manager = storage_manager 
     
    972974 
    973975    # MANIPULATORS 
    974      
     976 
    975977    def createIndividual(self, attendee_id, name): 
    976978        attendee = Attendee( 
     
    979981        self._attendees[attendee_id] = attendee 
    980982        return attendee 
    981      
     983 
    982984    def createRoom(self, attendee_id, name, on_invite=None): 
    983985        attendee = Attendee( 
     
    988990 
    989991    # ACCESSORS 
    990      
     992 
    991993    def getAttendee(self, attendee_id): 
    992994        return self._attendees[attendee_id] 
     
    995997        # there is no concept of a current user at this level 
    996998        return None 
    997      
     999 
    9981000    def findByName(self, query_str, attendee_type=None): 
    9991001        result = [] 
     
    10061008            result.append(attendee) 
    10071009        return result 
    1008      
     1010 
    10091011    def getAttendeesOfType(self, attendee_type): 
    10101012        result = [] 
     
    10241026def acceptIfPossible(attendee, event): 
    10251027    # always accept for now 
    1026     event.setParticipationStatus(attendee, 'ACCEPTED')     
     1028    event.setParticipationStatus(attendee, 'ACCEPTED') 
    10271029 
    10281030def Period(dtstart, duration): 
     
    10661068    assert period[0] is not None, "Period is unbounded (into the past)" 
    10671069    assert period[1] is not None, "Period is unbounded (into the future)" 
    1068      
     1070 
    10691071def addrspec_unique_id(unique_maker, hostname): 
    10701072    result = iso8601(datetime.now()) 
  • Python/CalCore/trunk/src/calcore/tests/test_ical.py

    r23746 r47410  
    1414 
    1515        self._calendar.addAttendee(martijn) 
    16          
     16 
    1717        meeting = martijn.createEvent( 
    1818            dtstart=datetime(2005, 4, 10, 16, 00), 
     
    2727            title='Another meeting', 
    2828            location='Room 2', 
     29            document='/path/to/some/document', 
    2930            categories=Set(['Public Holiday', 
    3031                            'Wonderful Event'])) 
     
    3233        self._meeting_uid = meeting.unique_id 
    3334        self._meeting2_uid = meeting2.unique_id 
    34      
     35 
    3536    def test_export_import1(self): 
    3637        # export the calendar to iCalendar text 
    3738        text = self._calendar.export() 
    38         # now we import the text again.  
     39        # now we import the text again. 
    3940        self._calendar.import_(text) 
    4041 
    4142        # nothing should be changed! 
    42          
     43 
    4344        # we need to reconnect to the events 
    4445        meeting = self._calendar.getEvent(self._meeting_uid) 
    4546        meeting2 = self._calendar.getEvent(self._meeting2_uid) 
    46          
     47 
    4748        self.assertEquals( 
    4849            "Martijn's Meeting", 
     
    6061            Set(['Public Holiday', 'Wonderful Event']), 
    6162            meeting2.categories) 
    62          
     63        self.assertEquals(None, meeting.document) 
     64        self.assertEquals('/path/to/some/document', meeting2.document) 
     65 
    6366    def test_export_import2(self): 
    6467        # export the calendar to iCalendar text 
    6568        text = self._calendar.export() 
    6669        # change a summary in the text 
    67         text = text.replace("Martijn's Meeting", "Foo's Meeting")  
    68         # now we import the text again.  
     70        text = text.replace("Martijn's Meeting", "Foo's Meeting") 
     71        # now we import the text again. 
    6972        self._calendar.import_(text) 
    7073        meeting = self._calendar.getEvent(self._meeting_uid) 
     
    8487        text = text.replace('DTSTART:20050411T170000', 
    8588                            'DTSTART:20050412T180000') 
    86         # now we import the text again.  
     89        # now we import the text again. 
    8790        self._calendar.import_(text) 
    8891        meeting = self._calendar.getEvent(self._meeting_uid) 
     
    121124            timedelta(hours=1), 
    122125            meeting3.duration) 
    123      
     126 
    124127    def test_export_import_remove_event(self): 
    125128        # export the calendar to iCalendar text 
     
    157160        self.assertEquals( 
    158161            timedelta(minutes=75), 
    159             meeting2.duration)     
     162            meeting2.duration) 
    160163 
    161164    def test_export_import_recurrence(self): 
     
    172175RRULE:FREQ=DAILY;INTERVAL=1 
    173176END:VEVENT""" 
    174         text = insert_event_textually(text, new_event)      
     177        text = insert_event_textually(text, new_event) 
    175178        # now import again 
    176179        self._calendar.import_(text) 
     
    244247STATUS:CONFIRMED 
    245248END:VEVENT""" 
    246         text = insert_event_textually(text, new_event)      
     249        text = insert_event_textually(text, new_event) 
    247250        # now import again 
    248251        self._calendar.import_(text) 
     
    274277CATEGORIES:HOLIDAY,MISC 
    275278END:VEVENT""" 
    276         text = insert_event_textually(text, new_event)         
     279        text = insert_event_textually(text, new_event) 
    277280        # now import again 
    278281        self._calendar.import_(text) 
     
    299302CATEGORIES:MISC 
    300303END:VEVENT""" 
    301         text = insert_event_textually(text, new_event)         
     304        text = insert_event_textually(text, new_event) 
    302305        # now import again 
    303306        self._calendar.import_(text) 
    304307        self.assertEquals( 
    305308            Set(['HOLIDAY', 'MISC']), 
    306             self._calendar.getEvent('hoi').categories)       
     309            self._calendar.getEvent('hoi').categories) 
    307310 
    308311    def test_categories_existing_event(self): 
     
    314317        self.assertEquals( 
    315318            Set(['HOLIDAY', 'MISC']), 
    316             self._calendar.getEvent(self._meeting_uid).categories)   
     319            self._calendar.getEvent(self._meeting_uid).categories) 
    317320 
    318321    def test_categories_existing_event_different(self): 
     
    355358TRANSP:TRANSPARENT 
    356359END:VEVENT""" 
    357         text = insert_event_textually(text, new_event)         
     360        text = insert_event_textually(text, new_event) 
    358361        # now import again 
    359362        self._calendar.import_(text) 
     
    389392            'PRIVATE', 
    390393            self._calendar.getEvent(self._meeting_uid).access) 
    391          
     394 
    392395    def test_access_change_confidential(self): 
    393396        text = self._calendar.export() 
     
    397400            'CONFIDENTIAL', 
    398401            self._calendar.getEvent(self._meeting_uid).access) 
    399          
     402 
    400403class RecurrentImportExportTestCase(unittest.TestCase): 
    401404    def setUp(self): 
     
    421424RRULE:FREQ=DAILY;INTERVAL=1 
    422425END:VEVENT""" 
    423         text = insert_event_textually(text, new_event)      
     426        text = insert_event_textually(text, new_event) 
    424427        self._calendar.import_(text) 
    425428        occurrences = self._calendar.getOccurrences( 
     
    440443RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20050406 
    441444END:VEVENT""" 
    442         text = insert_event_textually(text, new_event)      
     445        text = insert_event_textually(text, new_event) 
    443446        self._calendar.import_(text) 
    444447        occurrences = self._calendar.getOccurrences( 
     
    459462RRULE:FREQ=DAILY;INTERVAL=1;COUNT=4 
    460463END:VEVENT""" 
    461         text = insert_event_textually(text, new_event)      
     464        text = insert_event_textually(text, new_event) 
    462465        self._calendar.import_(text) 
    463466        occurrences = self._calendar.getOccurrences( 
     
    478481RRULE:FREQ=YEARLY;INTERVAL=1 
    479482END:VEVENT""" 
    480         text = insert_event_textually(text, new_event)      
     483        text = insert_event_textually(text, new_event) 
    481484        self._calendar.import_(text) 
    482485        occurrences = self._calendar.getOccurrences( 
     
    497500RRULE:FREQ=WEEKLY;INTERVAL=1 
    498501END:VEVENT""" 
    499         text = insert_event_textually(text, new_event)      
    500         self._calendar.import_(text)    
     502        text = insert_event_textually(text, new_event) 
     503        self._calendar.import_(text) 
    501504        occurrences = self._calendar.getOccurrences( 
    502505            (datetime(2005, 3, 1), 
     
    522525RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,FR 
    523526END:VEVENT""" 
    524         text = insert_event_textually(text, new_event)      
    525         self._calendar.import_(text)    
     527        text = insert_event_textually(text, new_event) 
     528        self._calendar.import_(text) 
    526529        occurrences = self._calendar.getOccurrences( 
    527530            (datetime(2005, 3, 1), 
     
    538541        self.assertEquals(datetime(2005, 4, 25, 16), occurrences[7].dtstart) 
    539542        self.assertEquals(datetime(2005, 4, 29, 16), occurrences[8].dtstart) 
    540           
     543 
    541544    def test_monthly_recurrence_simple(self): 
    542545        text = self._calendar.export() 
     
    551554RRULE:FREQ=MONTHLY;INTERVAL=1 
    552555END:VEVENT""" 
    553         text = insert_event_textually(text, new_event)      
     556        text = insert_event_textually(text, new_event) 
    554557        self._calendar.import_(text) 
    555558        occurrences = self._calendar.getOccurrences( 
     
    576579        # the start of the month, or the end of the month if the 
    577580        # BYDAY starts with -. 
    578         text = insert_event_textually(text, new_event)      
     581        text = insert_event_textually(text, new_event) 
    579582        self._calendar.import_(text) 
    580583        occurrences = self._calendar.getOccurrences( 
     
    608611        # original event is used instead, starting to count from 
    609612        # the end the month 
    610         text = insert_event_textually(text, new_event)      
     613        text = insert_event_textually(text, new_event) 
    611614        self._calendar.import_(text) 
    612615        occurrences = self._calendar.getOccurrences( 
     
    634637UID:hoi 
    635638END:VEVENT""" 
    636         text = insert_event_textually(text, new_event)      
     639        text = insert_event_textually(text, new_event) 
    637640        self._calendar.import_(text) 
    638641        occurrences = self._calendar.getOccurrences( 
     
    646649            timedelta(1), 
    647650            occurrences[0].duration) 
    648          
     651 
    649652    def test_no_duration_no_dtend_datetime(self): 
    650653        # when dtstart is a DATETIME, and dtend is absent, dtend is same 
     
    658661UID:hoi 
    659662END:VEVENT""" 
    660         text = insert_event_textually(text, new_event)      
     663        text = insert_event_textually(text, new_event) 
    661664        self._calendar.import_(text) 
    662665        occurrences = self._calendar.getOccurrences( 
     
    670673            timedelta(0), 
    671674            occurrences[0].duration) 
    672          
     675 
    673676class AllDayImportExportTestCase(unittest.TestCase): 
    674677    def setUp(self): 
     
    721724        self.assertEquals(timedelta(days=1), 
    722725                          event.duration) 
    723          
     726 
    724727    def test_allday_export_two_days(self): 
    725728        meeting = self._martijn.createEvent( 
     
    742745        self.assertEquals(timedelta(days=2), 
    743746                          event.duration) 
    744      
     747 
    745748def insert_event_textually(text, event_text): 
    746749    # correct newline story 
     
    751754    # insert just before it 
    752755    result = text[:i] + event_text + text[i:] 
    753     return result     
     756    return result 
    754757 
    755758def insert_lines_textually(text, after, lines): 
     
    761764    return result 
    762765 
    763          
     766 
    764767def test_suite(): 
    765768    suite = unittest.TestSuite()