source: indico/indico/MaKaC/schedule.py @ 4e85d60

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.99v1.0v1.1
Last change on this file since 4e85d60 was 4e85d60, checked in by Jose Benito <jose.benito.gonzalez@…>, 17 months ago

[IMP] Do not show TT in weekends if no entries

  • If no entries are in the weekends do not add to the dictionary.
  • Property mode set to 100644
File size: 57.1 KB
Line 
1# -*- coding: utf-8 -*-
2##
3##
4## This file is part of CDS Indico.
5## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
6##
7## CDS Indico is free software; you can redistribute it and/or
8## modify it under the terms of the GNU General Public License as
9## published by the Free Software Foundation; either version 2 of the
10## License, or (at your option) any later version.
11##
12## CDS Indico is distributed in the hope that it will be useful, but
13## WITHOUT ANY WARRANTY; without even the implied warranty of
14## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15## General Public License for more details.
16##
17## You should have received a copy of the GNU General Public License
18## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
19## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20
21"""
22"""
23import copy
24from persistent import Persistent
25from datetime import datetime,timedelta
26from MaKaC.common.Counter import Counter
27from MaKaC.errors import MaKaCError, TimingError, ParentTimingError,\
28    EntryTimingError
29from MaKaC.common import utils
30from MaKaC.trashCan import TrashCanManager
31from MaKaC.i18n import _
32from pytz import timezone
33from MaKaC.common.utils import daysBetween
34from MaKaC.common.Conversion import Conversion
35from MaKaC.common.contextManager import ContextManager
36from MaKaC.common.fossilize import Fossilizable, fossilizes
37from MaKaC.fossils.schedule import IContribSchEntryDisplayFossil,\
38        IContribSchEntryMgmtFossil, IBreakTimeSchEntryFossil,\
39        IBreakTimeSchEntryMgmtFossil,\
40        ILinkedTimeSchEntryDisplayFossil, ILinkedTimeSchEntryMgmtFossil
41
42class Schedule:
43    """base schedule class. Do NOT instantiate
44    """
45
46    def __init__( self, owner ):
47        pass
48
49    def getEntries( self ):
50        return []
51
52    def addEntry( self, entry, position=None ):
53        return
54
55    def removeEntry( self, entry ):
56        return
57
58    def getEntryPosition( self, entry ):
59        return
60
61    def moveEntry( self, entry, newPosition, after=1 ):
62        return
63
64    def getEntryLocator( self, entry ):
65        return
66
67    def getOwner( self ):
68        return
69
70    def reSchedule( self ):
71        return
72
73    def getEntryInPos( self, pos ):
74        return None
75
76
77class TimeSchedule(Schedule, Persistent):
78    """
79    """
80
81    def __init__(self,owner):
82        self._entries=[]
83        self._owner=owner
84        self._entryGen=Counter()
85        self._allowParallel=True
86
87    def notifyModification(self):
88        self.getOwner().notifyModification()
89
90    def getEntries( self ):
91        return self._entries
92
93    def hasEntriesBefore(self,d):
94        """Tells wether there is any entry before the specified date
95        """
96        entries=self.getEntries()
97        if len(entries)==0:
98            return False
99        return entries[0].getStartDate()<d
100
101    def hasEntriesAfter(self,d):
102        """Tells wether there is any entry after the specified date
103        """
104        entries=self.getEntries()
105        if len(entries)==0:
106            return False
107        return self.calculateEndDate()>d
108
109    def checkSanity( self ):
110        if self.hasEntriesBefore(self.getStartDate()) or self.hasEntriesAfter(self.getEndDate()):
111            raise TimingError("Sorry, cannot perform this date change as some entries in the timetable would fall outside the new dates.")
112
113    def isOutside(self,entry):
114        """Tells whether an entry is outside the date boundaries of the schedule
115        """
116        ######################################
117        # Fermi timezone awareness           #
118        ######################################
119        if entry.getStartDate() is not None:
120            if entry.getStartDate()<self.getStartDate('UTC') or \
121                    entry.getStartDate()>self.getEndDate('UTC'):
122                return True
123        if entry.getEndDate() is not None:
124            if entry.getEndDate()<self.getStartDate('UTC') or \
125                    entry.getEndDate()>self.getEndDate('UTC'):
126                return True
127        return False
128
129    def hasEntry(self,entry):
130        return entry.isScheduled() and entry.getSchedule()==self and\
131            entry in self._entries
132
133    def _addEntry(self,entry,check=2):
134        """check parameter:
135            0: no check at all
136            1: check and raise error in case of problem
137            2: check and adapt the owner dates"""
138        if entry.isScheduled():
139            # remove it from the old schedule and add it to this one
140            entry.getSchedule().removeEntry(entry)
141
142        owner = self.getOwner()
143
144        tz = owner.getConference().getTimezone()
145
146        # If user has entered start date use these dates
147        # if the entry has not a pre-defined start date we try to find a place
148        # within the schedule to allocate it
149        if entry.getStartDate() is None:
150            sDate=self.findFirstFreeSlot(entry.getDuration())
151            if sDate is None:
152                if check==2:
153                    newEndDate = self.getEndDate() + entry.getDuration()
154
155                    ContextManager.get('autoOps').append((owner,
156                                                          "OWNER_END_DATE_EXTENDED",
157                                                          owner,
158                                                          newEndDate.astimezone(timezone(tz))))
159
160                    owner.setEndDate(newEndDate, check)
161                    sDate = self.findFirstFreeSlot(entry.getDuration())
162                if sDate is None:
163                    raise ParentTimingError( _("There is not enough time found to add this entry in the schedule (duration: %s)")%entry.getDuration(), _("Add Entry"))
164            entry.setStartDate(sDate)
165        #if the entry has a pre-defined start date we must make sure that it is
166        #   not outside the boundaries of the schedule
167        else:
168            if entry.getStartDate() < self.getStartDate('UTC'):
169                if check==1:
170                    raise TimingError( _("Cannot schedule this entry because its start date (%s) is before its parents (%s)")%(entry.getAdjustedStartDate(),self.getAdjustedStartDate()),_("Add Entry"))
171                elif check == 2:
172                    ContextManager.get('autoOps').append((owner,
173                                                          "OWNER_START_DATE_EXTENDED",
174                                                          owner,
175                                                          entry.getAdjustedStartDate(tz=tz)))
176                    owner.setStartDate(entry.getStartDate(),check)
177            elif entry.getEndDate()>self.getEndDate('UTC'):
178                if check==1:
179                    raise TimingError( _("Cannot schedule this entry because its end date (%s) is after its parents (%s)")%(entry.getAdjustedEndDate(),self.getAdjustedEndDate()),_("Add Entry"))
180                elif check == 2:
181                    ContextManager.get('autoOps').append((owner,
182                                                          "OWNER_END_DATE_EXTENDED",
183                                                          owner,
184                                                          entry.getAdjustedEndDate(tz=tz)))
185                    owner.setEndDate(entry.getEndDate(),check)
186        #we make sure the entry end date does not go outside the schedule
187        #   boundaries
188        if entry.getEndDate() is not None and \
189                (entry.getEndDate()<self.getStartDate('UTC') or \
190                entry.getEndDate()>self.getEndDate('UTC')):
191            raise TimingError( _("Cannot schedule this entry because its end date (%s) is after its parents (%s)")%(entry.getAdjustedEndDate(),self.getAdjustedEndDate()), _("Add Entry"))
192        self._entries.append(entry)
193        entry.setSchedule(self,self._getNewEntryId())
194        self.reSchedule()
195        self._p_changed = 1
196
197    def _setEntryDuration(self,entry):
198        if entry.getDuration() is None:
199            entry.setDuration(0,5)
200
201    def addEntry(self,entry):
202        if (entry is None) or self.hasEntry(entry):
203            return
204        self._setEntryDuration(entry)
205        result = self._addEntry(entry)
206        self._p_changed = 1
207        return result
208
209    def _removeEntry(self,entry):
210        self._entries.remove(entry)
211        entry.setSchedule(None,"")
212        entry.setStartDate(None)
213        entry.delete()
214        self._p_changed = 1
215
216    def removeEntry(self,entry):
217        if entry is None or not self.hasEntry(entry):
218            return
219        self._removeEntry(entry)
220
221    def getEntryPosition( self, entry ):
222        return self._entries.index( entry )
223
224    def getOwner( self ):
225        return self._owner
226
227    ####################################
228    # Fermi timezone awareness         #
229    ####################################
230
231    def getStartDate( self ,tz='UTC'):
232        return self.getOwner().getAdjustedStartDate(tz)
233
234    def getAdjustedStartDate( self, tz=None ):
235        return self.getOwner().getAdjustedStartDate(tz)
236
237    def getEndDate( self, tz='UTC'):
238        return self.getOwner().getAdjustedEndDate(tz)
239
240    def getAdjustedEndDate( self, tz=None):
241        return self.getOwner().getAdjustedEndDate(tz)
242
243    ####################################
244    # Fermi timezone awareness(end)    #
245    ####################################
246
247    def cmpEntries(self,e1,e2):
248        datePrecedence = cmp(e1.getStartDate(), e2.getStartDate())
249
250        # if we're tied, let's use duration as a criterion
251        # this keeps zero-duration breaks from making it get stuck
252        if datePrecedence == 0:
253            return cmp(e1.getDuration(), e2.getDuration())
254        else:
255            return datePrecedence
256
257    def reSchedule(self):
258        try:
259            if self._allowParalell:
260                pass
261        except AttributeError:
262            self._allowParallel=True
263        self._entries.sort(self.cmpEntries)
264        lastEntry=None
265        for entry in self._entries:
266            if lastEntry is not None:
267                if not self._allowParallel:
268                    if lastEntry.collides(entry):
269                        entry.setStartDate(lastEntry.getEndDate())
270            lastEntry=entry
271        self._p_changed = 1
272
273    def calculateEndDate( self ):
274        if len(self._entries) == 0:
275            return self.getStartDate()
276        eDate = self.getStartDate()
277        for entry in self._entries:
278            if entry.getEndDate()>eDate:
279                eDate = entry.getEndDate()
280        return eDate
281
282    def calculateStartDate( self ):
283        if len(self._entries) == 0:
284            return self.getStartDate()
285        else:
286            return self._entries[0].getStartDate()
287
288    def getTimezone( self ):
289        return self.getOwner().getConference().getTimezone()
290
291    def getFirstFreeSlotOnDay(self,day):
292        if not day.tzinfo:
293            day = timezone(self.getTimezone()).localize(day)
294        tz = day.tzinfo
295        entries = self.getEntriesOnDay(day)
296        if len(entries)==0:
297            if self.getStartDate().astimezone(tz).date() == day.date():
298                return self.getStartDate().astimezone(tz)
299            return day.astimezone(timezone(self.getTimezone())).replace(hour=8,minute=0).astimezone(tz)
300        else:
301            return self.calculateDayEndDate(day)
302
303    def calculateDayEndDate(self,day,hour=0,min=0):
304        if day is None:
305            return self.calculateEndDate()
306        if not day.tzinfo:
307            day = timezone(self.getTimezone()).localize(day)
308        tz = day.tzinfo
309        maxDate=day.replace(hour=hour,minute=min)
310        entries = self.getEntriesOnDay(day)
311        if hour != 0 or min != 0:
312            return maxDate
313        elif len(entries)==0:
314            confstime = self.getOwner().getAdjustedStartDate()
315            return day.astimezone(timezone(self.getTimezone())).replace(hour=confstime.hour,minute=confstime.minute).astimezone(tz)
316        else:
317            for entry in entries:
318                if entry.getEndDate()>maxDate:
319                    maxDate=entry.getEndDate().astimezone(tz)
320            if maxDate.date() != day.date():
321                maxDate = day.replace(hour=23,minute=59)
322            return maxDate
323
324    def calculateDayStartDate( self, day ):
325        #
326        # This determines where the times start on the time table.
327        # day is a tz aware datetime
328        if not day.tzinfo:
329            day = timezone(self.getTimezone()).localize(day)
330        tz = day.tzinfo
331        for entry in self.getEntries():
332            if entry.inDay( day ):
333                if entry.getStartDate().astimezone(tz).date() >= day.date():
334                    return entry.getStartDate().astimezone(tz)
335                else:
336                    return day.replace(hour=0,minute=0)
337        return timezone(self.getTimezone()).localize(datetime(day.year,day.month,day.day,8,0)).astimezone(tz)
338
339    def getEntryInPos( self, pos ):
340        try:
341            return self.getEntries()[int(pos)]
342        except IndexError:
343            return None
344
345    def getEntriesOnDay( self, day ):
346        """Returns a list containing all the entries which occur whithin the
347            specified day. These entries will be ordered descending.
348        """
349        if not day.tzinfo:
350            day = timezone(self.getTimezone()).localize(day)
351        res = []
352        for entry in self.getEntries():
353            if entry.inDay( day ):
354                res.append( entry )
355        return res
356
357    def getEntriesOnDate( self, date ):
358        """Returns a list containing all the entries which occur whithin the
359            specified day. These entries will be ordered descending.
360        """
361        res = []
362        for entry in self.getEntries():
363            if entry.onDate( date ):
364                res.append( entry )
365        return res
366
367    def _getNewEntryId(self):
368        try:
369            if self._entryGen:
370                pass
371        except AttributeError:
372            self._entryGen=Counter()
373        return str(self._entryGen.newCount())
374
375    def getEntryById(self,id):
376        for entry in self.getEntries():
377            if entry.getId()==str(id).strip():
378                return entry
379        return None
380
381    def hasGap(self):
382        """check if schedule has gap between two entries"""
383        entries = self.getEntries()
384        if len(entries) > 1:
385            sDate = self.getStartDate('UTC')
386            for entry in entries:
387                if entry.getStartDate()!=sDate:
388                    return True
389                sDate = entry.getEndDate()
390        return False
391
392    def compact(self):
393        """removes any overlaping among schedule entries and make them go one
394            after the other without any gap
395        """
396        refDate=self.getStartDate('UTC')
397        for entry in self._entries:
398            entry.setStartDate(refDate)
399            refDate=entry.getEndDate()
400
401    def moveUpEntry(self,entry,tz=None):
402        pass
403
404    def moveDownEntry(self,entry,tz=None):
405        pass
406
407    def rescheduleTimes(self, type, diff, tz, day=None):
408        pass
409
410    def clear(self):
411        while len(self._entries)>0:
412            self._removeEntry(self._entries[0])
413        self._p_changed = 1
414
415
416    def findFirstFreeSlot(self,reqDur=None):
417        """Tries to find the first free time slot available where an entry with
418            the specified duration could be placed
419        """
420        d=self.getStartDate('UTC')
421        for entry in self.getEntries():
422            availDur=entry.getStartDate()-d
423            if availDur!=0:
424                if reqDur is not None and reqDur!=0:
425                    if reqDur<=availDur:
426                        return d
427                else:
428                    return d
429            d=entry.getEndDate()
430        availDur=self.getEndDate()-d
431        if availDur!=0:
432            if reqDur is not None and reqDur!=0:
433                if reqDur<=availDur:
434                    return d
435            else:
436                return d
437        return None
438
439    def moveEntriesBelow(self, diff, entriesList):
440        """diff: the difference we have to increase/decrease each entry of the list.
441           entriesList: list of entries for applying the diff"""
442
443        if diff is not None:
444            from MaKaC.conference import SessionSlot
445            sessionsAlreadyModif=[]
446            for entry in entriesList:
447                if isinstance(entry.getOwner(), SessionSlot):
448                    session=entry.getOwner().getSession()
449                    if session not in sessionsAlreadyModif:
450                        # if the slot is the first in the session schedule
451                        # we also change the session start date
452                        if session.getSchedule().getEntries()[0].getOwner()==entry.getOwner():
453                            session.setStartDate(session.getStartDate()+diff, check=0, moveEntries=0)
454                        sessionsAlreadyModif.append(session)
455                entry.setStartDate(entry.getStartDate()+diff, check=0, moveEntries=1)
456
457
458class SchEntry(Persistent, Fossilizable):
459    """base schedule entry class. Do NOT instantiate
460    """
461
462    def __init__(self):
463        self._sch=None
464        self.title = ""
465        self.description = ""
466        self._id=""
467
468    def getId(self):
469        try:
470            if self._id:
471                pass
472        except AttributeError:
473            self._id=str(self.getSchedule()._getNewEntryId())
474        return self._id
475
476    def notifyModification(self):
477        if self.getSchedule():
478            self.getSchedule().notifyModification()
479
480    def setSchedule(self,sch,id):
481        if self.getSchedule() is not None:
482            self.getSchedule().removeEntry(self)
483        self._sch=sch
484        self._id=str(id)
485        if self.getSchedule() is not None:
486            sch.addEntry(self)
487
488    def getSchedule(self):
489        try:
490            return self._sch
491        except:
492            self._sch = None
493        return self._sch
494
495    def isScheduled(self):
496        return self.getSchedule() is not None
497
498    def setValues( self, data ):
499        """Sets all the values of the current schedule entry object from a
500            dictionary containing the following key-value pairs:
501                title-(str)
502                description-(str)
503           Please, note that this method sets ALL values which means that if
504            the given dictionary doesn't contain any of the keys the value
505            will set to a default value.
506        """
507        if data.has_key("title"):
508            self.setTitle(data["title"])
509        if data.has_key("description"):
510            self.setDescription(data["description"])
511
512    def getTitle( self ):
513        return self.title
514
515    def setTitle( self, newTitle ):
516        self.title = newTitle.strip()
517
518    def getDescription( self ):
519        return self.description
520
521    def setDescription( self, newDesc ):
522        self.description = newDesc
523
524    def getLocator( self ):
525        if self.getSchedule() is None:
526            return None
527        loc=self.getSchedule().getOwner().getLocator()
528        loc["schEntryId"]=self.getId()
529        return loc
530
531    def synchro( self ):
532        if self.getSchedule() is not None:
533            self.getSchedule().reSchedule()
534
535    def delete(self):
536        pass
537
538    def recover(self):
539        pass
540
541class ConferenceSchedule(TimeSchedule, Fossilizable):
542    """
543    """
544
545#    fossilizes(IConferenceScheduleDisplayFossil, IConferenceScheduleMgmtFossil)
546
547    def __init__(self,conf):
548        TimeSchedule.__init__(self,conf)
549
550    def addEntry(self,entry,check=2):
551        """check parameter:
552            0: no check at all
553            1: check and raise error in case of problem
554            2: check and adapt the owner dates"""
555
556        if (entry is None) or self.hasEntry(entry):
557            return
558        if isinstance(entry,LinkedTimeSchEntry):
559            from MaKaC.conference import Session, Contribution
560            if isinstance(entry.getOwner(),Session):
561                raise MaKaCError( _("Sessions cannot be scheduled into the event, schedule their slots instead"), _("Event"))
562            elif isinstance(entry.getOwner(),Contribution):
563                if entry.getOwner().getSession() is not None:
564                    raise MaKaCError( _("Cannot schedule into the event a contribution that belongs to a session"), _("Event"))
565                if not self.getOwner().hasContribution(entry.getOwner()):
566                    raise MaKaCError( _("Cannot schedule into the event a contribution that does not belong to it"), _("Event"))
567        self._setEntryDuration(entry)
568        return self._addEntry(entry,check)
569
570    def moveUpEntry(self,entry,tz=None):
571        #not very smart, should be improved: contribs with same start date,
572        #   can cause overlapings
573
574        if not tz:
575            tz = self.getTimezone()
576        entriesDay=self.getEntriesOnDay(entry.getAdjustedStartDate())
577        if len(entriesDay)<2:
578            return
579        entrypos = 0
580        if entry in entriesDay:
581            entrypos = entriesDay.index(entry)
582        #if the entry is the first one...then it goes to the end.
583        if entrypos == 0 and len(entriesDay)>1:
584            entriesDay[1].setStartDate(entriesDay[0].getStartDate(), check=0, moveEntries=1)
585            i = 2
586            while(i < len(entriesDay)):
587                entry = entriesDay[i]
588                preventry = entriesDay[i-1]
589                entry.setStartDate(preventry.getEndDate(), check=0, moveEntries=1)
590                i += 1
591            entriesDay[0].setStartDate(entriesDay[len(entriesDay)-1].getEndDate(), check=0, moveEntries=1)
592        else:
593            preventry = entriesDay[entrypos-1]
594            entry.setStartDate(preventry.getStartDate(), check=0, moveEntries=1)
595            preventry.setStartDate(entry.getEndDate(), check=0, moveEntries=1)
596        self.reSchedule()
597        self._p_changed = 1
598
599    def moveDownEntry(self,entry,tz=None):
600        if not tz:
601            tz = self.getTimezone()
602        entriesDay=self.getEntriesOnDay(entry.getAdjustedStartDate())
603        if len(entriesDay)<2:
604            return
605        entrypos = 0
606        if entry in entriesDay:
607            entrypos = entriesDay.index(entry)
608        #if the entry is the last one...then it goes to the first place.
609        if entrypos+1 == len(entriesDay) and len(entriesDay)>1:
610            entriesDay[len(entriesDay)-1].setStartDate(entriesDay[0].getStartDate(), check=0, moveEntries=1)
611            i = -1
612            while(i < len(entriesDay)-2):
613                entry = entriesDay[i]
614                nextentry = entriesDay[i+1]
615                nextentry.setStartDate(entry.getEndDate(), check=0, moveEntries=1)
616                i += 1
617        else:
618            nextentry = entriesDay[entrypos+1]
619            nextentry.setStartDate(entry.getStartDate(), check=0, moveEntries=1)
620            entry.setStartDate(nextentry.getEndDate(), check=0, moveEntries=1)
621        self.reSchedule()
622        self._p_changed = 1
623
624    def rescheduleTimes( self, type, diff, day, doFit):
625        """
626        recalculate and reschedule the entries of the conference slot with a time "diff" of separation.
627        """
628        from MaKaC.conference import SessionSlot
629        entries = self.getEntriesOnDay(day)
630        if type=="duration":
631            i=0
632            while i<len(entries):
633                entry=entries[i]
634                if doFit:
635                    if isinstance( entry.getOwner(), SessionSlot ) :
636                        entry.getOwner().fit()
637                if i+1 == len(entries):
638                    dur=entry.getDuration()
639                else:
640                    nextentry=entries[i+1]
641                    dur=nextentry.getStartDate()-entry.getStartDate()-diff
642                if dur<timedelta(0):
643                    raise EntryTimingError( _("""With the time between entries you've chosen, the entry "%s" will have a duration less than zero minutes. Please, choose another time""")%entry.getTitle())
644                entry.setDuration(dur=dur, check=2)
645                i+=1
646        elif type=="startingTime":
647            st = day.replace(hour=self.getAdjustedStartDate().hour, minute=self.getAdjustedStartDate().minute).astimezone(timezone('UTC'))
648            for entry in entries:
649                if doFit:
650                    if isinstance( entry.getOwner(), SessionSlot ) :
651                        entry.getOwner().fit()
652                entry.setStartDate(st, check=2, moveEntries=1)
653                st=entry.getEndDate()+diff
654        elif type=="noAction" and doFit:
655            for entry in entries:
656                if isinstance( entry.getOwner(), SessionSlot ) :
657                    entry.getOwner().fit()
658
659class SessionSchedule(TimeSchedule):
660    """
661    """
662
663    def __init__(self,session):
664        TimeSchedule.__init__(self,session)
665
666    def checkSanity( self ):
667        if self.hasEntriesBefore(self.getStartDate()) or self.hasEntriesAfter(self.getEndDate()):
668            raise TimingError( _("Sorry, cannot perform this date change: Some entries in the schedule would be outside the new dates"))
669
670    def addEntry(self,entry,check=1):
671        if (entry is None) or self.hasEntry(entry):
672            return
673        if isinstance(entry,LinkedTimeSchEntry):
674            from MaKaC.conference import SessionSlot
675            if not(isinstance(entry.getOwner(),SessionSlot)):
676                raise MaKaCError( _("objects of class %s cannot be scheduled into a session")%(entry.getOwner().__class__), _("Session Schedule"))
677        else:
678            raise MaKaCError( _("objects of class %s cannot be scheduled into a session")%(entry.__class__), _("Session Schedule"))
679        self._addEntry(entry)
680
681    def removeEntry(self,entry):
682        if entry is None or not self.hasEntry(entry):
683            return
684        if entry.getOwner() in self.getOwner().getSlotList():
685            raise MaKaCError( _("Cannot remove a slot without removing it from the session slot list"), _("Session Schedule"))
686        self._removeEntry(entry)
687
688    def moveEntriesBelow(self, diff, entriesList):
689        """diff: the difference we have to increase/decrease each entry of the list.
690           entriesList: list of entries for applying the diff"""
691        if diff is not None:
692            for entry in entriesList:
693                entry.setStartDate(entry.getStartDate()+diff, check=0, moveEntries=1)
694
695class SlotSchedule(TimeSchedule):
696    """
697    """
698
699    def __init__(self,slot):
700        TimeSchedule.__init__(self,slot)
701
702    def _setEntryDuration(self,entry):
703        entryDur=entry.getDuration()
704        if entryDur is None:
705            ownerDur=self.getOwner().getContribDuration()
706            if ownerDur is not None and ownerDur!=timedelta(0):
707                entry.setDuration(dur=ownerDur)
708            else:
709                sessionDur=self.getOwner().getSession().getContribDuration()
710                entry.setDuration(dur=sessionDur)
711
712    def addEntry(self,entry,check=2):
713        """check parameter:
714            0: no check at all
715            1: check and raise error in case of problem
716            2: check and adapt the owner dates
717        """
718
719        tz = self.getTimezone();
720
721        owner = self.getOwner()
722        if (entry is None) or self.hasEntry(entry):
723            return
724        if isinstance(entry,LinkedTimeSchEntry):
725            from MaKaC.conference import Contribution
726            if not(isinstance(entry.getOwner(),Contribution)):
727                raise MaKaCError( _("objects of class %s cannot be scheduled into a session slot"), _("Slot"))
728            if (entry.getOwner().getSession() is None) or (not self.getOwner().getSession().hasContribution(entry.getOwner())):
729                raise MaKaCError( _("Cannot schedule into this session a contribution which does not belong to it"), _("Slot"))
730        if entry.getStartDate()!=None and entry.getStartDate() < self.getOwner().getStartDate():
731            if check == 1:
732                raise ParentTimingError( _("The entry would start at %s, which is before the start time of the time slot (%s)")%\
733                    (entry.getEndDate().strftime('%Y-%m-%d %H:%M'),\
734                    self.getOwner().getStartDate().strftime('%Y-%m-%d %H:%M')),\
735                      _("Slot"))
736            elif check == 2:
737                ContextManager.get('autoOps').append((owner,
738                                                      "OWNER_START_DATE_EXTENDED",
739                                                      owner,
740                                                      entry.getAdjustedStartDate(tz=tz)))
741                self.getOwner().setStartDate(entry.getStartDate(),check,0)
742        if entry.getEndDate()!=None and entry.getEndDate() > self.getOwner().getEndDate():
743            if check == 1:
744                raise ParentTimingError( _("The entry would finish at %s, which is after the end of the time slot (%s)")%\
745                    (entry.getAdjustedEndDate(tz=tz).strftime('%Y-%m-%d %H:%M'),\
746                    self.getOwner().getAdjustedEndDate(tz=tz).strftime('%Y-%m-%d %H:%M')),\
747                     "Slot")
748            elif check == 2:
749                ContextManager.get('autoOps').append((owner,
750                                                      "OWNER_END_DATE_EXTENDED",
751                                                      owner,
752                                                      entry.getAdjustedEndDate(tz=tz)))
753                self.getOwner().setEndDate(entry.getEndDate(),check)
754        self._setEntryDuration(entry)
755        self._addEntry(entry,check)
756
757
758    def moveUpEntry(self,entry):
759        #not very smart, should be improved: contribs with same start date,
760        #   can cause overlapings
761        entries = self.getEntriesOnDay(entry.getAdjustedStartDate())
762        if len(entries)<2:
763            return
764        entrypos = 0
765        if entry in entries:
766            entrypos = entries.index(entry)
767        #if the entry is the first one...then it goes to the end.
768        if entrypos == 0 and len(entries)>1:
769            entries[1].setStartDate(entries[0].getStartDate(),check=0,moveEntries=1)
770            i = 2
771            while(i < len(entries)):
772                entry = entries[i]
773                preventry = entries[i-1]
774                entry.setStartDate(preventry.getEndDate(),check=0,moveEntries=1)
775                i += 1
776            entries[0].setStartDate(entries[len(entries)-1].getEndDate(),check=0,moveEntries=1)
777        else:
778            preventry = entries[entrypos-1]
779            entry.setStartDate(preventry.getStartDate(),check=0,moveEntries=1)
780            preventry.setStartDate(entry.getEndDate(),check=0,moveEntries=1)
781        self.reSchedule()
782        self._p_changed = 1
783
784    def moveDownEntry(self,entry):
785        entries = self.getEntriesOnDay(entry.getAdjustedStartDate())
786        if len(entries)<2:
787            return
788        entrypos = 0
789        if entry in entries:
790            entrypos = entries.index(entry)
791        #if the entry is the last one...then it goes to the first place.
792        if entrypos+1 == len(entries) and len(entries)>1:
793            entries[len(entries)-1].setStartDate(entries[0].getStartDate(), check=0,moveEntries=1)
794            i = -1
795            while(i < len(entries)-2):
796                entry = entries[i]
797                nextentry = entries[i+1]
798                nextentry.setStartDate(entry.getEndDate(),check=0,moveEntries=1)
799                i += 1
800        else:
801            nextentry = entries[entrypos+1]
802            nextentry.setStartDate(entry.getStartDate(),check=0,moveEntries=1)
803            entry.setStartDate(nextentry.getEndDate(),check=0,moveEntries=1)
804        self.reSchedule()
805        self._p_changed = 1
806
807    def moveEntriesBelow(self, diff, entriesList):
808        """diff: the difference we have to increase/decrease each entry of the list.
809           entriesList: list of entries for applying the diff"""
810        if diff is not None:
811            for entry in entriesList:
812                entry.setStartDate(entry.getStartDate()+diff, check=2, moveEntries=1)
813
814class PosterSlotSchedule(SlotSchedule):
815
816    def _setEntryDuration(self,entry):
817        #In the posters schedulers the duration will (by default) always be the
818        #   same for every entry within the slot
819        if entry.getOwner().getDuration() != None and entry.getOwner().getDuration() != 0 \
820                and entry.getOwner().getDuration().seconds!=0:
821                    return
822        ownerDur=self.getOwner().getContribDuration()
823        if ownerDur is not None and \
824                                (ownerDur > timedelta(0)):
825            entry.setDuration(dur=ownerDur)
826        else:
827            sessionDur=self.getOwner().getSession().getContribDuration()
828            entry.setDuration(dur=sessionDur)
829
830    def addEntry(self,entry,check=0):
831        # check=0 is here only because we must  have 3 parameters.
832        if (entry is None) or self.hasEntry(entry):
833            return
834        from MaKaC.conference import Contribution
835        if not isinstance(entry,LinkedTimeSchEntry) or \
836                            not isinstance(entry.getOwner(),Contribution):
837                raise MaKaCError( _("objects of class %s cannot be scheduled into a poster session slot")%entry, _("Slot"))
838        if (entry.getOwner().getSession() is None) or \
839        (not self.getOwner().getSession().hasContribution(entry.getOwner())):
840                raise MaKaCError( _("Cannot schedule into this session a contribution which does not belong to it"), _("Slot"))
841        self._setEntryDuration(entry)
842        if entry.isScheduled():
843            #remove it from the old schedule and add it to this one
844            entry.getSchedule().removeEntry(entry)
845        entry.setStartDate(self.getStartDate())
846        self._entries.append(entry)
847        entry.setSchedule(self,self._getNewEntryId())
848        self._p_changed = 1
849
850    def reSchedule(self):
851        for e in self._entries:
852            e.setStartDate(self.getStartDate())
853
854class SlotSchTypeFactory:
855    _sch={"standard":SlotSchedule,"poster":PosterSlotSchedule}
856    _default="standard"
857
858    def getScheduleKlass(cls,id):
859        id=id.strip().lower()
860        if not cls._sch.has_key(id):
861            id=cls._default
862        return cls._sch[id]
863    getScheduleKlass=classmethod(getScheduleKlass)
864
865    def getDefaultKlass(cls):
866        return cls._sch[cls._default]
867    getDefaultKlass=classmethod(getDefaultKlass)
868
869    def getDefaultId(cls):
870        return cls._default
871    getDefaultId=classmethod(getDefaultId)
872
873    def getIdList(cls):
874        return cls._sch.keys()
875    getIdList=classmethod(getIdList)
876
877    def getId(cls,sch):
878        for (id,schKlass) in cls._sch.items():
879            if sch.__class__==schKlass:
880                return id
881        return ""
882    getId=classmethod(getId)
883
884class TimeSchEntry(SchEntry):
885
886    def __init__(self):
887        SchEntry.__init__(self)
888        self.startDate=None
889        self.duration=None
890
891    def getStartDate( self ):
892        pass
893
894    def setStartDate(self,sDate,check=1, moveEntries=0):
895        pass
896
897    def getEndDate( self ):
898        pass
899
900    def getDuration(self):
901        pass
902
903    def setDuration(self,hours=0,min=15, dur=0):
904        pass
905
906    def inDay( self, day ):
907        pass
908
909    def onDate( self, day ):
910        pass
911
912
913class LinkedTimeSchEntry(TimeSchEntry):
914
915    fossilizes(ILinkedTimeSchEntryDisplayFossil,
916               ILinkedTimeSchEntryMgmtFossil)
917
918    def __init__(self,owner):
919        SchEntry.__init__(self)
920        self.__owner = owner
921
922    # fermi - pass tz here.....
923    def getStartDate( self ):
924        return self.__owner.getStartDate()
925
926    def getAdjustedStartDate( self, tz=None ):
927        return self.__owner.getAdjustedStartDate(tz)
928
929    def setStartDate(self,newDate,check=2, moveEntries=0):
930        """check parameter:
931            0: no check at all
932            1: check and raise error in case of problem
933            2: check and adapt the owner dates"""
934        return self.getOwner().setStartDate(newDate,check, moveEntries)
935
936    def getEndDate( self ):
937        return self.__owner.getEndDate()
938
939    def getAdjustedEndDate( self, tz=None ):
940        return self.__owner.getAdjustedEndDate(tz)
941
942    def getDuration(self):
943        return self.__owner.getDuration()
944
945    def setDuration(self,hours=0,minutes=15,dur=0,check=2):
946        if dur!=0:
947            return self.getOwner().setDuration(dur=dur,check=check)
948        else:
949            return self.getOwner().setDuration(hours,minutes,check=check)
950
951    def getTitle( self ):
952        return self.__owner.getTitle()
953
954    def getDescription( self ):
955        return self.__owner.getDescription()
956
957    def getOwner( self ):
958        return self.__owner
959
960    def inDay( self, day ):
961        """Tells whether or not the current entry occurs whithin the specified
962            day (day is tz-aware)
963        """
964        if not self.isScheduled():
965            return False
966        return self.getStartDate().astimezone(day.tzinfo).date()<=day.date() and self.getEndDate().astimezone(day.tzinfo).date()>=day.date()
967
968    def onDate( self, date ):
969        """Tells whether or not the current entry occurs during the specified
970            date.
971        """
972        if not self.isScheduled():
973            return False
974        return self.getStartDate()<=date and \
975                self.getEndDate()>=date
976
977    def collides(self,entry):
978        return (entry.getStartDate()>=self.getStartDate() and \
979                entry.getStartDate()<self.getEndDate()) or \
980                (entry.getEndDate()>self.getStartDate() and \
981                entry.getEndDate()<=self.getEndDate())
982
983
984class IndTimeSchEntry(TimeSchEntry):
985
986    def setValues( self, data ):
987        """Sets all the values of the current schedule entry object from a
988            dictionary containing the following key-value pairs:
989                title-(str)
990                description-(str)
991                year, month, day, sHour, sMinute - (str) => components of the
992                        starting date of the entry, if not specified it will
993                        be set to now.
994                durationHours, durationMinutes - (str)
995           Please, note that this method sets ALL values which means that if
996            the given dictionary doesn't contain any of the keys the value
997            will set to a default value.
998        """
999        SchEntry.setValues(self,data)
1000        if data.get("sYear", None) != None and \
1001                data.get("sMonth", None) != None and \
1002                data.get("sDay", None) != None and \
1003                data.get("sHour", None) != None and \
1004                data.get("sMinute", None) != None:
1005            self.setStartDate(datetime(int(data["sYear"]),\
1006                                        int(data["sMonth"]),\
1007                                        int(data["sDay"]), \
1008                                        int(data["sHour"]),\
1009                                        int(data["sMinute"])) )
1010        if data.get("durHours",None)!=None and data.get("durMins",None)!=None:
1011            self.setDuration(data["durHours"],data["durMins"])
1012
1013    def getTimezone( self ):
1014        return self.getSchedule().getOwner().getTimezone()
1015
1016    def getStartDate( self ):
1017        return self.startDate
1018
1019    def getAdjustedStartDate( self, tz=None ):
1020        if not tz:
1021            tz = self.getTimezone()
1022
1023        return self.getStartDate().astimezone(timezone(tz))
1024
1025    def setStartDate(self,sDate,check=1, moveEntries=0):
1026        self.startDate=sDate
1027        self._p_changed=1
1028        if self.isScheduled():
1029            self.getSchedule().reSchedule()
1030
1031    def getEndDate( self ):
1032        if self.getStartDate() is None:
1033            return None
1034        return self.startDate+self.duration
1035
1036    def getAdjustedEndDate( self, tz=None ):
1037        if not tz:
1038            tz = self.getTimezone()
1039        return self.getEndDate().astimezone(timezone(tz))
1040
1041    def getDuration(self):
1042        return self.duration
1043
1044    def setDuration(self,hours=0,min=15,dur=0):
1045        if dur==0:
1046            self.duration=timedelta(hours=int(hours),minutes=int(min))
1047        else:
1048            self.duration=dur
1049        self._p_changed = 1
1050        if self.isScheduled():
1051            self.getSchedule().reSchedule()
1052
1053    def inDay( self, day ):
1054        """Tells whether or not the current entry occurs whithin the specified
1055            day (day is tz-aware)
1056        """
1057        if not self.isScheduled():
1058            return False
1059        return self.getStartDate().astimezone(day.tzinfo).date()<=day.date() and self.getEndDate().astimezone(day.tzinfo).date()>=day.date()
1060
1061    def onDate( self, date ):
1062        """Tells whether or not the current entry occurs during the specified
1063            date.
1064        """
1065        if not self.isScheduled():
1066            return False
1067        return self.getStartDate()<=date and \
1068                self.getEndDate()>=date
1069
1070
1071class BreakTimeSchEntry(IndTimeSchEntry):
1072
1073    fossilizes(IBreakTimeSchEntryFossil, IBreakTimeSchEntryMgmtFossil)
1074
1075    def __init__(self):
1076        IndTimeSchEntry.__init__(self)
1077        self._color="#90C0F0"
1078        self._textColor="#202020"
1079        self._textColorToLink=False
1080
1081    def clone(self, owner):
1082        btse = BreakTimeSchEntry()
1083        btse.setValues(self.getValues())
1084        olddate =  self.getOwner().getStartDate()
1085        newdate = owner.getSchedule().getStartDate()
1086        timeDifference = newdate - olddate
1087        btse.setStartDate(btse.getStartDate()+timeDifference)
1088        return btse
1089
1090    def getValues(self):
1091        values = {}
1092        values["startDate"] = self.getStartDate()
1093        values["endDate"] = self.getEndDate()
1094        values["durTimedelta"] = self.getDuration()
1095        values["description"] = self.getDescription()
1096        values["title"] = self.getTitle()
1097        if self.getOwnLocation() is not None :
1098            values["locationName"] = self.getLocation().getName()
1099        else :
1100            values["locationName"] = ""
1101        if self.getOwnRoom() is not None :
1102            values["roomName"] = self.getOwnRoom().getName()
1103        else :
1104            values["roomName"] = ""
1105        values["backgroundColor"] = self.getColor()
1106        values["textColor"] = self.getTextColor()
1107        if self.isTextColorToLinks():
1108            values["textcolortolinks"]="True"
1109
1110        return values
1111
1112    def setValues( self, data, check=2, moveEntriesBelow=0, tz='UTC'):
1113        from MaKaC.conference import CustomLocation, CustomRoom
1114        # In order to move the entries below, it is needed to know the diff (we have to move them)
1115        # and the list of entries to move. It's is needed to take those datas in advance because they
1116        # are going to be modified before the moving.
1117        if moveEntriesBelow == 1 and self.getSchedule():
1118            oldStartDate=copy.copy(self.getStartDate())
1119            oldDuration=copy.copy(self.getDuration())
1120            i=self.getSchedule().getEntries().index(self)+1
1121            entriesList = self.getSchedule().getEntries()[i:]
1122        if data.get("startDate", None) != None:
1123            self.setStartDate(data["startDate"], 0)
1124        elif data.get("sYear", None) != None and \
1125                data.get("sMonth", None) != None and \
1126                data.get("sDay", None) != None and \
1127                data.get("sHour", None) != None and \
1128                data.get("sMinute", None) != None:
1129            #########################################
1130            # Fermi timezone awareness              #
1131            #  We have to store as UTC, relative    #
1132            #  to the timezone of the conference.   #
1133            #########################################
1134            d = timezone(tz).localize(datetime(int(data["sYear"]),
1135                    int(data["sMonth"]),
1136                    int(data["sDay"]),
1137                    int(data["sHour"]),
1138                    int(data["sMinute"])))
1139            sDate = d.astimezone(timezone('UTC'))
1140            self.setStartDate(sDate)
1141            ########################################
1142            # Fermi timezone awareness             #
1143            #  We have to store as UTC, relative   #
1144            #  to the timezone of the conference.  #
1145            ########################################
1146
1147        if data.get("durTimedelta", None) != None:
1148            self.setDuration(check=0, dur=data["durTimedelta"])
1149        elif data.get("durHours","").strip()!="" and data.get("durMins","").strip()!="":
1150                self.setDuration(data["durHours"], data["durMins"], 0)
1151        else:
1152            h=data.get("durHours","").strip()
1153            m=data.get("durMins","").strip()
1154            force=False
1155            if h!="" or m!="":
1156                h=h or "0"
1157                m=m or "0"
1158                if h!="0" or m!="0":
1159                    self.setDuration(int(h), int(m), 0)
1160                else:
1161                    force=True
1162            else:
1163                force=True
1164            if force:
1165                if self.getDuration() is None or self.getDuration()==0:
1166                    self.setDuration("0", "15", 0)
1167        if data.get( "locationName", "" ).strip() == "":
1168            self.setLocation( None )
1169        else:
1170            loc = self.getOwnLocation()
1171            if not loc:
1172                loc = CustomLocation()
1173            self.setLocation( loc )
1174            loc.setName( data["locationName"] )
1175            loc.setAddress( data.get("locationAddress", "") )
1176        if data.get( "roomName", "" ).strip() == "":
1177            self.setRoom( None )
1178        else:
1179            room = self.getOwnRoom()
1180            if not room:
1181                room = CustomRoom()
1182            self.setRoom( room )
1183            room.setName( data["roomName"] )
1184            room.retrieveFullName(data.get('locationName', ''))
1185        self._color=data.get("backgroundColor","#90C0F0")
1186        if data.has_key("autotextcolor"):
1187            self._textColor=utils.getTextColorFromBackgroundColor(self.getColor())
1188        else:
1189            self._textColor=data.get("textColor","#202020")
1190        self.setTextColorToLinks(data.has_key("textcolortolinks"))
1191        if data.has_key("title"):
1192            self.setTitle(data["title"])
1193        if data.has_key("description"):
1194            self.setDescription(data["description"])
1195
1196        # now check if the slot new time is compatible with its parents limits
1197        if check == 1:
1198            if self.getSchedule() and self.getSchedule().isOutside(self):
1199                raise TimingError( _("This action would move the break out of its parents schedule dates"))
1200        elif check == 2:
1201            if self.getSchedule() and self.getSchedule().isOutside(self):
1202                self.synchro()
1203                # syncrho is not modifying the dates of the session slot. Fit does.
1204                if isinstance(self.getSchedule(), SlotSchedule):
1205                    self.getSchedule().getOwner().fit()
1206        if moveEntriesBelow == 1 and self.getSchedule():
1207            diff = (self.getStartDate() - oldStartDate) + (self.getDuration() - oldDuration)
1208            self.getSchedule().moveEntriesBelow(diff, entriesList)
1209        self.notifyModification()
1210
1211
1212    def getLocationParent( self ):
1213        if self.getSchedule() is not None:
1214            return self.getSchedule().getOwner()
1215        return None
1216
1217    def setLocation(self, loc):
1218        self.place = loc
1219
1220    def getLocation(self):
1221        if self.getOwnLocation() is None:
1222            return self.getInheritedLocation()
1223        return self.getOwnLocation()
1224
1225    def getInheritedLocation(self):
1226        locParent = self.getLocationParent()
1227        if locParent:
1228            return locParent.getLocation();
1229        else:
1230            return None
1231
1232    def getOwnLocation(self):
1233        try:
1234            if self.place:
1235                pass
1236        except AttributeError:
1237            self.place=None
1238        return self.place
1239
1240    def setRoom(self, room):
1241        self.room = room
1242
1243    def getRoom(self):
1244        if self.getOwnRoom() is None:
1245            return self.getInheritedRoom()
1246        return self.getOwnRoom()
1247
1248    def getInheritedRoom(self):
1249        locParent = self.getLocationParent()
1250        if locParent:
1251            return locParent.getRoom();
1252        else:
1253            return None
1254
1255    def getOwnRoom(self):
1256        try:
1257            if self.room:
1258                pass
1259        except AttributeError:
1260            self.room=None
1261        return self.room
1262
1263    def getOwner(self):
1264        if self.getSchedule() is not None:
1265            return self.getSchedule().getOwner()
1266        return None
1267
1268    def _verifyDuration(self,check=2):
1269
1270        if self.getSchedule() is not None:
1271            owner = self.getSchedule().getOwner()
1272            if self.getEndDate() > owner.getEndDate():
1273                if check==1:
1274                    raise ParentTimingError( _("The break cannot end after (%s) its parent (%s)")%\
1275                        (self.getEndDate().strftime('%Y-%m-%d %H:%M'),\
1276                        owner.getEndDate().strftime('%Y-%m-%d %H:%M')),\
1277                         _("Break"))
1278                elif check==2:
1279                    # update the schedule
1280                    owner.setEndDate(self.getEndDate(),check)
1281
1282    def setDuration(self, hours=0, min=15, check=2,dur=0):
1283
1284        if dur==0:
1285            IndTimeSchEntry.setDuration(self,hours,min)
1286        else:
1287            IndTimeSchEntry.setDuration(self,dur=dur)
1288        self._verifyDuration(check)
1289        self.notifyModification()
1290
1291    def setStartDate(self, newDate,check=2, moveEntries=0):
1292
1293#        try:
1294#            tz = str(newDate.tzinfo)
1295#        except:
1296#            tz = 'undef'
1297        if self.getSchedule() is not None:
1298            owner = self.getSchedule().getOwner()
1299            if newDate < owner.getStartDate():
1300                if check==1:
1301                    raise ParentTimingError( _("The break \"%s\" cannot start before (%s) its parent (%s)")%\
1302                        (self.getTitle(), \
1303                        newDate.astimezone(timezone(self.getTimezone())).strftime('%Y-%m-%d %H:%M'),\
1304                        owner.getAdjustedStartDate().strftime('%Y-%m-%d %H:%M')),\
1305                        "Break")
1306                elif check==2:
1307                    # update the schedule
1308                    owner.setStartDate(newDate, check)
1309                    ContextManager.get('autoOps').append((self, "OWNER_START_DATE_EXTENDED",
1310                                                          owner, owner.getAdjustedStartDate()))
1311            if newDate > owner.getEndDate():
1312                if check==1:
1313                    raise ParentTimingError("The break cannot start after (%s) its parent ends (%s)"%\
1314                        (newDate.astimezone(timezone(self.getTimezone())).strftime('%Y-%m-%d %H:%M'),\
1315                        owner.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
1316                         _("Break"))
1317                elif check==2:
1318                    # update the schedule
1319                    owner.setEndDate(newDate,check)
1320                    ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
1321                                                          owner, owner.getAdjustedEndDate()))
1322        IndTimeSchEntry.setStartDate(self, newDate,check)
1323        # Check that after modifying the start date, the end date is still within the limits of the slot
1324        if self.getSchedule() and self.getEndDate() > owner.getEndDate():
1325            if check==1:
1326                raise ParentTimingError("The break cannot end after (%s) its parent ends (%s)"%\
1327                        (self.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M'),\
1328                        owner.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
1329                         _("Break"))
1330            elif check==2:
1331                # update the schedule
1332                owner.setEndDate(self.getEndDate(),check)
1333                ContextManager.get('autoOps').append((self, "OWNER_END_DATE_EXTENDED",
1334                                                          owner, owner.getAdjustedEndDate()))
1335        self.notifyModification()
1336
1337    def setColor(self,newColor):
1338        self._color=newColor
1339    setBgColor=setColor
1340
1341    def getColor(self):
1342        try:
1343            if self._color:
1344                pass
1345        except AttributeError:
1346            self._color="#AADDEE"
1347        return self._color
1348    getBgColor=getColor
1349
1350    def setTextColor(self,newColor):
1351        self._textColor=newColor
1352
1353    def getTextColor(self):
1354        try:
1355            if self._textColor:
1356                pass
1357        except AttributeError:
1358            self._textColor="#202020"
1359        return self._textColor
1360
1361    def setTextColorToLinks(self,v):
1362        self._textColorToLink=v
1363
1364    def isTextColorToLinks(self):
1365        try:
1366            if self._textColorToLink:
1367                pass
1368        except AttributeError:
1369            self._textColorToLink=False
1370        return self._textColorToLink
1371
1372    def delete(self):
1373        TrashCanManager().add(self)
1374
1375    def recover(self):
1376        TrashCanManager().remove(self)
1377
1378class ContribSchEntry(LinkedTimeSchEntry):
1379
1380    fossilizes(IContribSchEntryDisplayFossil,
1381               IContribSchEntryMgmtFossil )
1382
1383    def __init__(self, owner):
1384        LinkedTimeSchEntry.__init__(self, owner)
1385
1386    def setSchedule(self,sch,id):
1387        status=self.getOwner().getCurrentStatus()
1388        from MaKaC.conference import ContribStatusWithdrawn,ContribStatusNotSch,ContribStatusSch
1389        if isinstance(status,ContribStatusWithdrawn) and sch is not None:
1390            raise MaKaCError( _("Cannot schedule a contribution which has been withdrawn"), _("Contribution"))
1391        LinkedTimeSchEntry.setSchedule(self,sch,id)
1392        if sch is None:
1393            newStatus=ContribStatusNotSch(self.getOwner())
1394        else:
1395            newStatus=ContribStatusSch(self.getOwner())
1396        self.getOwner().setStatus(newStatus)
1397
1398    def getRoom(self):
1399        return self.getOwner().getRoom()
1400
1401    def setRoom(self, room):
1402        self.getOwner().setRoom(room)
1403
1404    def getLocation(self):
1405        return self.getOwner().getLocation()
1406
1407    def setLocation(self, loc):
1408        self.getOwner().setLocation(loc)
1409
1410    def getOwnRoom(self):
1411        return self.getOwner().getOwnRoom()
1412
1413    def getOwnLocation(self):
1414        return self.getOwner().getOwnLocation()
1415
1416class ScheduleToJson:
1417
1418    @staticmethod
1419    def processEntry(obj, tz, aw, mgmtMode = False, useAttrCache = False):
1420
1421        if mgmtMode:
1422            if isinstance(obj, BreakTimeSchEntry):
1423                entry = obj.fossilize(IBreakTimeSchEntryMgmtFossil, useAttrCache = useAttrCache, tz = tz)
1424            elif isinstance(obj, ContribSchEntry):
1425                entry = obj.fossilize(IContribSchEntryMgmtFossil, useAttrCache = useAttrCache, tz = tz)
1426            elif isinstance(obj, LinkedTimeSchEntry):
1427                entry = obj.fossilize(ILinkedTimeSchEntryMgmtFossil, useAttrCache = useAttrCache, tz = tz)
1428            else:
1429                entry = obj.fossilize(useAttrCache = useAttrCache, tz = tz)
1430        else:
1431            # the fossils used for the display of entries
1432            # will be taken by default, since they're first
1433            # in the list of their respective Fossilizable
1434            # objects
1435            entry = obj.fossilize(useAttrCache = useAttrCache, tz = tz)
1436
1437        genId = entry['id']
1438
1439        # sessions that are no poster sessions will be expanded
1440        if entry['entryType'] == 'Session':
1441
1442            sessionSlot = obj.getOwner()
1443
1444            # get session content
1445            entries = {}
1446            for contrib in sessionSlot.getSchedule().getEntries():
1447                if ScheduleToJson.checkProtection(contrib, aw):
1448                    if mgmtMode:
1449                        if isinstance(contrib, ContribSchEntry):
1450                            contribData = contrib.fossilize(IContribSchEntryMgmtFossil, useAttrCache = useAttrCache, tz = tz)
1451                        elif isinstance(contrib, BreakTimeSchEntry):
1452                            contribData = contrib.fossilize(IBreakTimeSchEntryMgmtFossil, useAttrCache = useAttrCache, tz = tz)
1453                        else:
1454                            contribData = contrib.fossilize(useAttrCache = useAttrCache, tz = tz)
1455                    else:
1456                        # the fossils used for the display of entries
1457                        # will be taken by default, since they're first
1458                        # in the list of their respective Fossilizable
1459                        # objects
1460                        contribData = contrib.fossilize(useAttrCache = useAttrCache, tz = tz)
1461
1462                    entries[contribData['id']] = contribData
1463
1464            entry['entries'] = entries
1465
1466        return genId, entry
1467
1468    @staticmethod
1469    def checkProtection(obj, aw):
1470
1471        if aw is None:
1472            return True
1473
1474        from MaKaC.conference import SessionSlot
1475
1476        canBeDisplayed = False
1477        if isinstance(obj, BreakTimeSchEntry):
1478            canBeDisplayed = True
1479        else: #contrib or session slot
1480            owner = obj.getOwner()
1481            if owner.canAccess(aw):
1482                canBeDisplayed = True
1483            elif isinstance(owner, SessionSlot) and owner.canView(aw):
1484                canBeDisplayed = True
1485        return canBeDisplayed
1486
1487    @staticmethod
1488    def process(schedule, tz, aw, days = None, mgmtMode = False, useAttrCache = False, hideWeekends = False):
1489
1490        scheduleDict={}
1491
1492        if not days:
1493            days = daysBetween(schedule.getAdjustedStartDate(tz), schedule.getAdjustedEndDate(tz))
1494
1495        dates = [d.strftime("%Y%m%d") for d in days]
1496
1497        # Generating the days dictionnary
1498        for d in dates:
1499            scheduleDict[d] = {}
1500
1501        # Filling the day dictionnary with entries
1502        for obj in schedule.getEntries():
1503
1504            if ScheduleToJson.checkProtection(obj, aw):
1505                genId, resultData = ScheduleToJson.processEntry(obj, tz, aw, mgmtMode, useAttrCache)
1506                day = obj.getAdjustedStartDate(tz).strftime("%Y%m%d")
1507                # verify that start date is in dates
1508                if day in dates:
1509                    scheduleDict[day][genId] = resultData
1510        if hideWeekends:
1511            for entry in scheduleDict.keys():
1512                weekDay = datetime.strptime(entry, "%Y%m%d").weekday()
1513                if scheduleDict[entry] == {} and (weekDay == 5 or weekDay == 6):
1514                    del scheduleDict[entry]
1515
1516        return scheduleDict
1517
1518    @staticmethod
1519    def sort_dict(dict):
1520        new_dict = {}
1521        sorted_keys = dict.keys()
1522        sorted_keys.sort()
1523
1524        for key in sorted_keys:
1525            new_dict[key] = dict[key]
1526
1527        return new_dict
Note: See TracBrowser for help on using the repository browser.