EventAggregator

Annotated EventAggregatorSupport/Resources.py

428:a0e4dad42a1e
2014-03-30 Paul Boddie Made parsing functions return resources, not lists of events. Propagated page URLs to calendar resources parsed from pages. Added support for event retrieval from item/message stores. Added MoinMessage integration for item/message decryption.
paul@347 1
# -*- coding: iso-8859-1 -*-
paul@347 2
"""
paul@347 3
    MoinMoin - EventAggregator resource acquisition and access
paul@347 4
paul@405 5
    @copyright: 2008, 2009, 2010, 2011, 2012, 2013, 2014 by Paul Boddie <paul@boddie.org.uk>
paul@347 6
    @license: GNU GPL (v2 or later), see COPYING.txt for details.
paul@347 7
"""
paul@347 8
paul@347 9
from EventAggregatorSupport.Filter import *
paul@347 10
from EventAggregatorSupport.Types import *
paul@347 11
paul@347 12
from DateSupport import Date, Month
paul@347 13
from MoinSupport import *
paul@412 14
from MoinRemoteSupport import getCachedResource, getCachedResourceMetadata, imapreader
paul@428 15
from ItemSupport import ItemStore
paul@428 16
paul@428 17
from MoinMoin.Page import Page
paul@347 18
paul@347 19
import urllib
paul@347 20
paul@347 21
try:
paul@347 22
    from cStringIO import StringIO
paul@347 23
except ImportError:
paul@347 24
    from StringIO import StringIO
paul@347 25
paul@347 26
# Obtaining event containers and events from such containers.
paul@347 27
paul@347 28
def getEventPages(pages):
paul@347 29
paul@347 30
    "Return a list of events found on the given 'pages'."
paul@347 31
paul@428 32
    # Get event pages instead of result pages.
paul@347 33
paul@428 34
    return EventResourceCollection(map(EventPage, pages))
paul@347 35
paul@347 36
def getAllEventSources(request):
paul@347 37
paul@347 38
    "Return all event sources defined in the Wiki using the 'request'."
paul@347 39
paul@347 40
    sources_page = getattr(request.cfg, "event_aggregator_sources_page", "EventSourcesDict")
paul@347 41
paul@347 42
    # Remote sources are accessed via dictionary page definitions.
paul@347 43
paul@347 44
    return getWikiDict(sources_page, request)
paul@347 45
paul@347 46
def getEventResources(sources, calendar_start, calendar_end, request):
paul@347 47
paul@347 48
    """
paul@347 49
    Return resource objects for the given 'sources' using the given
paul@347 50
    'calendar_start' and 'calendar_end' to parameterise requests to the sources,
paul@347 51
    and the 'request' to access configuration settings in the Wiki.
paul@347 52
    """
paul@347 53
paul@347 54
    sources_dict = getAllEventSources(request)
paul@347 55
    if not sources_dict:
paul@347 56
        return []
paul@347 57
paul@347 58
    # Use dates for the calendar limits.
paul@347 59
paul@347 60
    if isinstance(calendar_start, Date):
paul@347 61
        pass
paul@347 62
    elif isinstance(calendar_start, Month):
paul@347 63
        calendar_start = calendar_start.as_date(1)
paul@347 64
paul@347 65
    if isinstance(calendar_end, Date):
paul@347 66
        pass
paul@347 67
    elif isinstance(calendar_end, Month):
paul@347 68
        calendar_end = calendar_end.as_date(-1)
paul@347 69
paul@428 70
    resources = EventResourceCollection()
paul@347 71
paul@347 72
    for source in sources:
paul@428 73
        details = sources_dict[source].split()
paul@428 74
        type = details[0]
paul@428 75
        resource = None
paul@428 76
paul@428 77
        # Support non-URL sources.
paul@428 78
paul@428 79
        if type == "store":
paul@428 80
            details = details[1:]
paul@428 81
paul@428 82
            details.extend([None] * 3)
paul@428 83
            page_name, store_name, lockdir_name = details[:3]
paul@428 84
paul@428 85
            resource = getEventResourcesFromStore(page_name, store_name, lockdir_name, request)
paul@428 86
paul@428 87
        # Unrecognised types are treated as URLs.
paul@428 88
paul@347 89
        else:
paul@428 90
            if type == "url":
paul@428 91
                details = details[1:]
paul@428 92
paul@428 93
            details.extend([None] * 3)
paul@428 94
            url, format, expected_content_type = details[:3]
paul@428 95
            format = format or "ical"
paul@428 96
paul@387 97
            resource = getEventResourcesFromSource(url, format, expected_content_type, calendar_start, calendar_end, request)
paul@428 98
paul@428 99
        if resource:
paul@428 100
            resources.append(resource)
paul@347 101
paul@347 102
    return resources
paul@347 103
paul@428 104
def getEventResourcesFromStore(page_name, store_name, lockdir_name, request):
paul@428 105
paul@428 106
    """
paul@428 107
    Return a resource object for the store indicated by the given 'page_name'
paul@428 108
    and 'store_name' (with the accompanying 'lockdir_name', specified as None
paul@428 109
    for a computed name to be used).
paul@428 110
    """
paul@428 111
paul@428 112
    store = ItemStore(Page(request, page_name), store_name, lockdir_name)
paul@428 113
    return parseEventsInStore(store, request)
paul@428 114
paul@387 115
def getEventResourcesFromSource(url, format, expected_content_type, calendar_start, calendar_end, request):
paul@363 116
paul@363 117
    """
paul@363 118
    Return a resource object for the given 'url' providing content in the
paul@387 119
    specified 'format', insisting on the 'expected_content_type', and using the
paul@387 120
    given 'calendar_start' and 'calendar_end' to parameterise requests to the
paul@387 121
    sources and the 'request' to access configuration settings in the Wiki.
paul@363 122
    """
