1 # This file is part of Indico.
2 # Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
4 # Indico is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License as
6 # published by the Free Software Foundation; either version 3 of the
7 # License, or (at your option) any later version.
9 # Indico is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with Indico; if not, see <http://www.gnu.org/licenses/>.
20 from persistent
import Persistent
21 from datetime
import datetime
,timedelta
22 from MaKaC
.common
.Counter
import Counter
23 from MaKaC
.errors
import MaKaCError
, TimingError
, ParentTimingError
,\
25 from MaKaC
.common
import utils
26 from MaKaC
.trashCan
import TrashCanManager
27 from MaKaC
.i18n
import _
28 from pytz
import timezone
29 from indico
.util
.date_time
import iterdays
30 from MaKaC
.common
.Conversion
import Conversion
31 from MaKaC
.common
.contextManager
import ContextManager
32 from MaKaC
.common
.fossilize
import Fossilizable
, fossilizes
33 from MaKaC
.fossils
.schedule
import IContribSchEntryDisplayFossil
,\
34 IContribSchEntryMgmtFossil
, IBreakTimeSchEntryFossil
,\
35 IBreakTimeSchEntryMgmtFossil
,\
36 ILinkedTimeSchEntryDisplayFossil
, ILinkedTimeSchEntryMgmtFossil
37 from MaKaC
.common
.cache
import GenericCache
38 from MaKaC
.errors
import NoReportError
39 from indico
.util
.decorators
import classproperty
43 """base schedule class. Do NOT instantiate
46 def __init__( self
, owner
):
49 def getEntries( self
):
52 def addEntry( self
, entry
, position
=None ):
55 def removeEntry( self
, entry
):
58 def getEntryPosition( self
, entry
):
61 def moveEntry( self
, entry
, newPosition
, after
=1 ):
64 def getEntryLocator( self
, entry
):
70 def reSchedule( self
):
73 def getEntryInPos( self
, pos
):
77 class TimeSchedule(Schedule
, Persistent
):
81 def __init__(self
,owner
):
84 self
._entryGen
=Counter()
85 self
._allowParallel
=True
87 def notifyModification(self
):
88 self
.getOwner().notifyModification()
90 def getEntries( self
):
93 def hasEntriesBefore(self
,d
):
94 """Tells wether there is any entry before the specified date
96 entries
=self
.getEntries()
99 return entries
[0].getStartDate()<d
101 def hasEntriesAfter(self
,d
):
102 """Tells wether there is any entry after the specified date
104 entries
=self
.getEntries()
107 return self
.calculateEndDate()>d
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.")
113 def isOutside(self
,entry
):
114 """Tells whether an entry is outside the date boundaries of the schedule
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'):
123 if entry
.getEndDate() is not None:
124 if entry
.getEndDate()<self
.getStartDate('UTC') or \
125 entry
.getEndDate()>self
.getEndDate('UTC'):
129 def hasEntry(self
,entry
):
130 return entry
.isScheduled() and entry
.getSchedule()==self
and\
131 entry
in self
._entries
133 def _addEntry(self
,entry
,check
=2):
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
)
142 owner
= self
.getOwner()
144 tz
= owner
.getConference().getTimezone()
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())
153 newEndDate
= self
.getEndDate() + entry
.getDuration()
155 ContextManager
.get('autoOps').append((owner
,
156 "OWNER_END_DATE_EXTENDED",
158 newEndDate
.astimezone(timezone(tz
))))
160 owner
.setEndDate(newEndDate
, check
)
161 sDate
= self
.findFirstFreeSlot(entry
.getDuration())
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
168 if entry
.getStartDate() < self
.getStartDate('UTC'):
170 raise TimingError( _("Cannot schedule this entry because its start date (%s) is before its parents (%s)")%(entry
.getAdjustedStartDate(),self
.getAdjustedStartDate()),_("Add Entry"))
172 ContextManager
.get('autoOps').append((owner
,
173 "OWNER_START_DATE_EXTENDED",
175 entry
.getAdjustedStartDate(tz
=tz
)))
176 owner
.setStartDate(entry
.getStartDate(),check
)
177 elif entry
.getEndDate()>self
.getEndDate('UTC'):
179 raise TimingError( _("Cannot schedule this entry because its end date (%s) is after its parents (%s)")%(entry
.getAdjustedEndDate(),self
.getAdjustedEndDate()),_("Add Entry"))
181 ContextManager
.get('autoOps').append((owner
,
182 "OWNER_END_DATE_EXTENDED",
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
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
())
195 self
._cleanCache
(entry
)
198 def _setEntryDuration(self
,entry
):
199 if entry
.getDuration() is None:
200 entry
.setDuration(0,5)
202 def addEntry(self
,entry
):
203 if (entry
is None) or self
.hasEntry(entry
):
205 self
._setEntryDuration
(entry
)
206 result
= self
._addEntry
(entry
)
210 def _removeEntry(self
,entry
):
211 self
._cleanCache
(entry
)
212 self
._entries
.remove(entry
)
213 entry
.setSchedule(None,"")
214 entry
.setStartDate(None)
218 def _cleanCache(self
, entry
):
219 if isinstance(entry
, ContribSchEntry
):
220 entry
.getOwner().cleanCache()
221 self
.getOwner().cleanCache()
222 elif isinstance(entry
, BreakTimeSchEntry
):
223 self
.getOwner().cleanCache()
224 ScheduleToJson
.cleanCache(entry
, False)
226 entry
.getOwner().cleanCache()
228 def removeEntry(self
,entry
):
229 if entry
is None or not self
.hasEntry(entry
):
231 self
._removeEntry
(entry
)
233 def getEntryPosition( self
, entry
):
234 return self
._entries
.index( entry
)
236 def getOwner( self
):
239 ####################################
240 # Fermi timezone awareness #
241 ####################################
243 def getStartDate( self
,tz
='UTC'):
244 return self
.getOwner().getAdjustedStartDate(tz
)
246 def getAdjustedStartDate( self
, tz
=None ):
247 return self
.getOwner().getAdjustedStartDate(tz
)
249 def getEndDate( self
, tz
='UTC'):
250 return self
.getOwner().getAdjustedEndDate(tz
)
252 def getAdjustedEndDate( self
, tz
=None):
253 return self
.getOwner().getAdjustedEndDate(tz
)
255 ####################################
256 # Fermi timezone awareness(end) #
257 ####################################
259 def cmpEntries(self
,e1
,e2
):
260 datePrecedence
= cmp(e1
.getStartDate(), e2
.getStartDate())
262 # if we're tied, let's use duration as a criterion
263 # this keeps zero-duration breaks from making it get stuck
264 if datePrecedence
== 0:
265 return cmp(e1
.getDuration(), e2
.getDuration())
267 return datePrecedence
269 def reSchedule(self
):
271 if self
._allowParalell
:
273 except AttributeError:
274 self
._allowParallel
=True
275 self
._entries
.sort(self
.cmpEntries
)
277 for entry
in self
._entries
:
278 if lastEntry
is not None:
279 if not self
._allowParallel
:
280 if lastEntry
.collides(entry
):
281 entry
.setStartDate(lastEntry
.getEndDate())
285 def calculateEndDate( self
):
286 if len(self
._entries
) == 0:
287 return self
.getStartDate()
288 eDate
= self
.getStartDate()
289 for entry
in self
._entries
:
290 if entry
.getEndDate()>eDate
:
291 eDate
= entry
.getEndDate()
294 def calculateStartDate( self
):
295 if len(self
._entries
) == 0:
296 return self
.getStartDate()
298 return self
._entries
[0].getStartDate()
300 def getTimezone( self
):
301 return self
.getOwner().getConference().getTimezone()
303 def getFirstFreeSlotOnDay(self
,day
):
305 day
= timezone(self
.getTimezone()).localize(day
)
307 entries
= self
.getEntriesOnDay(day
)
309 if self
.getStartDate().astimezone(tz
).date() == day
.date():
310 return self
.getStartDate().astimezone(tz
)
311 return day
.astimezone(timezone(self
.getTimezone())).replace(hour
=8,minute
=0).astimezone(tz
)
313 return self
.calculateDayEndDate(day
)
315 def calculateDayEndDate(self
,day
,hour
=0,min=0):
317 return self
.calculateEndDate()
319 day
= timezone(self
.getTimezone()).localize(day
)
321 maxDate
=day
.replace(hour
=hour
,minute
=min)
322 entries
= self
.getEntriesOnDay(day
)
323 if hour
!= 0 or min != 0:
325 elif len(entries
)==0:
326 confstime
= self
.getOwner().getAdjustedStartDate()
327 return day
.astimezone(timezone(self
.getTimezone())).replace(hour
=confstime
.hour
,minute
=confstime
.minute
).astimezone(tz
)
329 for entry
in entries
:
330 if entry
.getEndDate()>maxDate
:
331 maxDate
=entry
.getEndDate().astimezone(tz
)
332 if maxDate
.date() != day
.date():
333 maxDate
= day
.replace(hour
=23,minute
=59)
336 def calculateDayStartDate( self
, day
):
338 # This determines where the times start on the time table.
339 # day is a tz aware datetime
341 day
= timezone(self
.getTimezone()).localize(day
)
343 for entry
in self
.getEntries():
344 if entry
.inDay( day
):
345 if entry
.getStartDate().astimezone(tz
).date() >= day
.date():
346 return entry
.getStartDate().astimezone(tz
)
348 return day
.replace(hour
=0,minute
=0)
349 return timezone(self
.getTimezone()).localize(datetime(day
.year
,day
.month
,day
.day
,8,0)).astimezone(tz
)
351 def getEntryInPos( self
, pos
):
353 return self
.getEntries()[int(pos
)]
357 def getEntriesOnDay( self
, day
):
358 """Returns a list containing all the entries which occur whithin the
359 specified day. These entries will be ordered descending.
362 day
= timezone(self
.getTimezone()).localize(day
)
364 for entry
in self
.getEntries():
365 if entry
.inDay( day
):
369 def getEntriesOnDate( self
, date
):
370 """Returns a list containing all the entries which occur whithin the
371 specified day. These entries will be ordered descending.
374 for entry
in self
.getEntries():
375 if entry
.onDate( date
):
379 def _getNewEntryId(self
):
383 except AttributeError:
384 self
._entryGen
=Counter()
385 return str(self
._entryGen
.newCount())
387 def getEntryById(self
,id):
388 for entry
in self
.getEntries():
389 if entry
.getId()==str(id).strip():
394 """check if schedule has gap between two entries"""
395 entries
= self
.getEntries()
397 sDate
= self
.getStartDate('UTC')
398 for entry
in entries
:
399 if entry
.getStartDate()!=sDate
:
401 sDate
= entry
.getEndDate()
405 """removes any overlaping among schedule entries and make them go one
406 after the other without any gap
408 refDate
=self
.getStartDate('UTC')
409 for entry
in self
._entries
:
410 entry
.setStartDate(refDate
)
411 refDate
=entry
.getEndDate()
413 def moveUpEntry(self
,entry
,tz
=None):
416 def moveDownEntry(self
,entry
,tz
=None):
419 def rescheduleTimes(self
, type, diff
, day
, doFit
):
421 recalculate and reschedule the entries of the event with a time "diff" of separation.
424 from MaKaC
.conference
import SessionSlot
425 entries
= self
.getEntriesOnDay(day
)
426 if type == "duration":
428 while i
< len(entries
):
430 if isinstance(entry
.getOwner(), SessionSlot
) and entry
.getOwner().getSession().isClosed():
431 raise EntryTimingError(_("""The modification of the session "%s" is not allowed because it is closed""") % entry
.getOwner().getSession().getTitle())
434 if isinstance(entry
.getOwner(), SessionSlot
):
435 entry
.getOwner().fit()
436 if i
+ 1 == len(entries
):
437 dur
= entry
.getDuration()
439 nextentry
= entries
[i
+ 1]
440 dur
= nextentry
.getStartDate() - entry
.getStartDate() - diff
441 if dur
< timedelta(0):
442 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())
443 entry
.setDuration(dur
=dur
, check
=2)
445 elif type == "startingTime":
446 st
= day
.replace(hour
=self
.getAdjustedStartDate().hour
, minute
=self
.getAdjustedStartDate().minute
).astimezone(timezone('UTC'))
447 for entry
in entries
:
448 if isinstance(entry
.getOwner(), SessionSlot
) and entry
.getOwner().getSession().isClosed():
449 raise EntryTimingError(_("""The modification of the session "%s" is not allowed because it is closed""") % entry
.getOwner().getSession().getTitle())
451 if isinstance(entry
.getOwner(), SessionSlot
):
452 entry
.getOwner().fit()
453 entry
.setStartDate(st
, check
=2, moveEntries
=1)
454 st
= entry
.getEndDate() + diff
455 elif type == "noAction" and doFit
:
456 for entry
in entries
:
457 if isinstance(entry
.getOwner(), SessionSlot
):
458 if entry
.getOwner().getSession().isClosed():
459 raise EntryTimingError(_("""The modification of the session "%s" is not allowed because it is closed""") % entry
.getOwner().getSession().getTitle())
460 entry
.getOwner().fit()
463 while len(self
._entries
)>0:
464 self
._removeEntry
(self
._entries
[0])
468 def findFirstFreeSlot(self
,reqDur
=None):
469 """Tries to find the first free time slot available where an entry with
470 the specified duration could be placed
472 d
=self
.getStartDate('UTC')
473 for entry
in self
.getEntries():
474 availDur
=entry
.getStartDate()-d
476 if reqDur
is not None and reqDur
!=0:
482 availDur
=self
.getEndDate()-d
484 if reqDur
is not None and reqDur
!=0:
491 def moveEntriesBelow(self
, diff
, entriesList
, check
=2):
492 """diff: the difference we have to increase/decrease each entry of the list.
493 entriesList: list of entries for applying the diff"""
496 from MaKaC
.conference
import SessionSlot
497 sessionsAlreadyModif
= []
498 for entry
in entriesList
:
499 if isinstance(entry
.getOwner(), SessionSlot
):
500 session
= entry
.getOwner().getSession()
501 if session
not in sessionsAlreadyModif
:
502 # if the slot is the first in the session schedule
503 # we also change the session start date
504 if session
.getSchedule().getEntries()[0].getOwner() == entry
.getOwner():
505 session
.setStartDate(session
.getStartDate() + diff
, check
=0, moveEntries
=0)
506 sessionsAlreadyModif
.append(session
)
507 entry
.setStartDate(entry
.getStartDate() + diff
, check
=check
, moveEntries
=1)
510 class SchEntry(Persistent
, Fossilizable
):
511 """base schedule entry class. Do NOT instantiate
517 self
.description
= ""
524 except AttributeError:
525 self
._id
=str(self
.getSchedule()._getNewEntryId
())
528 def notifyModification(self
):
529 if self
.getSchedule():
530 self
.getSchedule().notifyModification()
532 def setSchedule(self
,sch
,id):
533 if self
.getSchedule() is not None:
534 self
.getSchedule().removeEntry(self
)
537 if self
.getSchedule() is not None:
540 def getSchedule(self
):
547 def isScheduled(self
):
548 return self
.getSchedule() is not None
550 def setValues( self
, data
):
551 """Sets all the values of the current schedule entry object from a
552 dictionary containing the following key-value pairs:
555 Please, note that this method sets ALL values which means that if
556 the given dictionary doesn't contain any of the keys the value
557 will set to a default value.
559 if data
.has_key("title"):
560 self
.setTitle(data
["title"])
561 if data
.has_key("description"):
562 self
.setDescription(data
["description"])
564 def getTitle( self
):
567 def setTitle( self
, newTitle
):
568 self
.title
= newTitle
.strip()
570 def getDescription( self
):
571 return self
.description
573 def setDescription( self
, newDesc
):
574 self
.description
= newDesc
576 def getLocator( self
):
577 if self
.getSchedule() is None:
579 loc
=self
.getSchedule().getOwner().getLocator()
580 loc
["schEntryId"]=self
.getId()
584 if self
.getSchedule() is not None:
585 self
.getSchedule().reSchedule()
593 class ConferenceSchedule(TimeSchedule
, Fossilizable
):
597 # fossilizes(IConferenceScheduleDisplayFossil, IConferenceScheduleMgmtFossil)
599 def __init__(self
,conf
):
600 TimeSchedule
.__init
__(self
,conf
)
602 def addEntry(self
,entry
,check
=2):
605 1: check and raise error in case of problem
606 2: check and adapt the owner dates"""
608 if (entry
is None) or self
.hasEntry(entry
):
610 if isinstance(entry
,LinkedTimeSchEntry
):
611 from MaKaC
.conference
import Session
, Contribution
612 if isinstance(entry
.getOwner(),Session
):
613 raise MaKaCError( _("Sessions cannot be scheduled into the event, schedule their slots instead"), _("Event"))
614 elif isinstance(entry
.getOwner(),Contribution
):
615 if entry
.getOwner().getSession() is not None:
616 raise MaKaCError( _("Cannot schedule into the event a contribution that belongs to a session"), _("Event"))
617 if not self
.getOwner().hasContribution(entry
.getOwner()):
618 raise MaKaCError( _("Cannot schedule into the event a contribution that does not belong to it"), _("Event"))
619 self
._setEntryDuration
(entry
)
620 return self
._addEntry
(entry
,check
)
622 def moveUpEntry(self
,entry
,tz
=None):
623 #not very smart, should be improved: contribs with same start date,
624 # can cause overlapings
627 tz
= self
.getTimezone()
628 entriesDay
=self
.getEntriesOnDay(entry
.getAdjustedStartDate())
629 if len(entriesDay
)<2:
632 if entry
in entriesDay
:
633 entrypos
= entriesDay
.index(entry
)
634 #if the entry is the first one...then it goes to the end.
635 if entrypos
== 0 and len(entriesDay
)>1:
636 entriesDay
[1].setStartDate(entriesDay
[0].getStartDate(), check
=0, moveEntries
=1)
638 while(i
< len(entriesDay
)):
639 entry
= entriesDay
[i
]
640 preventry
= entriesDay
[i
-1]
641 entry
.setStartDate(preventry
.getEndDate(), check
=0, moveEntries
=1)
643 entriesDay
[0].setStartDate(entriesDay
[len(entriesDay
)-1].getEndDate(), check
=0, moveEntries
=1)
645 preventry
= entriesDay
[entrypos
-1]
646 entry
.setStartDate(preventry
.getStartDate(), check
=0, moveEntries
=1)
647 preventry
.setStartDate(entry
.getEndDate(), check
=0, moveEntries
=1)
651 def moveDownEntry(self
,entry
,tz
=None):
653 tz
= self
.getTimezone()
654 entriesDay
=self
.getEntriesOnDay(entry
.getAdjustedStartDate())
655 if len(entriesDay
)<2:
658 if entry
in entriesDay
:
659 entrypos
= entriesDay
.index(entry
)
660 #if the entry is the last one...then it goes to the first place.
661 if entrypos
+1 == len(entriesDay
) and len(entriesDay
)>1:
662 entriesDay
[len(entriesDay
)-1].setStartDate(entriesDay
[0].getStartDate(), check
=0, moveEntries
=1)
664 while(i
< len(entriesDay
)-2):
665 entry
= entriesDay
[i
]
666 nextentry
= entriesDay
[i
+1]
667 nextentry
.setStartDate(entry
.getEndDate(), check
=0, moveEntries
=1)
670 nextentry
= entriesDay
[entrypos
+1]
671 nextentry
.setStartDate(entry
.getStartDate(), check
=0, moveEntries
=1)
672 entry
.setStartDate(nextentry
.getEndDate(), check
=0, moveEntries
=1)
677 class SessionSchedule(TimeSchedule
):
681 def __init__(self
,session
):
682 TimeSchedule
.__init
__(self
,session
)
684 def checkSanity( self
):
685 if self
.hasEntriesBefore(self
.getStartDate()) or self
.hasEntriesAfter(self
.getEndDate()):
686 raise TimingError( _("Sorry, cannot perform this date change: Some entries in the schedule would be outside the new dates"))
688 def addEntry(self
,entry
,check
=1):
689 if (entry
is None) or self
.hasEntry(entry
):
691 if isinstance(entry
,LinkedTimeSchEntry
):
692 from MaKaC
.conference
import SessionSlot
693 if not(isinstance(entry
.getOwner(),SessionSlot
)):
694 raise MaKaCError( _("objects of class %s cannot be scheduled into a session")%(entry
.getOwner().__class
__), _("Session Schedule"))
696 raise MaKaCError( _("objects of class %s cannot be scheduled into a session")%(entry
.__class
__), _("Session Schedule"))
697 self
._addEntry
(entry
)
699 def removeEntry(self
,entry
):
700 if entry
is None or not self
.hasEntry(entry
):
702 if entry
.getOwner() in self
.getOwner().getSlotList():
703 raise MaKaCError( _("Cannot remove a slot without removing it from the session slot list"), _("Session Schedule"))
704 self
._removeEntry
(entry
)
706 def moveEntriesBelow(self
, diff
, entriesList
):
707 """diff: the difference we have to increase/decrease each entry of the list.
708 entriesList: list of entries for applying the diff"""
710 for entry
in entriesList
:
711 entry
.setStartDate(entry
.getStartDate()+diff
, check
=0, moveEntries
=1)
714 class SlotSchedule(TimeSchedule
):
718 def __init__(self
,slot
):
719 TimeSchedule
.__init
__(self
,slot
)
721 def _setEntryDuration(self
,entry
):
722 entryDur
=entry
.getDuration()
724 ownerDur
=self
.getOwner().getContribDuration()
725 if ownerDur
is not None and ownerDur
!=timedelta(0):
726 entry
.setDuration(dur
=ownerDur
)
728 sessionDur
=self
.getOwner().getSession().getContribDuration()
729 entry
.setDuration(dur
=sessionDur
)
731 def addEntry(self
,entry
,check
=2):
734 1: check and raise error in case of problem
735 2: check and adapt the owner dates
738 tz
= self
.getTimezone();
740 owner
= self
.getOwner()
741 if (entry
is None) or self
.hasEntry(entry
):
743 if isinstance(entry
,LinkedTimeSchEntry
):
744 from MaKaC
.conference
import Contribution
745 if not(isinstance(entry
.getOwner(),Contribution
)):
746 raise MaKaCError( _("objects of class %s cannot be scheduled into a session slot"), _("Slot"))
747 if (entry
.getOwner().getSession() is None) or (not self
.getOwner().getSession().hasContribution(entry
.getOwner())):
748 raise MaKaCError( _("Cannot schedule into this session a contribution which does not belong to it"), _("Slot"))
749 if entry
.getStartDate()!=None and entry
.getStartDate() < self
.getOwner().getStartDate():
751 raise ParentTimingError( _("The entry would start at %s, which is before the start time of the time slot (%s)")%\
752 (entry
.getEndDate().strftime('%Y-%m-%d %H:%M'),\
753 self
.getOwner().getStartDate().strftime('%Y-%m-%d %H:%M')),\
756 ContextManager
.get('autoOps').append((owner
,
757 "OWNER_START_DATE_EXTENDED",
759 entry
.getAdjustedStartDate(tz
=tz
)))
760 self
.getOwner().setStartDate(entry
.getStartDate(),check
,0)
761 if entry
.getEndDate()!=None and entry
.getEndDate() > self
.getOwner().getEndDate():
763 raise ParentTimingError( _("The entry would finish at %s, which is after the end of the time slot (%s)")%\
764 (entry
.getAdjustedEndDate(tz
=tz
).strftime('%Y-%m-%d %H:%M'),\
765 self
.getOwner().getAdjustedEndDate(tz
=tz
).strftime('%Y-%m-%d %H:%M')),\
768 ContextManager
.get('autoOps').append((owner
,
769 "OWNER_END_DATE_EXTENDED",
771 entry
.getAdjustedEndDate(tz
=tz
)))
772 self
.getOwner().setEndDate(entry
.getEndDate(),check
)
773 self
._setEntryDuration
(entry
)
774 self
._addEntry
(entry
,check
)
778 def moveUpEntry(self
,entry
):
779 #not very smart, should be improved: contribs with same start date,
780 # can cause overlapings
781 entries
= self
.getEntriesOnDay(entry
.getAdjustedStartDate())
786 entrypos
= entries
.index(entry
)
787 #if the entry is the first one...then it goes to the end.
788 if entrypos
== 0 and len(entries
)>1:
789 entries
[1].setStartDate(entries
[0].getStartDate(),check
=0,moveEntries
=1)
791 while(i
< len(entries
)):
793 preventry
= entries
[i
-1]
794 entry
.setStartDate(preventry
.getEndDate(),check
=0,moveEntries
=1)
796 entries
[0].setStartDate(entries
[len(entries
)-1].getEndDate(),check
=0,moveEntries
=1)
798 preventry
= entries
[entrypos
-1]
799 entry
.setStartDate(preventry
.getStartDate(),check
=0,moveEntries
=1)
800 preventry
.setStartDate(entry
.getEndDate(),check
=0,moveEntries
=1)
804 def moveDownEntry(self
,entry
):
805 entries
= self
.getEntriesOnDay(entry
.getAdjustedStartDate())
810 entrypos
= entries
.index(entry
)
811 #if the entry is the last one...then it goes to the first place.
812 if entrypos
+1 == len(entries
) and len(entries
)>1:
813 entries
[len(entries
)-1].setStartDate(entries
[0].getStartDate(), check
=0,moveEntries
=1)
815 while(i
< len(entries
)-2):
817 nextentry
= entries
[i
+1]
818 nextentry
.setStartDate(entry
.getEndDate(),check
=0,moveEntries
=1)
821 nextentry
= entries
[entrypos
+1]
822 nextentry
.setStartDate(entry
.getStartDate(),check
=0,moveEntries
=1)
823 entry
.setStartDate(nextentry
.getEndDate(),check
=0,moveEntries
=1)
827 def moveEntriesBelow(self
, diff
, entriesList
):
828 """diff: the difference we have to increase/decrease each entry of the list.
829 entriesList: list of entries for applying the diff"""
831 for entry
in entriesList
:
832 entry
.setStartDate(entry
.getStartDate() + diff
, check
=2, moveEntries
=1)
834 def rescheduleTimes(self
, type, diff
, day
, doFit
):
838 class PosterSlotSchedule(SlotSchedule
):
840 def _setEntryDuration(self
,entry
):
841 #In the posters schedulers the duration will (by default) always be the
842 # same for every entry within the slot
843 if entry
.getOwner().getDuration() != None and entry
.getOwner().getDuration() != 0 \
844 and entry
.getOwner().getDuration().seconds
!=0:
846 ownerDur
=self
.getOwner().getContribDuration()
847 if ownerDur
is not None and \
848 (ownerDur
> timedelta(0)):
849 entry
.setDuration(dur
=ownerDur
)
851 sessionDur
=self
.getOwner().getSession().getContribDuration()
852 entry
.setDuration(dur
=sessionDur
)
854 def addEntry(self
,entry
,check
=0):
855 # check=0 is here only because we must have 3 parameters.
856 if (entry
is None) or self
.hasEntry(entry
):
858 from MaKaC
.conference
import Contribution
859 if not isinstance(entry
,LinkedTimeSchEntry
) or \
860 not isinstance(entry
.getOwner(),Contribution
):
861 raise MaKaCError( _("objects of class %s cannot be scheduled into a poster session slot")%entry
, _("Slot"))
862 if (entry
.getOwner().getSession() is None) or \
863 (not self
.getOwner().getSession().hasContribution(entry
.getOwner())):
864 raise MaKaCError( _("Cannot schedule into this session a contribution which does not belong to it"), _("Slot"))
865 self
._setEntryDuration
(entry
)
866 if entry
.isScheduled():
867 #remove it from the old schedule and add it to this one
868 entry
.getSchedule().removeEntry(entry
)
869 entry
.setStartDate(self
.getStartDate())
870 self
._entries
.append(entry
)
871 entry
.setSchedule(self
,self
._getNewEntryId
())
874 def reSchedule(self
):
875 for e
in self
._entries
:
876 if e
.getStartDate() != self
.getStartDate():
877 e
.setStartDate(self
.getStartDate())
879 class SlotSchTypeFactory
:
880 _sch
={"standard":SlotSchedule
,"poster":PosterSlotSchedule
}
883 def getScheduleKlass(cls
,id):
884 id=id.strip().lower()
885 if not cls
._sch
.has_key(id):
888 getScheduleKlass
=classmethod(getScheduleKlass
)
890 def getDefaultKlass(cls
):
891 return cls
._sch
[cls
._default
]
892 getDefaultKlass
=classmethod(getDefaultKlass
)
894 def getDefaultId(cls
):
896 getDefaultId
=classmethod(getDefaultId
)
899 return cls
._sch
.keys()
900 getIdList
=classmethod(getIdList
)
903 for (id,schKlass
) in cls
._sch
.items():
904 if sch
.__class
__==schKlass
:
907 getId
=classmethod(getId
)
909 class TimeSchEntry(SchEntry
):
912 SchEntry
.__init
__(self
)
916 def getStartDate( self
):
919 def setStartDate(self
,sDate
,check
=1, moveEntries
=0):
922 def getEndDate( self
):
925 def getDuration(self
):
928 def setDuration(self
,hours
=0,min=15, dur
=0):
931 def inDay( self
, day
):
934 def onDate( self
, day
):
938 class LinkedTimeSchEntry(TimeSchEntry
):
940 fossilizes(ILinkedTimeSchEntryDisplayFossil
,
941 ILinkedTimeSchEntryMgmtFossil
)
943 def __init__(self
,owner
):
944 SchEntry
.__init
__(self
)
947 # fermi - pass tz here.....
948 def getStartDate( self
):
949 return self
.__owner
.getStartDate()
951 def getAdjustedStartDate( self
, tz
=None ):
952 return self
.__owner
.getAdjustedStartDate(tz
)
954 def setStartDate(self
,newDate
,check
=2, moveEntries
=0):
957 1: check and raise error in case of problem
958 2: check and adapt the owner dates"""
959 return self
.getOwner().setStartDate(newDate
,check
, moveEntries
)
961 def getEndDate( self
):
962 return self
.__owner
.getEndDate()
964 def getAdjustedEndDate( self
, tz
=None ):
965 return self
.__owner
.getAdjustedEndDate(tz
)
967 def getDuration(self
):
968 return self
.__owner
.getDuration()
970 def setDuration(self
,hours
=0,minutes
=15,dur
=0,check
=2):
972 return self
.getOwner().setDuration(dur
=dur
,check
=check
)
974 return self
.getOwner().setDuration(hours
,minutes
,check
=check
)
976 def getTitle( self
):
977 return self
.__owner
.getTitle()
979 def getDescription( self
):
980 return self
.__owner
.getDescription()
982 def getOwner( self
):
985 def inDay( self
, day
):
986 """Tells whether or not the current entry occurs whithin the specified
987 day (day is tz-aware)
989 if not self
.isScheduled():
991 return self
.getStartDate().astimezone(day
.tzinfo
).date()<=day
.date() and self
.getEndDate().astimezone(day
.tzinfo
).date()>=day
.date()
993 def onDate( self
, date
):
994 """Tells whether or not the current entry occurs during the specified
997 if not self
.isScheduled():
999 return self
.getStartDate()<=date
and \
1000 self
.getEndDate()>=date
1002 def collides(self
,entry
):
1003 return (entry
.getStartDate()>=self
.getStartDate() and \
1004 entry
.getStartDate()<self
.getEndDate()) or \
1005 (entry
.getEndDate()>self
.getStartDate() and \
1006 entry
.getEndDate()<=self
.getEndDate())
1008 def getUniqueId(self
):
1009 return self
.getOwner().getUniqueId()
1012 class IndTimeSchEntry(TimeSchEntry
):
1014 def setValues( self
, data
):
1015 """Sets all the values of the current schedule entry object from a
1016 dictionary containing the following key-value pairs:
1019 year, month, day, sHour, sMinute - (str) => components of the
1020 starting date of the entry, if not specified it will
1022 durationHours, durationMinutes - (str)
1023 Please, note that this method sets ALL values which means that if
1024 the given dictionary doesn't contain any of the keys the value
1025 will set to a default value.
1027 SchEntry
.setValues(self
,data
)
1028 if data
.get("sYear", None) != None and \
1029 data
.get("sMonth", None) != None and \
1030 data
.get("sDay", None) != None and \
1031 data
.get("sHour", None) != None and \
1032 data
.get("sMinute", None) != None:
1033 self
.setStartDate(datetime(int(data
["sYear"]),\
1034 int(data
["sMonth"]),\
1035 int(data
["sDay"]), \
1036 int(data
["sHour"]),\
1037 int(data
["sMinute"])) )
1038 if data
.get("durHours",None)!=None and data
.get("durMins",None)!=None:
1039 self
.setDuration(data
["durHours"],data
["durMins"])
1041 def getTimezone( self
):
1042 return self
.getSchedule().getOwner().getTimezone()
1044 def getStartDate( self
):
1045 return self
.startDate
1047 def getAdjustedStartDate( self
, tz
=None ):
1049 tz
= self
.getTimezone()
1051 return self
.getStartDate().astimezone(timezone(tz
))
1053 def setStartDate(self
,sDate
,check
=1, moveEntries
=0):
1054 self
.startDate
=sDate
1056 if self
.isScheduled():
1057 self
.getSchedule().reSchedule()
1059 def getEndDate( self
):
1060 if self
.getStartDate() is None:
1062 return self
.startDate
+self
.duration
1064 def getAdjustedEndDate( self
, tz
=None ):
1066 tz
= self
.getTimezone()
1067 return self
.getEndDate().astimezone(timezone(tz
))
1069 def getDuration(self
):
1070 return self
.duration
1072 def setDuration(self
,hours
=0,min=15,dur
=0):
1074 self
.duration
=timedelta(hours
=int(hours
),minutes
=int(min))
1078 if self
.isScheduled():
1079 self
.getSchedule().reSchedule()
1081 def inDay( self
, day
):
1082 """Tells whether or not the current entry occurs whithin the specified
1083 day (day is tz-aware)
1085 if not self
.isScheduled():
1087 return self
.getStartDate().astimezone(day
.tzinfo
).date()<=day
.date() and self
.getEndDate().astimezone(day
.tzinfo
).date()>=day
.date()
1089 def onDate( self
, date
):
1090 """Tells whether or not the current entry occurs during the specified
1093 if not self
.isScheduled():
1095 return self
.getStartDate()<=date
and \
1096 self
.getEndDate()>=date
1099 class BreakTimeSchEntry(IndTimeSchEntry
):
1101 fossilizes(IBreakTimeSchEntryFossil
, IBreakTimeSchEntryMgmtFossil
)
1104 IndTimeSchEntry
.__init
__(self
)
1105 self
._color
="#90C0F0"
1106 self
._textColor
="#202020"
1107 self
._textColorToLink
=False
1109 def clone(self
, owner
):
1110 btse
= BreakTimeSchEntry()
1111 btse
.setValues(self
.getValues())
1112 olddate
= self
.getOwner().getStartDate()
1113 newdate
= owner
.getSchedule().getStartDate()
1114 timeDifference
= newdate
- olddate
1115 btse
.setStartDate(btse
.getStartDate()+timeDifference
)
1118 def getValues(self
):
1120 values
["startDate"] = self
.getStartDate()
1121 values
["endDate"] = self
.getEndDate()
1122 values
["durTimedelta"] = self
.getDuration()
1123 values
["description"] = self
.getDescription()
1124 values
["title"] = self
.getTitle()
1125 if self
.getOwnLocation() is not None :
1126 values
["locationName"] = self
.getLocation().getName()
1128 values
["locationName"] = ""
1129 if self
.getOwnRoom() is not None :
1130 values
["roomName"] = self
.getOwnRoom().getName()
1132 values
["roomName"] = ""
1133 values
["backgroundColor"] = self
.getColor()
1134 values
["textColor"] = self
.getTextColor()
1135 if self
.isTextColorToLinks():
1136 values
["textcolortolinks"]="True"
1140 def setValues( self
, data
, check
=2, moveEntriesBelow
=0, tz
='UTC'):
1141 from MaKaC
.conference
import CustomLocation
, CustomRoom
1142 # In order to move the entries below, it is needed to know the diff (we have to move them)
1143 # and the list of entries to move. It's is needed to take those datas in advance because they
1144 # are going to be modified before the moving.
1145 if moveEntriesBelow
== 1 and self
.getSchedule():
1146 oldStartDate
=copy
.copy(self
.getStartDate())
1147 oldDuration
=copy
.copy(self
.getDuration())
1148 i
=self
.getSchedule().getEntries().index(self
)+1
1149 entriesList
= self
.getSchedule().getEntries()[i
:]
1150 if data
.get("startDate", None) != None:
1151 self
.setStartDate(data
["startDate"], 0)
1152 elif data
.get("sYear", None) != None and \
1153 data
.get("sMonth", None) != None and \
1154 data
.get("sDay", None) != None and \
1155 data
.get("sHour", None) != None and \
1156 data
.get("sMinute", None) != None:
1157 #########################################
1158 # Fermi timezone awareness #
1159 # We have to store as UTC, relative #
1160 # to the timezone of the conference. #
1161 #########################################
1162 d
= timezone(tz
).localize(datetime(int(data
["sYear"]),
1163 int(data
["sMonth"]),
1166 int(data
["sMinute"])))
1167 sDate
= d
.astimezone(timezone('UTC'))
1168 self
.setStartDate(sDate
)
1169 ########################################
1170 # Fermi timezone awareness #
1171 # We have to store as UTC, relative #
1172 # to the timezone of the conference. #
1173 ########################################
1175 if data
.get("durTimedelta", None) != None:
1176 self
.setDuration(check
=0, dur
=data
["durTimedelta"])
1177 elif data
.get("durHours","").strip()!="" and data
.get("durMins","").strip()!="":
1178 self
.setDuration(data
["durHours"], data
["durMins"], 0)
1180 h
=data
.get("durHours","").strip()
1181 m
=data
.get("durMins","").strip()
1186 if h
!="0" or m
!="0":
1187 self
.setDuration(int(h
), int(m
), 0)
1193 if self
.getDuration() is None or self
.getDuration()==0:
1194 self
.setDuration("0", "15", 0)
1195 if data
.get( "locationName", "" ).strip() == "":
1196 self
.setLocation( None )
1198 loc
= self
.getOwnLocation()
1200 loc
= CustomLocation()
1201 self
.setLocation( loc
)
1202 loc
.setName( data
["locationName"] )
1203 loc
.setAddress( data
.get("locationAddress", "") )
1204 if data
.get( "roomName", "" ).strip() == "":
1205 self
.setRoom( None )
1207 room
= self
.getOwnRoom()
1210 self
.setRoom( room
)
1211 room
.setName( data
["roomName"] )
1212 room
.retrieveFullName(data
.get('locationName', ''))
1213 self
._color
=data
.get("backgroundColor","#90C0F0")
1214 if data
.has_key("autotextcolor"):
1215 self
._textColor
=utils
.getTextColorFromBackgroundColor(self
.getColor())
1217 self
._textColor
=data
.get("textColor","#202020")
1218 self
.setTextColorToLinks(data
.has_key("textcolortolinks"))
1219 if data
.has_key("title"):
1220 self
.setTitle(data
["title"])
1221 if data
.has_key("description"):
1222 self
.setDescription(data
["description"])
1224 # now check if the slot new time is compatible with its parents limits
1226 if self
.getSchedule() and self
.getSchedule().isOutside(self
):
1227 raise TimingError( _("This action would move the break out of its parents schedule dates"))
1229 if self
.getSchedule() and self
.getSchedule().isOutside(self
):
1231 # syncrho is not modifying the dates of the session slot. Fit does.
1232 if isinstance(self
.getSchedule(), SlotSchedule
):
1233 self
.getSchedule().getOwner().fit()
1234 if moveEntriesBelow
== 1 and self
.getSchedule():
1235 diff
= (self
.getStartDate() - oldStartDate
) + (self
.getDuration() - oldDuration
)
1236 self
.getSchedule().moveEntriesBelow(diff
, entriesList
)
1237 self
.notifyModification(False)
1240 def getLocationParent( self
):
1241 if self
.getSchedule() is not None:
1242 return self
.getSchedule().getOwner()
1245 def setLocation(self
, loc
):
1247 self
.notifyModification()
1249 def getLocation(self
):
1250 if self
.getOwnLocation() is None:
1251 return self
.getInheritedLocation()
1252 return self
.getOwnLocation()
1254 def getInheritedLocation(self
):
1255 locParent
= self
.getLocationParent()
1257 return locParent
.getLocation();
1261 def getOwnLocation(self
):
1265 except AttributeError:
1269 def setRoom(self
, room
):
1271 self
.notifyModification()
1274 if self
.getOwnRoom() is None:
1275 return self
.getInheritedRoom()
1276 return self
.getOwnRoom()
1278 def getInheritedRoom(self
):
1279 locParent
= self
.getLocationParent()
1281 return locParent
.getRoom();
1285 def getOwnRoom(self
):
1289 except AttributeError:
1294 if self
.getSchedule() is not None:
1295 return self
.getSchedule().getOwner()
1298 def _verifyDuration(self
,check
=2):
1300 if self
.getSchedule() is not None:
1301 owner
= self
.getSchedule().getOwner()
1302 if self
.getEndDate() > owner
.getEndDate():
1304 raise ParentTimingError( _("The break cannot end after (%s) its parent (%s)")%\
1305 (self
.getEndDate().strftime('%Y-%m-%d %H:%M'),\
1306 owner
.getEndDate().strftime('%Y-%m-%d %H:%M')),\
1309 # update the schedule
1310 owner
.setEndDate(self
.getEndDate(),check
)
1312 def setDuration(self
, hours
=0, min=15, check
=2,dur
=0):
1315 IndTimeSchEntry
.setDuration(self
,hours
,min)
1317 IndTimeSchEntry
.setDuration(self
,dur
=dur
)
1318 self
._verifyDuration
(check
)
1319 self
.notifyModification()
1321 def setStartDate(self
, newDate
,check
=2, moveEntries
=0):
1324 # tz = str(newDate.tzinfo)
1327 if self
.getSchedule() is not None:
1328 owner
= self
.getSchedule().getOwner()
1329 if newDate
< owner
.getStartDate():
1331 raise ParentTimingError( _("The break \"%s\" cannot start before (%s) its parent (%s)")%\
1333 newDate
.astimezone(timezone(self
.getTimezone())).strftime('%Y-%m-%d %H:%M'),\
1334 owner
.getAdjustedStartDate().strftime('%Y-%m-%d %H:%M')),\
1337 # update the schedule
1338 owner
.setStartDate(newDate
, check
)
1339 ContextManager
.get('autoOps').append((self
, "OWNER_START_DATE_EXTENDED",
1340 owner
, owner
.getAdjustedStartDate()))
1341 if newDate
> owner
.getEndDate():
1343 raise ParentTimingError("The break cannot start after (%s) its parent ends (%s)"%\
1344 (newDate
.astimezone(timezone(self
.getTimezone())).strftime('%Y-%m-%d %H:%M'),\
1345 owner
.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
1348 # update the schedule
1349 owner
.setEndDate(newDate
,check
)
1350 ContextManager
.get('autoOps').append((self
, "OWNER_END_DATE_EXTENDED",
1351 owner
, owner
.getAdjustedEndDate()))
1352 IndTimeSchEntry
.setStartDate(self
, newDate
,check
)
1353 # Check that after modifying the start date, the end date is still within the limits of the slot
1354 if self
.getSchedule() and self
.getEndDate() > owner
.getEndDate():
1356 raise ParentTimingError("The break cannot end after (%s) its parent ends (%s)"%\
1357 (self
.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M'),\
1358 owner
.getAdjustedEndDate().strftime('%Y-%m-%d %H:%M')),\
1361 # update the schedule
1362 owner
.setEndDate(self
.getEndDate(),check
)
1363 ContextManager
.get('autoOps').append((self
, "OWNER_END_DATE_EXTENDED",
1364 owner
, owner
.getAdjustedEndDate()))
1365 self
.notifyModification(cleanCache
= self
.getSchedule() is not None)
1367 def setColor(self
,newColor
):
1368 self
._color
=newColor
1369 self
.notifyModification()
1376 except AttributeError:
1377 self
._color
="#AADDEE"
1381 def setTextColor(self
,newColor
):
1382 self
._textColor
=newColor
1383 self
.notifyModification()
1385 def getTextColor(self
):
1389 except AttributeError:
1390 self
._textColor
="#202020"
1391 return self
._textColor
1393 def setTextColorToLinks(self
,v
):
1394 self
._textColorToLink
=v
1395 self
.notifyModification()
1397 def isTextColorToLinks(self
):
1399 if self
._textColorToLink
:
1401 except AttributeError:
1402 self
._textColorToLink
=False
1403 return self
._textColorToLink
1406 TrashCanManager().add(self
)
1409 TrashCanManager().remove(self
)
1411 def getUniqueId(self
):
1412 return self
.getOwner().getUniqueId() + "brk" + self
.getId()
1414 def notifyModification(self
, cleanCache
= True):
1415 IndTimeSchEntry
.notifyModification(self
)
1416 if cleanCache
and self
.getOwner() and not ContextManager
.get('clean%s'%self
.getUniqueId(), False):
1417 ScheduleToJson
.cleanCache(self
)
1418 ContextManager
.set('clean%s'%self
.getUniqueId(), True)
1421 class ContribSchEntry(LinkedTimeSchEntry
):
1423 fossilizes(IContribSchEntryDisplayFossil
,
1424 IContribSchEntryMgmtFossil
)
1426 def __init__(self
, owner
):
1427 LinkedTimeSchEntry
.__init
__(self
, owner
)
1429 def setSchedule(self
,sch
,id):
1430 status
=self
.getOwner().getCurrentStatus()
1431 from MaKaC
.conference
import ContribStatusWithdrawn
,ContribStatusNotSch
,ContribStatusSch
1432 if isinstance(status
,ContribStatusWithdrawn
) and sch
is not None:
1433 raise MaKaCError( _("Cannot schedule a contribution which has been withdrawn"), _("Contribution"))
1434 LinkedTimeSchEntry
.setSchedule(self
,sch
,id)
1436 newStatus
=ContribStatusNotSch(self
.getOwner())
1438 newStatus
=ContribStatusSch(self
.getOwner())
1439 self
.getOwner().setStatus(newStatus
)
1442 return self
.getOwner().getRoom()
1444 def setRoom(self
, room
):
1445 self
.getOwner().setRoom(room
)
1447 def getLocation(self
):
1448 return self
.getOwner().getLocation()
1450 def setLocation(self
, loc
):
1451 self
.getOwner().setLocation(loc
)
1453 def getOwnRoom(self
):
1454 return self
.getOwner().getOwnRoom()
1456 def getOwnLocation(self
):
1457 return self
.getOwner().getOwnLocation()
1460 class ScheduleToJson(object):
1462 _cacheEntries
= GenericCache("ConfTTEntries")
1463 _cache
= GenericCache("ConfTT")
1466 def get_versioned_key(cache
, key
, timezone
):
1467 num
= int(cache
.get('_version-%s' % key
, 0))
1468 return '%s.%d.%s' % (key
, num
, timezone
)
1473 return not ContextManager
.get('offlineMode', False)
1476 def bump_cache_version(cache
, key
):
1477 vkey
= '_version-%s' % key
1478 cache
.set(vkey
, int(cache
.get(vkey
, 0)) + 1)
1481 def obtainFossil(cls
, entry
, tz
, fossilInterface
=None, mgmtMode
=False, useAttrCache
=False):
1483 if mgmtMode
or (not isinstance(entry
, BreakTimeSchEntry
) and not entry
.getOwner().getAccessController().isFullyPublic()):
1484 # We check if it is fully public because it could be some material protected
1485 # that would create a security hole if we cache it
1486 result
= entry
.fossilize(interfaceArg
= fossilInterface
, useAttrCache
= useAttrCache
, tz
= tz
, convert
=True)
1488 cache_key
= cls
.get_versioned_key(cls
._cacheEntries
, entry
.getUniqueId(), tz
)
1490 result
= cls
._cacheEntries
.get(cache_key
) if cls
.use_cache
else None
1493 result
= entry
.fossilize(interfaceArg
= fossilInterface
, useAttrCache
= useAttrCache
, tz
= tz
, convert
=True)
1495 cls
._cacheEntries
.set(cache_key
, result
, timedelta(minutes
=5))
1500 def processEntry(obj
, tz
, aw
, mgmtMode
= False, useAttrCache
= False):
1503 if isinstance(obj
, BreakTimeSchEntry
):
1504 entry
= ScheduleToJson
.obtainFossil(obj
, tz
, IBreakTimeSchEntryMgmtFossil
, mgmtMode
, useAttrCache
)
1505 elif isinstance(obj
, ContribSchEntry
):
1506 entry
= ScheduleToJson
.obtainFossil(obj
, tz
, IContribSchEntryMgmtFossil
, mgmtMode
, useAttrCache
)
1507 elif isinstance(obj
, LinkedTimeSchEntry
):
1508 entry
= ScheduleToJson
.obtainFossil(obj
, tz
, ILinkedTimeSchEntryMgmtFossil
, mgmtMode
, useAttrCache
)
1510 entry
= ScheduleToJson
.obtainFossil(obj
, tz
, None, mgmtMode
, useAttrCache
)
1512 # the fossils used for the display of entries
1513 # will be taken by default, since they're first
1514 # in the list of their respective Fossilizable
1516 entry
= ScheduleToJson
.obtainFossil(obj
, tz
, None, mgmtMode
, useAttrCache
)
1520 # sessions that are no poster sessions will be expanded
1521 if entry
['entryType'] == 'Session':
1523 sessionSlot
= obj
.getOwner()
1525 # get session content
1527 for contrib
in sessionSlot
.getSchedule().getEntries():
1528 if ScheduleToJson
.checkProtection(contrib
, aw
):
1530 if isinstance(contrib
, ContribSchEntry
):
1531 contribData
= ScheduleToJson
.obtainFossil(contrib
, tz
, IContribSchEntryMgmtFossil
, mgmtMode
, useAttrCache
)
1532 elif isinstance(contrib
, BreakTimeSchEntry
):
1533 contribData
= ScheduleToJson
.obtainFossil(contrib
, tz
, IBreakTimeSchEntryMgmtFossil
, mgmtMode
, useAttrCache
)
1535 contribData
= ScheduleToJson
.obtainFossil(contrib
, tz
, None, mgmtMode
, useAttrCache
)
1537 # the fossils used for the display of entries
1538 # will be taken by default, since they're first
1539 # in the list of their respective Fossilizable
1541 contribData
= ScheduleToJson
.obtainFossil(contrib
, tz
, None, mgmtMode
, useAttrCache
)
1543 entries
[contribData
['id']] = contribData
1545 entry
['entries'] = entries
1550 def checkProtection(obj
, aw
):
1551 if aw
is None or ContextManager
.get('offlineMode', False):
1554 from MaKaC
.conference
import SessionSlot
1556 canBeDisplayed
= False
1557 if isinstance(obj
, BreakTimeSchEntry
):
1558 canBeDisplayed
= True
1559 else: #contrib or session slot
1560 owner
= obj
.getOwner()
1561 if isinstance(owner
, SessionSlot
) and owner
.canView(aw
):
1562 canBeDisplayed
= True
1563 elif not owner
.isProtected() or owner
.canAccess(aw
): #isProtected avoids checking access if public
1564 canBeDisplayed
= True
1566 return canBeDisplayed
1569 def isOnlyWeekend(days
):
1571 It checks if the event takes place only during the weekend
1573 # If there are more than 2 days, there is at least one day that is not part of the weekend
1578 if (datetime
.strptime(day
, "%Y%m%d").weekday() not in [5, 6]):
1583 def process(cls
, schedule
, tz
, aw
, days
=None, mgmtMode
=False, useAttrCache
=False, hideWeekends
=False):
1586 if cls
.use_cache
and not days
and schedule
.getOwner().getAccessController().isFullyPublic() and not mgmtMode
:
1587 scheduleDict
= cls
._cache
.get(cls
.get_versioned_key(cls
._cache
, schedule
.getOwner().getUniqueId(), tz
))
1589 if not scheduleDict
:
1591 fullTT
= False # This flag is used to indicate that we must save the general cache (not entries). When
1592 if not days
: # asking only one day, we don't need to cache (it can generate issues)
1594 days
= iterdays(schedule
.getAdjustedStartDate(tz
), schedule
.getAdjustedEndDate(tz
))
1596 dates
= [d
.strftime("%Y%m%d") for d
in days
]
1598 # Generating the days dictionnary
1600 scheduleDict
[d
] = {}
1602 # Filling the day dictionnary with entries
1603 for obj
in schedule
.getEntries():
1605 if ScheduleToJson
.checkProtection(obj
, aw
):
1606 day
= obj
.getAdjustedStartDate(tz
).strftime("%Y%m%d")
1607 # verify that start date is in dates
1609 genId
, resultData
= ScheduleToJson
.processEntry(obj
, tz
, aw
, mgmtMode
, useAttrCache
)
1610 scheduleDict
[day
][genId
] = resultData
1611 if cls
.use_cache
and fullTT
and schedule
.getOwner().getAccessController().isFullyPublic() and not mgmtMode
:
1612 cls
._cache
.set(cls
.get_versioned_key(cls
._cache
, schedule
.getOwner().getUniqueId(), tz
), scheduleDict
,
1613 timedelta(minutes
=5))
1615 if hideWeekends
and not ScheduleToJson
.isOnlyWeekend(scheduleDict
.keys()):
1616 for entry
in scheduleDict
.keys():
1617 weekDay
= datetime
.strptime(entry
, "%Y%m%d").weekday()
1618 if scheduleDict
[entry
] == {} and (weekDay
== 5 or weekDay
== 6):
1619 del scheduleDict
[entry
]
1624 def sort_dict(dict):
1626 sorted_keys
= dict.keys()
1629 for key
in sorted_keys
:
1630 new_dict
[key
] = dict[key
]
1635 def cleanCache(cls
, obj
, cleanConferenceCache
=True):
1636 cls
.bump_cache_version(cls
._cacheEntries
, obj
.getUniqueId())
1637 if cleanConferenceCache
:
1638 cls
.cleanConferenceCache(obj
.getOwner().getConference())
1641 def cleanConferenceCache(cls
, obj
):
1642 cls
.bump_cache_version(cls
._cache
, obj
.getUniqueId())