~didrocks/unity/altf10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
__metaclass__ = type

import httplib
import simplejson
from urlparse import urlunparse
from urllib import (
    quote,
    urlencode,
)

from grackle.error import (
    ArchiveIdExists,
    MessageIdNotFound,
    UnparsableDateRange,
    UnsupportedDisplayType,
    UnsupportedOrder,
    )


class GrackleClient:
    """Class for accessing Grackle web service."""

    def __init__(self, host, port):
        """Constructor.

        :param host: The name of the server.
        :param port: The port providing Grackle service.
        """
        self.host = host
        self.port = port
        self.netloc = '%s:%d' % (host, port)

    def archive_url(self, path, query):
        """Return the URL for an archive

        :param path: The path to generate the URL for.
            Maybe be '', 'archive_id', or 'archive_id/message_id'
        :param query: The query to use in the URL, as a dict.
        """
        path = '/archive/%s' % quote(path)
        query_string = urlencode(query)
        return urlunparse(('http', self.netloc, path, '', query_string, ''))

    def _get_connection(self):
        return httplib.HTTPConnection(self.host, self.port)

    def _method_archive(self, method, path, query, body=None):
        """Perform an HTTP method on an archive's URL."""
        url = self.archive_url(path, query)
        connection = self._get_connection()
        connection.request(method, url, body)
        return connection.getresponse()

    def put_archive(self, archive_id, mbox=None):
        """Create an archive.

        :param archive_id: The archive id.
        :param mbox: An optional mbox with messages to add to the new archive.
        """
        response = self._method_archive(
            'PUT', archive_id, {}, None)
        response.read()
        if response.status == httplib.BAD_REQUEST:
            if response.reason == ArchiveIdExists.__doc__:
                raise ArchiveIdExists
            raise Exception('wtf')
        elif response.status == httplib.CREATED:
            return
        else:
            raise Exception('!!')

    def put_message(self, archive_id, key, file_obj):
        """Put a message into an archive.

        :param archive_id: The archive to put the message into.
        :param key: An arbitrary identifier that can later be used to retrieve
            the message.
        :param file_obj: The raw text of the message, as a file.
        """
        path = '%s/%s' % (archive_id, key)
        response = self._method_archive(
            'PUT', path, {}, file_obj.read())
        response.read()
        if response.status == httplib.BAD_REQUEST:
            if response.reason == ArchiveIdExists.__doc__:
                raise ArchiveIdExists
            raise Exception('wtf')
        elif response.status == httplib.CREATED:
            return
        else:
            raise Exception('!!')

    def get_messages(self, archive_id, message_ids=None, date_range=None,
                     limit=None, memo=None, order=None, headers=None,
                     include_hidden=False, max_body_length=None,
                     display_type='all'):
        """Retrieve specified messages.

        :param archive_id: The archive to retrieve messages from.
        :param message_ids: (optional) Retrieve only messages with these ids.
        :param date_range: Retrieve the messages from or between a range of
            dates. Example: 2012-01-01..2012-01-31 retrieve all the messages
            between the 01 and 31 of January, including message from 01
            and 31.
        :param limit: The maximum number of messages to return.  The server
            may, at its discretion, return fewer.
        :param memo: (optional) Opaque identifier describing the position in
            the list of messages to return.  The combination of a memo and a
            limit describes a batch of results.  If not specified, the start
            is used.
        :param order: The order to return results in.  Supported orders are
            determined by the server.  See test_client.SUPPORTED_ORDERS for an
            example.
        :param headers: The headers to include in the message.  Only headers
            actually present in the message will be provided.  If unspecified,
            most headers will be included.
        :param max_body_length: The maximum length for a message's body.  When
            multiple messages are nested (as with a thread), this applies to
            each message's body, not the aggregate length of all messages'
            bodies.
        :param include_hidden: If true, include messages that have been
            flagged "hidden" in the results.
        :param display_type: Adjust the message content to meet the needs of
            the intended display. Valid values are:
            all: (the default) include all message content.
            text-only: include only plain/text parts; exclude all other parts.
            headers-only: include only the message headers.
        """
        parameters = {}
        if message_ids is not None:
            parameters['message_ids'] = message_ids
        if date_range is not None:
            parameters['date_range'] = date_range
        if limit is not None:
            parameters['limit'] = limit
        if memo is not None:
            parameters['memo'] = memo
        if order is not None:
            parameters['order'] = order
        if headers is not None:
            parameters['headers'] = headers
        if max_body_length is not None:
            parameters['max_body_length'] = max_body_length
        parameters['display_type'] = display_type
        parameters['include_hidden'] = include_hidden
        query = {'parameters': simplejson.dumps(parameters)}
        response = self._method_archive('GET', archive_id, query)
        if response.status == httplib.BAD_REQUEST:
            if response.reason == UnsupportedOrder.__doc__:
                raise UnsupportedOrder
            elif response.reason == UnsupportedDisplayType.__doc__:
                raise UnsupportedDisplayType
            elif response.reason == UnparsableDateRange.__doc__:
                raise UnparsableDateRange
            else:
                raise ValueError('Bad request')
        data = response.read()
        return simplejson.loads(data)

    def hide_message(self, archive_id, message_id, hidden):
        parameters = {
            'hidden': hidden,
            }
        query = {'parameters': simplejson.dumps(parameters)}
        path = '%s/%s' % (archive_id, message_id)
        response = self._method_archive('POST', path, query)
        if response.status == httplib.BAD_REQUEST:
            if response.reason == MessageIdNotFound.__doc__:
                raise MessageIdNotFound
        data = response.read()
        return simplejson.loads(data)