VC: Fix error on clone page for legacy-ID events
[cds-indico.git] / indico / modules / upcoming.py
blob428aa8446e370c1a5cdcd36fbeb25a04d4676293
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/>.
17 """
18 *Upcoming events* module
19 """
21 import os, datetime, pickle, operator, time
22 from pytz import timezone
23 from persistent import Persistent
25 from MaKaC.common.cache import GenericCache
26 from MaKaC.common import timezoneUtils, indexes
28 from MaKaC.common.fossilize import Fossilizable, fossilizes
29 from MaKaC.fossils.modules import IObservedObjectFossil
30 import MaKaC.conference as conference
32 from indico.core.logger import Logger
33 from MaKaC.common.utils import formatTime, formatDateTime
34 import MaKaC.webinterface.wcomponents as wcomponents
36 from indico.util.i18n import _
37 from indico.modules import Module
40 class ObservedObject(Persistent, Fossilizable):
42 fossilizes(IObservedObjectFossil)
44 def __init__(self, obj, weight, advertisingDelta):
45 """
46 obj - the object to encapsulate
47 weight - the weight that is associated with
48 the object
49 """
51 self.obj = obj
52 self.weight = weight
53 self.advertisingDelta = advertisingDelta
55 def getObject(self):
56 return self.obj
58 def getWeight(self):
59 return self.weight
61 def getAdvertisingDelta(self):
62 return self.advertisingDelta
64 class UpcomingEventsModule(Module):
65 """
66 This module contains all the information and logics that support
67 the *upcoming events* feature.
68 """
70 id = "upcoming_events"
72 def __init__(self):
74 # id, weight dictionary = id is the category id and
75 # weight determines the position of the categories'
76 # events in the list
77 self._objects = []
78 self._maxEvents = 10
79 self._ttl = datetime.timedelta(minutes=5)
81 @property
82 def _cache(self):
83 return GenericCache('UpcomingEvents')
85 def setCacheTTL(self, ttl):
86 self._ttl = ttl
87 self._cache.delete('public')
89 def getCacheTTL(self):
90 if not hasattr(self, '_ttl'):
91 self._ttl = datetime.timedelta(minutes=5)
92 return self._ttl
94 def setNumberItems(self, number):
95 self._maxEvents = number
96 self._cache.delete('public')
98 def getNumberItems(self):
99 return self._maxEvents
101 def addObject(self, object, weight, delta):
103 category - Category object
104 weight - integer (negative allowed), zero by default
106 self._objects.append(ObservedObject(object, weight, delta))
107 self._cache.delete('public')
108 self._p_changed = 1
110 def removeObject(self, obj):
111 element = None
112 for observed in self._objects:
113 if observed.getObject() == obj:
114 element = observed
115 break
117 if not element:
118 raise Exception('Element not in list')
119 self._objects.remove(element)
120 self._cache.delete('public')
121 self._p_changed = 1
123 def hasObject(self, obj):
124 for observed in self._objects:
125 if observed.getObject() == obj:
126 return True
127 break
129 return False
131 def getObjectList(self):
132 return self._objects
134 def processEvent(self, date, eventObj, obj, objDict):
136 if (not eventObj.hasAnyProtection() and
137 (date > (eventObj.getStartDate() - obj.getAdvertisingDelta()))):
138 weight = float(obj.getWeight())
139 if not objDict.has_key(weight):
140 objDict[weight] = []
142 objDict[weight].append(eventObj)
145 def _processEventDisplay(self, event):
147 dateTime = event.getStartDate()
149 if dateTime < timezoneUtils.nowutc():
150 status = 'ongoing'
151 # return end date instead (for 'ongoing till...')
152 dateTime = event.getEndDate()
153 elif dateTime.date() == timezoneUtils.nowutc().date():
154 status = 'today'
155 else:
156 status = 'future'
158 return (status, dateTime, event.getTitle(), event.getId())
160 def getUpcomingEventList(self):
162 # check if there's a valid cached copy first
163 resultList = self._cache.get('public')
165 if resultList is None:
166 resultList = map(self._processEventDisplay, self._generateList())
167 self._cache.set('public', resultList, self.getCacheTTL())
169 return resultList
171 def _generateList(self, date=None):
173 if not date:
174 date = timezoneUtils.nowutc()
176 categDateIdx = indexes.IndexesHolder().getById('categoryDate')
178 objDict = {}
180 for obj in self._objects:
181 wrappedObj = obj.getObject()
182 if isinstance(wrappedObj, conference.Conference):
183 self.processEvent(date, wrappedObj, obj, objDict)
184 elif isinstance(wrappedObj, conference.Category):
185 events = categDateIdx.getObjectsIn(
186 wrappedObj.getId(),
187 date, date + obj.getAdvertisingDelta())
188 for conf in events:
189 self.processEvent(date, conf, obj, objDict)
191 resultList = []
192 keys = objDict.keys()
193 keys.sort(reverse=True)
195 for weight in keys:
196 sortedEvents = objDict[weight]
197 sortedEvents.sort(key=operator.attrgetter('startDate'))
198 for elem in sortedEvents:
199 resultList.append(elem)
200 if len(resultList) == self._maxEvents:
201 break
203 # sort again, so that the final result is an ordered list
204 # that is suitable for display
205 resultList.sort(key=operator.attrgetter('startDate'))
207 Logger.get('upcoming_events').info("Regenerated upcoming event cache")
209 return resultList
212 class WUpcomingEvents(wcomponents.WTemplated):
214 def formatDateTime(self, dateTime):
215 now = timezoneUtils.nowutc().astimezone(self._timezone)
217 if dateTime.date() == now.date():
218 return _("today") + " " + formatTime(dateTime.time())
219 elif dateTime.date() == (now + datetime.timedelta(days=1)).date():
220 return _("tomorrow") + " " + formatTime(dateTime.time())
221 elif dateTime < (now + datetime.timedelta(days=6)):
222 return formatDateTime(dateTime, format="EEEE H:mm")
223 elif dateTime.date().year == now.date().year:
224 return formatDateTime(dateTime, format="d MMM")
225 else:
226 return formatDateTime(dateTime, format="d MMM yyyy")
228 def _getUpcomingEvents(self):
229 # Just convert UTC to display timezone
231 return map(lambda x: (x[0], x[1].astimezone(self._timezone), x[2], x[3]),
232 self._list)
234 def getVars(self):
235 vars = wcomponents.WTemplated.getVars(self)
236 vars['upcomingEvents'] = self._getUpcomingEvents()
237 return vars
239 def __init__(self, timezone, upcoming_list):
240 self._timezone = timezone
241 self._list = upcoming_list
242 wcomponents.WTemplated.__init__(self)