paul@363 123
paul@363 124
    # Prevent local file access.
paul@363 125
paul@363 126
    if url.startswith("file:"):
paul@363 127
        return None
paul@363 128
paul@412 129
    # Support IMAP access.
paul@412 130
paul@412 131
    elif url.startswith("imap"):
paul@412 132
        reader = imapreader
paul@412 133
paul@412 134
    # Otherwise, use the default access mechanism.
paul@412 135
paul@412 136
    else:
paul@412 137
        reader = None
paul@412 138
paul@363 139
    # Parameterise the URL.
paul@363 140
    # Where other parameters are used, care must be taken to encode them
paul@363 141
    # properly.
paul@363 142
paul@363 143
    url = url.replace("{start}", urllib.quote_plus(calendar_start and str(calendar_start) or ""))
paul@363 144
    url = url.replace("{end}", urllib.quote_plus(calendar_end and str(calendar_end) or ""))
paul@363 145
paul@363 146
    # Get a parser.
paul@363 147
    # NOTE: This could be done reactively by choosing a parser based on
paul@363 148
    # NOTE: the content type provided by the URL.
paul@363 149
paul@428 150
    parser, default_content_type = getParserForFormat(format)
paul@428 151
    required_content_type = expected_content_type or default_content_type
paul@363 152
paul@363 153
    # Obtain the resource, using a cached version if appropriate.
paul@363 154
paul@363 155
    max_cache_age = int(getattr(request.cfg, "event_aggregator_max_cache_age", "300"))
paul@412 156
    data = getCachedResource(request, url, "EventAggregator", "wiki", max_cache_age, reader)
paul@363 157
    if not data:
paul@363 158
        return None
paul@363 159
paul@363 160
    # Process the entry, parsing the content.
paul@363 161
paul@363 162
    f = StringIO(data)
paul@363 163
    try:
paul@363 164
        # Get the content type and encoding, making sure that the data
paul@363 165
        # can be parsed.
paul@363 166
paul@363 167
        url, content_type, encoding, metadata = getCachedResourceMetadata(f)
paul@363 168
paul@363 169
        if content_type != required_content_type:
paul@363 170
            return None
paul@363 171
paul@363 172
        # Send the data to the parser.
paul@363 173
paul@405 174
        return parser(f, encoding, url, metadata)
paul@405 175
paul@363 176
    finally:
paul@363 177
        f.close()
paul@363 178
paul@428 179
def getParserForFormat(format):
paul@347 180
paul@428 181
    "Return a parser and default content type for the given 'format'."
paul@347 182
paul@428 183
    if format == "ical":
paul@428 184
        return parseEventsInCalendarFromResource, "text/calendar"
paul@428 185
    elif format == "xcal":
paul@428 186
        return parseEventsInXMLCalendarFromResource, "application/calendar+xml"
paul@428 187
    elif format == "mbox":
paul@428 188
        return parseEventsInMailboxFromResource, "multipart/mixed"
paul@428 189
    else:
paul@428 190
        return None, None
paul@347 191
paul@347 192
# Page-related functions.
paul@347 193
paul@347 194
def fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames):
paul@347 195
paul@347 196
    """
paul@347 197
    Using the given 'template_page', complete the 'new_page' by copying the
paul@347 198
    template and adding the given 'event_details' (a dictionary of event
paul@347 199
    fields), setting also the 'category_pagenames' to define category
paul@347 200
    membership.
paul@347 201
    """
paul@347 202
paul@347 203
    event_page = EventPage(template_page)
paul@347 204
    new_event_page = EventPage(new_page)
paul@347 205
    new_event_page.copyPage(event_page)
paul@347 206
paul@347 207
    if new_event_page.getFormat() == "wiki":
paul@347 208
        new_event = Event(new_event_page, event_details)
paul@347 209
        new_event_page.setEvents([new_event])
paul@347 210
        new_event_page.setCategoryMembership(category_pagenames)
paul@347 211
        new_event_page.flushEventDetails()
paul@347 212
paul@347 213
    return new_event_page.getBody()
paul@347 214
paul@347 215
# Event selection from request parameters.
paul@347 216
paul@347 217
def getEventsUsingParameters(category_names, search_pattern, remote_sources,
paul@347 218
    calendar_start, calendar_end, resolution, request):
paul@347 219
paul@347 220
    "Get the events according to the resolution of the calendar."
paul@347 221
paul@347 222
    if search_pattern:
paul@347 223
        results         = getPagesForSearch(search_pattern, request)
paul@347 224
    else:
paul@347 225
        results         = []
paul@347 226
paul@347 227
    results            += getAllCategoryPages(category_names, request)
paul@347 228
    pages               = getPagesFromResults(results, request)
paul@428 229
    events              = getEventPages(pages).getEvents()
paul@428 230
    events             += getEventResources(remote_sources, calendar_start, calendar_end, request).getEvents()
paul@347 231
    all_shown_events    = getEventsInPeriod(events, getCalendarPeriod(calendar_start, calendar_end))
paul@347 232
    earliest, latest    = getEventLimits(all_shown_events)
paul@347 233
paul@347 234
    # Get a concrete period of time.
paul@347 235
paul@347 236
    first, last = getConcretePeriod(calendar_start, calendar_end, earliest, latest, resolution)
paul@347 237
paul@347 238
    return all_shown_events, first, last
paul@347 239
paul@347 240
# vim: tabstop=4 expandtab shiftwidth=4