source: indico/indico/MaKaC/plugins/Collaboration/RecordingManager/common.py @ 0cb016

burotelhello-world-walkthroughipv6new-webexv0.97-seriesv0.98-seriesv0.98.2v0.98.3v0.98b1v0.98b2v0.99v1.0v1.1
Last change on this file since 0cb016 was 0cb016, checked in by Jeremy Herr <jeremy.herr@…>, 3 years ago

[IMP] implemented web upload to micala server

  • Got web upload working to micala server
  • Added micalaUploadURL to options.py
  • Found bug ... whenever there are double quotes in a talk title, it caused a JavaScript? error because I am passing the list RMTalkList to JavaScript?. I did string.replace('"', '%22') to fix it
  • In services.py, added conditional, so submitMicalaMetadata is only called if the talk in question is a web_lecture
  • edited micala_lecture_conference.xsl, making it conform more closely to the standard
  • added micala_lecture_session.xsl, micala_lecture_contribution.xsl, micala_lecture_subcontribution.xsl
  • Property mode set to 100644
File size: 39.5 KB
Line 
1# -*- coding: utf-8 -*-
2##
3## $Id: common.py,v 1.4 2009/04/25 13:56:17 dmartinc Exp $
4##
5## This file is par{t of CDS Indico.
6## Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 CERN.
7##
8## CDS Indico is free software; you can redistribute it and/or
9## modify it under the terms of the GNU General Public License as
10## published by the Free Software Foundation; either version 2 of the
11## License, or (at your option) any later version.
12##
13## CDS Indico is distributed in the hope that it will be useful, but
14## WITHOUT ANY WARRANTY; without even the implied warranty of
15## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16## General Public License for more details.
17##
18## You should have received a copy of the GNU General Public License
19## along with CDS Indico; if not, write to the Free Software Foundation, Inc.,
20## 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21
22from MaKaC.plugins.Collaboration.base import CSErrorBase
23from MaKaC.plugins.Collaboration.RecordingManager.exceptions import RecordingManagerException
24from MaKaC.common.PickleJar import Retrieves
25from MaKaC.webinterface.common.contribFilters import PosterFilterField
26from MaKaC.conference import ConferenceHolder, Contribution
27import MySQLdb
28from MaKaC.plugins.Collaboration.collaborationTools import CollaborationTools
29
30from MaKaC.common.logger import Logger
31
32import time
33from MaKaC.common.xmlGen import XMLGen
34from MaKaC.common.output import outputGenerator, XSLTransformer
35from MaKaC.conference import Link
36from MaKaC import conference
37
38from urllib import urlencode
39from urllib2 import Request, urlopen, HTTPError
40import re
41import os
42import sys
43from MaKaC.plugins.Collaboration.RecordingManager.micala import MicalaCommunication
44from MaKaC.i18n import _
45
46# If you want to add more languages,
47# use the MARC language codes http://www.loc.gov/marc/languages
48# Also, make sure English and French come first in the list,
49# and the rest in alphabetical order.
50languageList = [
51# English and French are the first two
52    ("eng", _("English")),
53    ("fre", _("French")),
54# now start the rest of the list
55    ("bul", _("Bulgarian")),
56    ("chi", _("Chinese")),
57    ("cze", _("Czech")),
58    ("dan", _("Danish")),
59    ("dut", _("Dutch/Flemish")),
60    ("fin", _("Finnish")),
61    ("ger", _("German")),
62    ("gre", _("Greek")),
63    ("hun", _("Hungarian")),
64    ("ita", _("Italian")),
65    ("jpn", _("Japanese")),
66    ("nor", _("Norweigan")),
67    ("pol", _("Polish")),
68    ("por", _("Portuguese")),
69    ("slo", _("Slovak")),
70    ("spa", _("Spanish")),
71    ("swe", _("Swedish"))
72]
73
74def getTalks(conference, sort = False):
75    """" sort: if True, contributions are sorted by start date (non scheduled contributions at the end)
76    """
77
78    Logger.get('RecMan').debug("in getTalks()")
79
80    # max length for title string
81    title_length = 39
82
83    # recordable_events is my own list of tags for each recordable event
84    # which will be used by the tpl file.
85    recordable_events = []
86    talks = []
87
88    event_info = {}
89    event_info["speakers"]   = ""
90    event_info["type"]       = "conference"
91    event_info["IndicoID"]   = generateIndicoID(conference = conference.getId(),
92                                              session         = None,
93                                              contribution    = None,
94                                              subcontribution = None)
95    event_info["title"]      = conference.getTitle()
96    event_info["titleshort"] = truncateString(event_info["title"], 40)
97    # this always comes first, so just pretend it's 0 seconds past the epoch
98    event_info["date"]       = int(time.mktime(conference.getStartDate().timetuple()))
99    event_info["LOID"]       = ""
100    event_info["IndicoLink"] = doesExistIndicoLink(conference)
101
102    recordable_events.append(event_info)
103
104    # Posters are contributions that are not recordable,
105    # so filter them out here
106    filter = PosterFilterField(conference, False, False)
107    for contribution in conference.getContributionList():
108        if filter.satisfies(contribution):
109
110            talks.append(contribution)
111            speaker_str = ""
112            speaker_list = contribution.getSpeakerList()
113            if speaker_list is not None:
114                tag_first_iter = True
115                for speaker in speaker_list:
116                    if tag_first_iter:
117                        speaker_str = "%s %s" % (speaker_list[0].getFirstName(),
118                                                 speaker_list[0].getFamilyName())
119                    else:
120                        speaker_str = "%s, %s %s" % \
121                            (speaker_str, speaker.getFirstName(), speaker.getFamilyName())
122
123            event_info = {}
124            event_info["speakers"]   = speaker_str
125            event_info["type"]       = "contribution"
126            event_info["IndicoID"]   = generateIndicoID(conference = conference.getId(),
127                                              session         = None,
128                                              contribution    = contribution.getId(),
129                                              subcontribution = None)
130            event_info["title"]      = contribution.getTitle()
131            event_info["titleshort"] = truncateString(event_info["title"], title_length)
132            # NOTE: Sometimes there is no start date?! e.g. 21917. I guess I should deal with this
133            try:
134                event_info["date"]       = int(time.mktime(contribution.getStartDate().timetuple()))
135            except AttributeError:
136                event_info["date"]       = int(time.mktime(conference.getStartDate().timetuple())) + 1
137
138            event_info["LOID"]       = ""
139            event_info["IndicoLink"] = doesExistIndicoLink(contribution)
140
141            recordable_events.append(event_info)
142            ctr_sc = 0
143            for subcontribution in contribution.getSubContributionList():
144                ctr_sc += 1
145                event_info = {}
146                speaker_str = ""
147                speaker_list = subcontribution.getSpeakerList()
148
149                if speaker_list is not None:
150                    tag_first_iter = True
151                    for speaker in speaker_list:
152                        if tag_first_iter:
153                            speaker_str = "%s %s" % (speaker_list[0].getFirstName(),
154                                                     speaker_list[0].getFamilyName())
155                        else:
156                            speaker_str = "%s, %s %s" % \
157                            (speaker_str, speaker.getFirstName(), speaker.getFamilyName())
158
159                event_info["speakers"]   = speaker_str
160                event_info["type"]       = "subcontribution"
161                event_info["IndicoID"]   = generateIndicoID(conference = conference.getId(),
162                                              session         = None,
163                                              contribution    = contribution.getId(),
164                                              subcontribution = subcontribution.getId())
165                event_info["title"]      = subcontribution.getTitle()
166                event_info["titleshort"] = truncateString(event_info["title"], title_length)
167                # Subcontribution objects don't have start dates,
168                # so get the owner contribution's start date
169                # and add the counter ctr_sc to that
170                event_info["date"]     = int(time.mktime(subcontribution.getOwner().getStartDate().timetuple()) + ctr_sc)
171                event_info["LOID"]       = ""
172                event_info["IndicoLink"] = doesExistIndicoLink(subcontribution)
173
174                recordable_events.append(event_info)
175
176
177    if sort:
178        talks.sort(key = Contribution.contributionStartDateForSort)
179
180    for session in conference.getSessionList():
181        event_info = {}
182        event_info["speakers"] = ""
183        event_info["type"]     = "session"
184        event_info["IndicoID"] = generateIndicoID(conference = conference.getId(),
185                                                  session         = session.getId(),
186                                                  contribution    = None,
187                                                  subcontribution = None)
188        event_info["title"]    = session.getTitle()
189        event_info["titleshort"] = truncateString(event_info["title"], title_length)
190        # Get start time as seconds since the epoch so we can sort
191        event_info["date"]     = int(time.mktime(session.getStartDate().timetuple()))
192        event_info["LOID"]       = ""
193        event_info["IndicoLink"] = doesExistIndicoLink(session)
194
195        recordable_events.append(event_info)
196
197    # Get list of matching IndicoIDs and CDS records from CDS
198    cds_indico_matches = getCDSRecords(conference.getId())
199    Logger.get('RecMan').debug('cds_indico_pending...')
200    cds_indico_pending = MicalaCommunication.getCDSPending(conference.getId(), cds_indico_matches)
201
202    # In case there are any records that were pending and are now appearing in CDS,
203    # then update the micala database accordingly.
204    MicalaCommunication.updateMicalaCDSExport(cds_indico_matches, cds_indico_pending)
205
206    Logger.get('RecMan').debug("cds_indico_matches: %s, cds_indico_pending: %s" % (cds_indico_matches, cds_indico_pending))
207    for event_info in recordable_events:
208        try:
209            event_info["CDSID"]     = cds_indico_matches[event_info["IndicoID"]]
210            event_info["CDSURL"]    = CollaborationTools.getOptionValue("RecordingManager", "CDSBaseURL") % event_info["CDSID"]
211        except KeyError:
212            Logger.get('RecMan').debug("Following talk not in CDS: %s" % event_info["title"])
213            if cds_indico_pending is not None and event_info["IndicoID"] in set(cds_indico_pending):
214                event_info["CDSID"]  = 'pending'
215                event_info["CDSURL"] = ""
216            else:
217                event_info["CDSID"]  = "none"
218                event_info["CDSURL"] = ""
219
220    # Get list of matching IndicoID's and LOIDs from the Micala database
221    existing_matches = MicalaCommunication.getMatches(conference.getId())
222
223    # insert any existing matches into the recordable_events array
224    for talk in recordable_events:
225        try:
226            matching_LOID = existing_matches[talk["IndicoID"]]
227        except KeyError:
228            matching_LOID = ""
229
230        if matching_LOID != "":
231            talk["LOID"] = matching_LOID
232
233    # Now that we have all the micala, CDS and IndicoLink info, set up the bg images
234    for talk in recordable_events:
235        talk["bg"]         = chooseBGColor(talk)
236
237    # Format dates for each talk for pleasing display
238    for talk in recordable_events:
239        talk["date_nice"] = formatDate(talk["date"])
240
241    # Next, sort the list of events by startDate for display purposes
242    recordable_events.sort(startTimeCompare)
243
244    Logger.get('RecMan').debug('leaving getTalks()')
245
246    return recordable_events
247
248def startTimeCompare(a, b):
249    '''This subroutine is for sorting the events, sessions,
250    contributions and subcontributions correctly.
251    Note: if a session and contribution have the exact same start time,
252    then it must be the first contribution in the session, and we
253    want to display the session first, so return the appropriate value to do that.'''
254
255    if a["date"] > b["date"]:
256        return 1
257    elif a["date"] == b["date"]:
258        if a["type"] == "contribution" and b["type"] == "session":
259            return 1
260        elif a["type"] == "session" and b["type"] == "contribution":
261            return -1
262        else:
263            return 0
264    else:  #a < b
265        return -1
266
267def truncateString(string, length):
268    '''Truncates given string to the desired length and if it
269    was longer than that, sticks ellipses on the end'''
270
271    if len(string) < length:
272        return string
273    else:
274        return string[:length] + "..."
275
276def formatDate(date_str):
277    '''Given number of seconds since the epoch, convert for display in the main Recording Manager interface.'''
278
279    time_struct = time.localtime(date_str)
280
281    day_of_week   = [_('Mon'),
282                     _('Tue'),
283                     _('Wed'),
284                     _('Thu'),
285                     _('Fri'),
286                     _('Sat'),
287                     _('Sun')]
288    month_of_year = [_('January'),
289                     _('February'),
290                     _('March'),
291                     _('April'),
292                     _('May'),
293                     _('June'),
294                     _('July'),
295                     _('August'),
296                     _('September'),
297                     _('October'),
298                     _('November'),
299                     _('December')]
300    month_of_year = [_('Jan'),
301                     _('Feb'),
302                     _('Mar'),
303                     _('Apr'),
304                     _('May'),
305                     _('Jun'),
306                     _('Jul'),
307                     _('Aug'),
308                     _('Sep'),
309                     _('Oct'),
310                     _('Nov'),
311                     _('Dec')]
312
313    return "%02d:%02d, %s %d %s" % (time_struct[3],
314                                    time_struct[4],
315                                    day_of_week[time_struct[6]],
316                                    time_struct[2],
317                                    month_of_year[int(time_struct[1] - 1)])
318
319def generateIndicoID(conference     = None,
320                    session         = None,
321                    contribution    = None,
322                    subcontribution = None):
323    """Given the conference, session, contribution and subcontribution IDs,
324    return an "Indico ID" of the form:
325    12345
326    12345s1
327    12345c0
328    12345c0sc3
329    """
330    IndicoID = ""
331
332    if session is not None:
333        IndicoID = "%ds%d" % (int(conference), int(session))
334    elif contribution is None:
335        IndicoID = "%d" % (int(conference),)
336    elif subcontribution is not None:
337        IndicoID = "%dc%dsc%d" % (int(conference), int(contribution), int(subcontribution))
338    else:
339        IndicoID = "%dc%d" % (int(conference), int(contribution))
340
341    return IndicoID
342
343def getOrphans():
344    """Get list of Lecture Objects in the database that have no IndicoID assigned"""
345
346    # Initialize success flag and result string
347    flagSuccess = True
348    result      = ""
349
350    try:
351        connection = MySQLdb.connect(host   = CollaborationTools.getOptionValue("RecordingManager", "micalaDBServer"),
352                                     port   = int(CollaborationTools.getOptionValue("RecordingManager", "micalaDBPort")),
353                                     user   = CollaborationTools.getOptionValue("RecordingManager", "micalaDBReaderUser"),
354                                     passwd = CollaborationTools.getOptionValue("RecordingManager", "micalaDBReaderPW"),
355                                     db     = CollaborationTools.getOptionValue("RecordingManager", "micalaDBName"))
356    except MySQLdb.Error, e:
357        flagSuccess = False
358        result += "MySQL error %d: %s" % (e.args[0], e.args[1])
359
360    if flagSuccess == True:
361        try:
362            cursor = connection.cursor(cursorclass=MySQLdb.cursors.DictCursor)
363            cursor.execute("SELECT id, LOID, IndicoID, Title, Creator FROM Lectures WHERE NOT IndicoID")
364            connection.commit()
365            rows = cursor.fetchall()
366            cursor.close()
367            connection.close()
368        except MySQLdb.Error, e:
369            flagSuccess = False
370            result += "MySQL error %d: %s" % (e.args[0], e.args[1])
371        # nothing went wrong, so populate the list "rows" with the appropriate LOID metadata and assign to result to be returned
372        else:
373            for lecture in rows:
374                lecture["time"] = formatLOID(lecture["LOID"])[0]
375                lecture["date"] = formatLOID(lecture["LOID"])[1]
376                lecture["box"]  = formatLOID(lecture["LOID"])[2]
377            result = rows
378
379    return {"success": flagSuccess, "result": result}
380
381def parseIndicoID(IndicoID):
382    """Given an "Indico ID" of the form shown above, determine whether it is
383    a conference, subcontribution etc, and return that info with the individual IDs."""
384
385    # regular expressions to match IndicoIDs for conference, session, contribution, subcontribution
386    pConference      = re.compile('(\d+)$')
387    pSession         = re.compile('(\d+)s(\d+)$')
388    pContribution    = re.compile('(\d+)c(\d+)$')
389    pSubcontribution = re.compile('(\d+)c(\d+)sc(\d+)$')
390
391    # perform the matches (match searches from the beginning of the string,
392    # unlike search, which matches anywhere in the string)
393    mE  = pConference.match(IndicoID)
394    mS  = pSession.match(IndicoID)
395    mC  = pContribution.match(IndicoID)
396    mSC = pSubcontribution.match(IndicoID)
397
398    # Depending on which talk type it is, populate a dictionary containing the name of
399    # the type of talk, the actual object, and the individual conference, session, contribution,
400    # subcontribution IDs.
401    if mE:
402        Logger.get('RecMan').debug("searched %s, matched %s" % (IndicoID, 'conference'))
403        conference = ConferenceHolder().getById(mE.group(1))
404        return {'type':           'conference',
405                'object':         conference,
406                'conference':     mE.group(1),
407                'session':        '',
408                'contribution':   '',
409                'subcontribution':''}
410    elif mS:
411        Logger.get('RecMan').debug("searched %s, matched %s" % (IndicoID, 'session'))
412        conference = ConferenceHolder().getById(mS.group(1))
413        return {'type':           'session',
414                'object':         conference.getSessionById(mS.group(2)),
415                'conference':     mS.group(1),
416                'session':        mS.group(2),
417                'contribution':   '',
418                'subcontribution':''}
419    elif mC:
420        Logger.get('RecMan').debug("searched %s, matched %s" % (IndicoID, 'contribution'))
421        conference = ConferenceHolder().getById(mC.group(1))
422        return {'type':           'contribution',
423                'object':         conference.getContributionById(mC.group(2)),
424                'conference':     mC.group(1),
425                'session':        '',
426                'contribution':   mC.group(2),
427                'subcontribution':''}
428    elif mSC:
429        Logger.get('RecMan').debug("searched %s, matched %s" % (IndicoID, 'subcontribution'))
430        conference = ConferenceHolder().getById(mSC.group(1))
431        contribution = conference.getContributionById(mSC.group(2))
432        return {'type':           'subcontribution',
433                'object':         contribution.getSubContributionById(mSC.group(3)),
434                'conference':     mSC.group(1),
435                'session':        '',
436                'contribution':   mSC.group(2),
437                'subcontribution':mSC.group(3)}
438    else:
439        return None
440
441
442def getBasicXMLRepresentation(aw, IndicoID, contentType, videoFormat, languages):
443    '''Generate the basic XML that is to be transformed using one of the XSL files.'''
444
445    # Incantation to initialize XML that I don't fully understand
446    xmlGen = XMLGen()
447    xmlGen.initXml()
448
449    # aw stands for AccessWrapper. I don't really understand exactly what
450    # this command does, but it is apparently necessary
451    og = outputGenerator(aw, xmlGen)
452
453    # Generate XML event tag to enclose the entire conference
454    xmlGen.openTag("event")
455
456    # Given the IndicoID, retrieve the type of talk and IDs
457    parsed = parseIndicoID(IndicoID)
458
459    # populate dictionary with RecordingManager parameters to be used by methods in outputGenerator
460    # such as confToXML, _confToXML, _sessionToXML, _contribToXML, _subContributionToXML
461    tags = {'talkType':    parsed['type'],
462            'talkId':      parsed[parsed['type']],
463            'contentType': contentType,
464            'videoFormat': videoFormat,
465            'languages':   languages}
466
467    Logger.get('RecMan').info("tags: [%s] [%s] [%s] [%s]" %\
468                              (tags['talkType'],
469                              tags['talkId'],
470                              tags['contentType'],
471                              tags['videoFormat']))
472    for l in tags["languages"]:
473        Logger.get('RecMan').info("language: %s" % l)
474
475    # Given the conference ID, retrieve the corresponding Conference object
476    conference = ConferenceHolder().getById(parsed["conference"])
477
478    # Defining the dictionary 'tags' is how we identify ourselves to the outputGenerator
479    # methods.
480    # Call ConfToXML with different args depending on talk type.
481    # includeSession - descend into each session.
482    #                  This is necessary for sessions, contributions, and subcontributions,
483    #                  since contributions and subcontributions are children of sessions.
484    # includeContribution - necessary for contributions and subcontributions
485    # includeMaterial - this is always set to "1".
486    # showSession - create XML for a particular session, identified by ID
487    # showContribution - create XML for a particular contribution, identified by ID
488    # showSubContribution - create XML for a particular subcontribution, identified by ID
489    # forceCache - True means force it NOT to use the cache, the opposite of what you would expect.
490    # recordingManagerTags - this is how we pass along all the necessary RecordingManager args to the outputGenerator methods.
491    #
492    # Nobody outside CERN should have access to CERN access lists.
493    # OAI harvesters outside CERN call the same methods we'll be calling,
494    # and we don't want to make the access lists available to them.
495    if parsed["type"] == 'conference':
496        Logger.get('RecMan').info("generating MARC XML for a conference")
497        og.confToXML(conference,
498                     0, # includeSession
499                     0, # includeContribution
500                     1, # includeMaterial
501                     showSession         = None,
502                     showContribution    = None,
503                     showSubContribution = None,
504                     forceCache          = True,
505                     recordingManagerTags = tags)
506    elif parsed["type"] == 'session':
507        Logger.get('RecMan').info("generating MARC XML for a session")
508        og.confToXML(conference,
509                     1, # includeSession
510                     0, # includeContribution
511                     1, # includeMaterial
512                     showSession         = parsed["session"],
513                     showContribution    = None,
514                     showSubContribution = None,
515                     forceCache          = True,
516                     recordingManagerTags = tags)
517    elif parsed["type"] == 'contribution':
518        Logger.get('RecMan').info("generating MARC XML for a contribution")
519        og.confToXML(conference,
520                     1, # includeSession
521                     1, # includeContribution
522                     1, # includeMaterial
523                     showSession         = parsed["session"],
524                     showContribution    = parsed["contribution"],
525                     showSubContribution = None,
526                     forceCache          = True,
527                     recordingManagerTags = tags)
528    elif parsed["type"] == 'subcontribution':
529        Logger.get('RecMan').info("generating MARC XML for a subcontribution")
530        og.confToXML(conference,
531                     1, # includeSession
532                     1, # includeContribution
533                     1, # includeMaterial
534                     showSession         = None,
535                     showContribution    = parsed["contribution"], # maybe I should turn this on?
536                     showSubContribution = parsed["subcontribution"],
537                     forceCache          = True,
538                     recordingManagerTags = tags)
539    else:
540        raise RecordingManagerException("IndicoID %s is not a conference, session, contribution or subcontribution." % IndicoID)
541
542    xmlGen.closeTag("event")
543
544    # Retrieve the entire basic XML string
545    return xmlGen.getXml()
546
547def createCDSRecord(aw, IndicoID, contentType, videoFormat, languages):
548    '''Retrieve a MARC XML string for the given conference, then web upload it to CDS.'''
549
550# I need to break this up into 2 functions: one to simply get the MARC XML,
551# and another one to retrieve that and submit it to CDS.
552# Then I need another function to get the MARC XML (or even just the base xml, not sure)
553# and somehow submit the metadata to micala. I don't know how to handle the submission yet.
554# Also I guess I will need to create XSL file(s) for lecture.xml / metadata.xml
555#
556
557    # Initialize success flag, and result string to which we will append any errors.
558    flagSuccess = True
559    result = ""
560
561    # generate the basic XML from which we will produce MARC XML for CDS and lecture.xml for micala
562    basexml = getBasicXMLRepresentation(aw, IndicoID, contentType, videoFormat, languages)
563
564    from MaKaC.common import Config
565
566    marcxml = ""
567
568    # Given the IndicoID, retrieve the type of talk and IDs, so we know which XSL file to use.
569    parsed = parseIndicoID(IndicoID)
570
571    # Choose the appropriate stylesheet:
572    # - cds_marcxml_video_conference.xsl
573    # - cds_marcxml_video_session.xsl
574    # - cds_marcxml_video_contribution.xsl
575    # - cds_marcxml_video_subcontribution.xsl
576    styleSheet = "%s_%s.xsl" % ('cds_marcxml_video', parsed["type"])
577    stylePath = os.path.join(Config.getInstance().getStylesheetsDir(), styleSheet)
578
579    if os.path.exists(stylePath):
580        try:
581            Logger.get('RecMan').info("Trying to do XSLT using path %s" % stylePath)
582            parser = XSLTransformer(stylePath)
583            marcxml = parser.process(basexml)
584        except Exception:
585            flagSuccess = False
586            result += "Cannot parse stylesheet: %s" % sys.exc_info()[0]
587    else:
588        flagSuccess = False
589        result += "Stylesheet does not exist: %s" % stylePath
590
591    # temporary, for my own debugging
592    f = open('/tmp/base.xml', 'w')
593    f.write(basexml)
594    f.close()
595
596    # temporary, for my own debugging
597    f = open('/tmp/marc.xml', 'w')
598    f.write(marcxml)
599    f.close()
600
601    # Submit MARC XML record to CDS
602    data = urlencode({
603        "file": marcxml,
604        "mode": "-ir"
605    })
606    headers = {"User-Agent": "invenio_webupload"}
607    req = Request(CollaborationTools.getOptionValue("RecordingManager", "CDSUploadURL"), data, headers)
608    Logger.get('RecMan').debug("req = %s" % str(req))
609
610    try:
611        f = urlopen(req)
612        result = f.read()
613    except HTTPError, e:
614        flagSuccess = False
615        result += "CDS returned an error when submitting to %s: %s\n" % \
616            (CollaborationTools.getOptionValue("RecordingManager", "CDSUploadURL"), e)
617    except Exception, e:
618        flagSuccess = False
619        result += "Unknown error occured when submitting CDS record: %s.\n" % e
620
621    # temporary, for my own debugging
622    f = open('/tmp/cds_result.txt', 'w')
623    f.write(result)
624    f.close()
625
626    # Update the micala database showing the task has started, but only if
627    # the submission actually succeeded.
628    if flagSuccess == True:
629        # pattern to match for the signal file
630        # Here we are looking for
631        # CONFID
632        # or CONFIDcCONTRID
633        # or CONFIDcCONTRIDscSCONTRID
634        # or CONFIDsSESSID
635        pattern_cern  = re.compile('([sc\d]+)$')
636        # Here we are looking for YYYYMMDD-DEVICENAME-HHMMSS
637        pattern_umich = re.compile('(\d+\-[\w\d]+\-\d)$')
638
639        # Update the micala database with our current task status
640        try:
641            idMachine = MicalaCommunication.getIdMachine(CollaborationTools.getOptionValue("RecordingManager", "micalaDBMachineName"))
642            idTask    = MicalaCommunication.getIdTask(CollaborationTools.getOptionValue("RecordingManager", "micalaDBStatusExportCDS"))
643            idLecture = MicalaCommunication.getIdLecture(IndicoID, pattern_cern, pattern_umich)
644            if idLecture == '':
645                idLecture = MicalaCommunication.createNewMicalaLecture(IndicoID, contentType, pattern_cern, pattern_umich)
646            MicalaCommunication.reportStatus('START', '', idMachine, idTask, idLecture)
647        except Exception, e:
648            flagSuccess = False
649            result += _("Unknown error occured when updating task information in micala database: ") + e + "\n"
650
651    return {"success": flagSuccess, "result": result}
652
653def submitMicalaMetadata(aw, IndicoID, contentType, LOID, videoFormat, languages):
654    '''Generate a lecture.xml file for the given event, then web upload it to the micala server.'''
655
656    Logger.get('RecMan').debug('in submitMicalaMetadata()')
657
658    # Initialize success flag, and result string to which we will append any errors.
659    flagSuccess = True
660    result = ""
661
662    # First, update the micala Tasks table that the submission has started
663
664    # pattern to match for the signal file
665    # Here we are looking for
666    # CONFID
667    # or CONFIDcCONTRID
668    # or CONFIDcCONTRIDscSCONTRID
669    # or CONFIDsSESSID
670    pattern_cern  = re.compile('([sc\d]+)$')
671    # Here we are looking for YYYYMMDD-DEVICENAME-HHMMSS
672    pattern_umich = re.compile('(\d+\-[\w\d]+\-\d)$')
673
674    # Update the micala database with our current task status
675    try:
676        Logger.get('RecMan').debug('calling getIdMachine...')
677        idMachine = MicalaCommunication.getIdMachine(CollaborationTools.getOptionValue("RecordingManager", "micalaDBMachineName"))
678        Logger.get('RecMan').debug('calling getIdTask...')
679        idTask    = MicalaCommunication.getIdTask(CollaborationTools.getOptionValue("RecordingManager", "micalaDBStatusExportMicala"))
680        Logger.get('RecMan').debug('calling getIdLecture...')
681        idLecture = MicalaCommunication.getIdLecture(IndicoID, pattern_cern, pattern_umich)
682        Logger.get('RecMan').debug('calling reportStatus...')
683        if idLecture == '':
684            idLecture = MicalaCommunication.createNewMicalaLecture(IndicoID, contentType, pattern_cern, pattern_umich)
685        MicalaCommunication.reportStatus('START', '', idMachine, idTask, idLecture)
686    except Exception, e:
687        flagSuccess = False
688        result += _("Unknown error occured when updating MICALA START task information in micala database: %s\n." % e)
689
690    basexml = getBasicXMLRepresentation(aw, IndicoID, contentType, videoFormat, languages)
691
692    from MaKaC.common import Config
693
694    micalaxml = ""
695
696    # Given the IndicoID, retrieve the type of talk and IDs, so we know which XSL file to use.
697    parsed = parseIndicoID(IndicoID)
698
699    # Choose the appropriate stylesheet:
700    # - micala_lecture_conference.xsl
701    # - micala_lecture_session.xsl
702    # - micala_lecture_contribution.xsl
703    # - micala_lecture_subcontribution.xsl
704    styleSheet = "%s_%s.xsl" % ('micala_lecture', parsed["type"])
705    stylePath = os.path.join(Config.getInstance().getStylesheetsDir(), styleSheet)
706
707    if os.path.exists(stylePath):
708        try:
709            Logger.get('RecMan').info("Trying to do XSLT using path %s" % stylePath)
710            parser = XSLTransformer(stylePath)
711            micalaxml = parser.process(basexml)
712        except Exception, e:
713            flagSuccess = False
714            result += "Cannot parse stylesheet: %s" % sys.exc_info()[0]
715    else:
716        flagSuccess = False
717        result += "Stylesheet does not exist: %s" % stylePath
718
719    # temporary, for my own debugging
720    f = open('/tmp/micala.xml', 'w')
721    f.write(micalaxml)
722    f.close()
723
724    # Web upload metadata to micala server
725    if flagSuccess == True:
726        # encode the LOID and the XML file as POST variables
727        data = urlencode({ "LOID": LOID, "micalaxml": micalaxml })
728        # use the header to identify ourselves
729        headers = {"User-Agent": "micala_webupload"}
730        # build the request
731        request = Request(CollaborationTools.getOptionValue("RecordingManager", "micalaUploadURL"), data, headers)
732
733        Logger.get('RecMan').debug("micala request = %s" % str(request))
734
735        # submit the request, and append caught exceptions to the result string,
736        # to be displayed by services.py
737        try:
738            f = urlopen(request)
739            request_result = f.read()
740            if request_result == 'no data':
741                result += _("micala web upload returned an unknown error when submitting to ") + "%s\n" % \
742                    (CollaborationTools.getOptionValue("RecordingManager", "micalaUploadURL"))
743            Logger.get('RecMan').debug("micala result = %s" % str(request_result))
744        except HTTPError, e:
745            flagSuccess = False
746            result += _("micala web upload returned an error when submitting to ") + "%s: %s\n" % \
747                (CollaborationTools.getOptionValue("RecordingManager", "micalaUploadURL"), e)
748        except Exception, e:
749            flagSuccess = False
750            result += _("Unknown error occured when submitting micala metadata: ") + e + "\n"
751
752    # Update the micala database showing the task has started, but only if
753    # the submission actually succeeded.
754    if flagSuccess == True:
755        # pattern to match for the signal file
756        # Here we are looking for
757        # CONFID
758        # or CONFIDcCONTRID
759        # or CONFIDcCONTRIDscSCONTRID
760        # or CONFIDsSESSID
761        pattern_cern  = re.compile('([sc\d]+)$')
762        # Here we are looking for YYYYMMDD-DEVICENAME-HHMMSS
763        pattern_umich = re.compile('(\d+\-[\w\d]+\-\d)$')
764
765        # Update the micala database with our current task status
766        try:
767            if idLecture == '':
768                idLecture = MicalaCommunication.createNewMicalaLecture(IndicoID, contentType, pattern_cern, pattern_umich)
769            MicalaCommunication.reportStatus('COMPLETE', '', idMachine, idTask, idLecture)
770        except Exception, e:
771            flagSuccess = False
772            result += _("Unknown error occured when updating COMPLETE MICALA task information in micala database: %s\n." % e)
773
774    return {"success": flagSuccess, "result": result}
775
776def getCDSRecords(confId):
777    '''Query CDS to see if it has an entry for the given event as well as all its sessions, contributions and subcontributions.
778    If yes return a dictionary pairing CDS IDs with Indico IDs.'''
779
780    # Also, this method should then make sure that the database Status table has been updated to show that the export to CDS task is complete
781
782    Logger.get('RecMan').debug('in getCDSRecords()')
783
784    # Slap a wildcard on the end of the ID to find the conference itself as well as all children.
785    id_plus_wildcard = confId + "*"
786
787    # Here is a help page describing the GET args,
788    # if this CDS query needs to be changed (thanks jerome.caffaro@cern.ch):
789    # http://invenio-demo.cern.ch/help/hacking/search-engine-api
790    # NB: We replace the string REPLACE_WITH_INDICO_ID manually instead of using %s because
791    # there are also url-encoded chars containing the % char.
792    optionsCDSQueryURL = CollaborationTools.getOptionValue("RecordingManager", "CDSQueryURL")
793    escapedOptionsCDSQueryURL = optionsCDSQueryURL.replace("REPLACE_WITH_INDICO_ID", id_plus_wildcard)
794    Logger.get('RecMan').debug("escapedOptionsCDSQueryURL = " + escapedOptionsCDSQueryURL)
795    url = escapedOptionsCDSQueryURL
796
797    # This dictionary will contain CDS ID's, referenced by Indico ID's
798    results = {}
799
800    # perform the URL query to CDS. It contains one line for each CDS record,
801    # of the form:
802    # 001121974 970__ $$aINDICO.21917c22
803    # The first number is the CDS record ID, with leading zeros
804    # The second number is the MARC XML tag
805    # The third string contains the Indico ID
806    # (see http://www.loc.gov/marc/bibliographic for detailed information on MARC)
807    request = Request(url)
808    f = urlopen(request)
809    lines = f.readlines()
810    f.close()
811
812    # Read each line, extracting the IndicoIDs and their corresponding CDS IDs
813    for line in lines:
814        Logger.get('RecMan').debug(" CDS query result: %s" % line)
815        result = line.strip()
816        if result != "":
817
818            bigcdsid = result.split(" ")[0]
819            CDSID = bigcdsid.lstrip("0")
820
821            p = re.compile('INDICO\.([sc\d]+)')
822            m = p.search(line)
823
824            if m:
825                IndicoID = m.group(1)
826                results[IndicoID] = CDSID
827            else:
828                # If we end up here then probably something's wrong with the URL
829                pass
830
831#            results.append({"CDSID": CDSID, "IndicoID": IndicoID})
832
833    return results
834
835
836def createIndicoLink(IndicoID, CDSID):
837    """Create a link in Indico to the CDS record."""
838
839    Logger.get('RecMan').debug("in createIndicoLink()")
840    # From IndicoID, get info
841    talkInfo = parseIndicoID(IndicoID)
842    obj = talkInfo["object"]
843
844    # Only one link per talk allowed.
845    if doesExistIndicoLink(obj):
846        pass
847    else:
848        Logger.get('RecMan').info("creating a new link in Indico for talk %s, CDS record %s" % (IndicoID, CDSID))
849
850        # material object holds link object.
851        # First create a material object with title "Video in CDS" or whatever the current text is.
852        material = conference.Material()
853        material.setTitle(CollaborationTools.getOptionValue("RecordingManager", "videoLinkName"))
854        videoLink = Link()
855        videoLink.setOwner(material)
856#    I don't think this stuff is necessary:
857#        videoLink.setName("Name goes here")
858#        videoLink.setDescription("Description goes here")
859        videoLink.setURL(CollaborationTools.getOptionValue("RecordingManager", "CDSBaseURL") % str(CDSID))
860        material.addResource(videoLink)
861        material.setMainResource(videoLink)
862        obj.addMaterial(material)
863
864def doesExistIndicoLink(obj):
865    """This function will be called with a conference, session, contribution or subcontribution object.
866    Each of those has a getAllMaterialList() method. Call that method and search for a title "Video in CDS"
867    and make sure it has a link."""
868
869    flagLinkFound = False
870
871    materials = obj.getAllMaterialList()
872    if materials is not None and len(materials) > 0:
873        for material in materials:
874            # If the material in question is a link
875            # whose title is either the original "Video in CDS"
876            # or whatever other title might be specified in the RecordingManager
877            # plugin options, then we've found a link.
878            if isinstance(material.getMainResource(), Link) and \
879            (material.getTitle() == "Video in CDS" or material.getTitle() == \
880             CollaborationTools.getOptionValue("RecordingManager", "videoLinkName") ):
881                flagLinkFound = True
882
883    return flagLinkFound
884
885def formatLOID(LOID):
886    """Given a LOID of the form YYYYMMDD-recordingDev-HHMMSS, split it up into nicely readable parts: time, date, recording_device."""
887
888    rawdate = LOID.split('-')[0]
889    rawtime = LOID.split('-')[2]
890
891    date = rawdate[6:8] + '.' + rawdate[4:6] + '.' + rawdate[0:4]
892    time = rawtime[0:2] + ':' + rawtime[2:4] + ':' + rawtime[4:8]
893
894    recording_device = LOID.split('-')[1]
895
896    return(time, date, recording_device)
897
898def chooseBGColor(talk):
899    """Given a talk dictionary, check if it has an LOID, CDS record, and IndicoLink.
900    Pick the appropriate image RecordingManagerNNN.png to match that.
901    This image will be used for the div background.
902    """
903
904    # NB: not currently used
905    # I might change this later to gray out talks that have already been matched etc.
906
907    return "#FFFFFF"
908
909class RecordingManagerError(CSErrorBase):
910    def __init__(self, operation, inner):
911        CSErrorBase.__init__(self)
912        self._operation = operation
913        self._inner = inner
914
915    @Retrieves(['MaKaC.plugins.Collaboration.RecordingManager.common.RecordingManagerError'], 'origin')
916    def getOrigin(self):
917        return 'RecordingManager'
918
919    @Retrieves(['MaKaC.plugins.Collaboration.RecordingManager.common.RecordingManagerError'],
920               'operation')
921    def getOperation(self):
922        return self._operation
923
924    @Retrieves(['MaKaC.plugins.Collaboration.RecordingManager.common.RecordingManagerError'],
925               'inner')
926    def getInner(self):
927        return str(self._inner)
928
929    def getUserMessage(self):
930        return ''
931
932    def getLogMessage(self):
933        return "Recording Request error for operation: " + str(self._operation) + ", inner exception: " + str(self._inner)
934
Note: See TracBrowser for help on using the repository browser.