source: indico/indico/MaKaC/plugins/Collaboration/http_api.py @ 8ba2f9

hello-world-walkthroughipv6v0.98-seriesv0.98.2v0.98.3v0.99v1.0v1.1
Last change on this file since 8ba2f9 was 8ba2f9, checked in by Pedro Ferreira <jose.pedro.ferreira@…>, 15 months ago

[MIN] VS iCal Ouput: Condensed prefixes

  • Added utils class to iCal export for Collaboration
  • Condenses prefixes to iCal event summaries
  • Property mode set to 100644
File size: 11.3 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, 2011 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
21import icalendar as ical
22from datetime import timedelta
23
24from indico.web.http_api import HTTPAPIHook, DataFetcher
25from indico.web.http_api.ical import ICalSerializer
26from indico.web.http_api.util import get_query_parameter
27from indico.web.http_api.responses import HTTPAPIError
28from indico.web.wsgi import webinterface_handler_config as apache
29from indico.util.fossilize import fossilize, IFossil
30from indico.util.fossilize.conversion import Conversion
31
32from MaKaC.webinterface.rh.collaboration import RCCollaborationAdmin
33from MaKaC.common.indexes import IndexesHolder
34from MaKaC.plugins.Collaboration.RecordingManager.common import createIndicoLink
35from MaKaC.plugins.Collaboration.collaborationTools import CollaborationTools
36from MaKaC.conference import ConferenceHolder
37from MaKaC.plugins.Collaboration.base import SpeakerStatusEnum
38from MaKaC.plugins.Collaboration.fossils import ICollaborationMetadataFossil
39
40
41globalHTTPAPIHooks = ['CollaborationAPIHook', 'CollaborationExportHook', 'VideoEventHook']
42
43
44def serialize_collaboration_alarm(fossil, now):
45    alarm = ical.Alarm()
46    trigger = timedelta(minutes=-int(fossil['alarm']))
47    alarm.set('trigger', trigger)
48    alarm.set('action', 'DISPLAY')
49    alarm.set('summary', VideoExportUtilities.getCondensedPrefix(fossil['type'],
50                                                                 fossil['status']) + fossil['title'].decode('utf-8'))
51    alarm.set('description', str(fossil['url']))
52
53    return alarm
54
55
56def serialize_collaboration(cal, fossil, now):
57    event = ical.Event()
58    url = str(fossil['url'])
59    event.set('uid', 'indico-collaboration-%s@cern.ch' % fossil['uniqueId'])
60    event.set('dtstamp', now)
61    event.set('dtstart', fossil['startDate'])
62    event.set('dtend', fossil['endDate'])
63    event.set('url', url)
64    event.set('categories', "VideoService - " + fossil['type'])
65    event.set('summary', VideoExportUtilities.getCondensedPrefix(fossil['type'],
66                                                                 fossil['status']) + fossil['title'].decode('utf-8'))
67    event.set('description', url)
68
69    # If there is an alarm required, add a subcomponent to the Event
70    if fossil.has_key('alarm'):
71        event.add_component(serialize_collaboration_alarm(fossil, now))
72    cal.add_component(event)
73
74
75# the iCal serializer needs some extra info on how to display things
76ICalSerializer.register_mapper('collaborationMetadata', serialize_collaboration)
77
78
79class VideoExportUtilities():
80
81    SERVICE_MAP = {
82        'CERNMCU': 'MCU',
83        'WebcastRequest': 'W',
84        'RecordingRequest': 'R'
85    }
86    STATUS_MAP = {
87        'Accepted': 'A',
88        'Rejected': 'R',
89        'Pending': 'P'
90    }
91
92    @staticmethod
93    def getCondensedPrefix(service, status):
94        """ Condense down the summary lines based on the above dictionaries
95            for easier reading in calendar applications.
96        """
97        prefix = ""
98
99        if VideoExportUtilities.SERVICE_MAP.has_key(service):
100            prefix += VideoExportUtilities.SERVICE_MAP.get(service)
101        else:
102            prefix += service
103
104        prefix += '-'
105
106        if VideoExportUtilities.STATUS_MAP.has_key(status):
107            prefix += VideoExportUtilities.STATUS_MAP.get(status)
108        else:
109            prefix += status
110
111        return (prefix + ": ")
112
113
114class CollaborationAPIHook(HTTPAPIHook):
115    PREFIX = 'api'
116    TYPES = ('recordingManager',)
117    RE = r'createLink'
118    GUEST_ALLOWED = False
119    VALID_FORMATS = ('json',)
120    COMMIT = True
121    HTTP_POST = True
122
123    def _hasAccess(self, aw):
124        return RCCollaborationAdmin.hasRights(user=aw.getUser())
125
126    def _getParams(self):
127
128        super(CollaborationAPIHook, self)._getParams()
129        self._indicoID = get_query_parameter(self._queryParams, ['iid', 'indicoID'])
130        self._cdsID = get_query_parameter(self._queryParams, ['cid', 'cdsID'])
131
132    def api_recordingManager(self, aw):
133        if not self._indicoID or not self._cdsID:
134            raise HTTPAPIError('A required argument is missing.', apache.HTTP_BAD_REQUEST)
135
136        success = createIndicoLink(self._indicoID, self._cdsID)
137        return {'success': success}
138
139
140class CollaborationExportHook(HTTPAPIHook):
141    TYPES = ('eAgreements', )
142    RE = r'(?P<confId>\w+)'
143    GUEST_ALLOWED = False
144    VALID_FORMATS = ('json', 'jsonp', 'xml')
145
146    def _hasAccess(self, aw):
147        return RCCollaborationAdmin.hasRights(user=aw.getUser())
148
149    def _getParams(self):
150        super(CollaborationExportHook, self)._getParams()
151        self._conf = ConferenceHolder().getById(self._pathParams['confId'], True)
152        if not self._conf:
153            raise HTTPAPIError('Conference does not exist.', apache.HTTP_BAD_REQUEST)
154
155    def export_eAgreements(self, aw):
156        manager = self._conf.getCSBookingManager()
157        requestType = CollaborationTools.getRequestTypeUserCanManage(self._conf, aw.getUser())
158        contributions = manager.getContributionSpeakerByType(requestType)
159        for cont, speakers in contributions.items():
160            for spk in speakers:
161                sw = manager.getSpeakerWrapperByUniqueId('%s.%s' % (cont, spk.getId()))
162                if not sw:
163                    continue
164                signed = None
165                if sw.getStatus() in (SpeakerStatusEnum.FROMFILE, SpeakerStatusEnum.SIGNED):
166                    signed = True
167                elif sw.getStatus() == SpeakerStatusEnum.REFUSED:
168                    signed = False
169                yield {
170                    'confId': sw.getConference().getId(),
171                    'contrib': cont,
172                    'type': sw.getRequestType(),
173                    'status': sw.getStatus(),
174                    'signed': signed,
175                    'speaker': {
176                        'id': spk.getId(),
177                        'name': spk.getFullName(),
178                        'email': spk.getEmail()
179                    }
180                }
181
182
183class VideoEventHook(HTTPAPIHook):
184    """
185    This has been defined as a separate hook to CollaborationExportHook et al
186    due to the different input expected for both. It would be beneficial to
187    find a way to amalgamate the two at a later date.
188    """
189
190    TYPES = ('video', )
191    RE = r'(?P<idlist>\w+(?:-\w+)*)'
192    DEFAULT_DETAIL = 'all'
193    MAX_RECORDS = { # @TODO: Ascertain reasonable limits for each section.
194        'all': 100000,
195        'vidyo': 50000,
196        'webcast': 50000,
197        'mcu': 50000,
198        'evo': 50000
199    }
200    GUEST_ALLOWED = False
201
202    def _getParams(self):
203        super(VideoEventHook, self)._getParams()
204
205        """ In this case, idlist refers to the different indicies which can
206            be called, e.g: vidyo, evo, mcu etc.
207        """
208        self._idList = self._pathParams['idlist'].split('-')
209
210        if not self._queryParams.has_key('alarms'):
211            self._alarms = None
212        else:
213            self._alarms = get_query_parameter(self._queryParams, ['alarms'], 0, True)
214
215    def export_video(self, aw):
216        expInt = VideoEventFetcher(aw, self)
217        return expInt.video(self._idList, self._alarms)
218
219
220class VideoEventFetcher(DataFetcher):
221    DETAIL_INTERFACES = {
222        'all' : ICollaborationMetadataFossil
223    }
224    ID_TO_IDX = {
225        'all' : 'all',
226        'vidyo' : 'Vidyo',
227        'webcast' : 'WebcastRequest',
228        'recording' : 'RecordingRequest',
229        'mcu' : 'CERNMCU',
230        'evo' : 'EVO'
231    }
232
233    def __init__(self, aw, hook):
234        super(VideoEventFetcher, self).__init__(aw, hook)
235        self._alarm = None
236
237    def _postprocess(self, obj, fossil, iface):
238        if self._alarm is not None:
239            fossil['alarm'] = self._alarm
240
241        return fossil
242
243    def video(self, idList, alarm = None):
244        idx = IndexesHolder().getById('collaboration');
245        bookings = []
246        dateFormat = '%d/%m/%Y'
247        self._alarm = alarm
248
249        for id in idList:
250            tempBookings = idx.getBookings(self.ID_TO_IDX[id], "conferenceStartDate",
251                                           self._orderBy, self._fromDT, self._toDT,
252                                           'UTC', False, None, None, False, dateFormat)
253            bookings.extend(tempBookings.getResults())
254
255        """ Iterate the bookings and yield the results for fossilization, the form
256            by this point should be objs[i] = tuple('arranger', [CSBooking Objects]).
257        """
258        def _iter_bookings(objs):
259            for obj in objs:
260                for bk in obj[1]:
261                    bk._conf = obj[0] # Ensure all CSBookings are aware of their Conference
262
263                    """ This is for plugins whose structure include 'talkSelected',
264                        examples of which in CERN Indico being WebcastRequest and
265                        RecordingRequest.
266                    """
267                    if bk.hasTalkSelection():
268                        ts = bk.getTalkSelectionList()
269                        contributions = []
270
271                        if ts is None: # No individual talks, therefore an event for every contribution
272                            contributions = bk._conf.getContributionList()
273                        else:
274                            for contribId in ts:
275                                tempContrib = bk._conf.getContributionById(contribId)
276                                contributions.append(tempContrib)
277
278                        if len(contributions) == 0: # If we are here, no contributions but a request exists.
279                            bk.setStartDate(bk._conf.getStartDate())
280                            bk.setEndDate(bk._conf.getEndDate())
281                            yield bk
282                        else: # contributions is the list of all to be exported now
283                            for contrib in contributions:
284                                if contrib.isScheduled():
285                                    bk.setStartDate(contrib.getStartDate())
286                                    bk.setEndDate(contrib.getEndDate())
287                                else:
288                                    bk.setStartDate(bk._conf.getStartDate())
289                                    bk.setEndDate(bk._conf.getEndDate())
290                                yield bk
291
292                        continue
293
294                    yield bk
295
296        """ Simple filter, as this method can return None for Pending and True
297            for accepted, both are valid booking statuses for exporting.
298        """
299        def filter(obj):
300            return obj.getAcceptRejectStatus() is not False
301
302        iface = self.DETAIL_INTERFACES.get('all')
303
304        for booking in self._process(_iter_bookings(bookings), filter, iface):
305            yield booking
Note: See TracBrowser for help on using the repository browser.