diff options
author | Mike Crute <mcrute@gmail.com> | 2010-02-15 23:15:34 -0500 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2010-02-15 23:15:34 -0500 |
commit | b8cce82b1c31dea2602d1f52bca8f46881d709c8 (patch) | |
tree | 00a9c1abd9e28747c15992c68c2f58914bae3d1f | |
parent | f75f35fda0509f93378a74a395bc212b35d83c5f (diff) | |
download | calendar_proxy-b8cce82b1c31dea2602d1f52bca8f46881d709c8.tar.bz2 calendar_proxy-b8cce82b1c31dea2602d1f52bca8f46881d709c8.tar.xz calendar_proxy-b8cce82b1c31dea2602d1f52bca8f46881d709c8.zip |
Re-factoring commands for better seperation of concerns.
-rw-r--r-- | exchange/commands.py | 123 |
1 files changed, 64 insertions, 59 deletions
diff --git a/exchange/commands.py b/exchange/commands.py index a2ab471..f962342 100644 --- a/exchange/commands.py +++ b/exchange/commands.py | |||
@@ -3,6 +3,7 @@ | |||
3 | Exchange Commands | 3 | Exchange Commands |
4 | 4 | ||
5 | @author: Mike Crute (mcrute@gmail.com) | 5 | @author: Mike Crute (mcrute@gmail.com) |
6 | @organization: American Greetings Interactive | ||
6 | @date: November 10, 2008 | 7 | @date: November 10, 2008 |
7 | 8 | ||
8 | This is a set of classes that starts to define a set of classes for | 9 | This is a set of classes that starts to define a set of classes for |
@@ -11,14 +12,68 @@ development code but it does the trick. Watch out, it doesn't consider | |||
11 | many corner cases. | 12 | many corner cases. |
12 | """ | 13 | """ |
13 | 14 | ||
14 | import xml.etree.cElementTree as ElementTree | ||
15 | import dateutil.parser as date_parser | 15 | import dateutil.parser as date_parser |
16 | import xml.etree.cElementTree as ElementTree | ||
16 | 17 | ||
17 | from httplib import HTTPSConnection | ||
18 | from string import Template | 18 | from string import Template |
19 | from httplib import HTTPSConnection | ||
19 | from datetime import datetime, timedelta | 20 | from datetime import datetime, timedelta |
20 | from icalendar import Calendar, Event as _Event, Alarm | ||
21 | from exchange import ExchangeException, EST | 21 | from exchange import ExchangeException, EST |
22 | from icalendar import Calendar, Event as _Event | ||
23 | |||
24 | |||
25 | class Event(_Event): | ||
26 | |||
27 | def _get_element_text(self, element, key): | ||
28 | value = element.find(key) | ||
29 | |||
30 | if hasattr(value, 'text'): | ||
31 | return value.text | ||
32 | |||
33 | def add_text(self, element, key, add_as=None): | ||
34 | value = self._get_element_text(element, key) | ||
35 | |||
36 | add_as = key if not add_as else add_as | ||
37 | self.add(add_as, value) | ||
38 | |||
39 | def add_date(self, element, key, add_as=None): | ||
40 | value = date_parser.parse(self._get_element_text(element, key)) | ||
41 | |||
42 | add_as = key if not add_as else add_as | ||
43 | self.add(add_as, value) | ||
44 | |||
45 | |||
46 | class ExchangeRequest(object): | ||
47 | |||
48 | def __init__(self, session, service, method='GET'): | ||
49 | self.session = session | ||
50 | self.service = service | ||
51 | self.method = method | ||
52 | self.server = session.server | ||
53 | self.username = session.username | ||
54 | self._headers = { 'Content-Type': 'text/xml', | ||
55 | 'Depth': '0', 'Translate': 'f' } | ||
56 | |||
57 | @property | ||
58 | def headers(self): | ||
59 | self._headers['Cookie'] = self.session.token | ||
60 | return self._headers | ||
61 | |||
62 | @property | ||
63 | def request_url(self): | ||
64 | path = '/'.join(['exchange', self.username, self.service]) | ||
65 | return '/{0}'.format(path) | ||
66 | |||
67 | def get_response(self, query=None): | ||
68 | connection = HTTPSConnection(self.server) | ||
69 | connection.request(self.method, self.request_url, query, | ||
70 | headers=self.headers) | ||
71 | resp = connection.getresponse() | ||
72 | |||
73 | if int(resp.status) > 299 or int(resp.status) < 200: | ||
74 | raise ExchangeException("%s %s" % (resp.status, resp.reason)) | ||
75 | |||
76 | return resp.read() | ||
22 | 77 | ||
23 | 78 | ||
24 | class ExchangeCommand(object): | 79 | class ExchangeCommand(object): |
@@ -27,18 +82,7 @@ class ExchangeCommand(object): | |||
27 | directly but should be subclassed to do useful things. | 82 | directly but should be subclassed to do useful things. |
28 | """ | 83 | """ |
29 | 84 | ||
30 | #: Base URL for Exchange commands. | ||
31 | BASE_URL = Template("/exchange/${username}/${method}") | ||
32 | |||
33 | #: Basic headers that are required for all requests | ||
34 | BASE_HEADERS = { | ||
35 | "Content-Type": 'text/xml; charset="UTF-8"', | ||
36 | "Depth": "0", | ||
37 | "Translate": "f", | ||
38 | } | ||
39 | |||
40 | def __init__(self, session): | 85 | def __init__(self, session): |
41 | self.server = session.server | ||
42 | self.session = session | 86 | self.session = session |
43 | 87 | ||
44 | def _get_xml(self, **kwargs): | 88 | def _get_xml(self, **kwargs): |
@@ -47,22 +91,13 @@ class ExchangeCommand(object): | |||
47 | @return: ElementTree response | 91 | @return: ElementTree response |
48 | """ | 92 | """ |
49 | kwargs["username"] = self.session.username | 93 | kwargs["username"] = self.session.username |
50 | |||
51 | xml = self._get_query(**kwargs) | 94 | xml = self._get_query(**kwargs) |
52 | url = self.BASE_URL.substitute({ "username": self.session.username, | ||
53 | "method": self.exchange_method }) | ||
54 | query = Template(xml).substitute(kwargs) | ||
55 | send_headers = self.BASE_HEADERS.copy() | ||
56 | send_headers['Cookie'] = self.session.token | ||
57 | |||
58 | conn = HTTPSConnection(self.server) | ||
59 | conn.request(self.dav_method.upper(), url, query, headers=send_headers) | ||
60 | resp = conn.getresponse() | ||
61 | 95 | ||
62 | if int(resp.status) > 299 or int(resp.status) < 200: | 96 | req = ExchangeRequest(self.session, self.exchange_method, |
63 | raise ExchangeException("%s %s" % (resp.status, resp.reason)) | 97 | self.dav_method) |
98 | resp = req.get_response(Template(xml).substitute(kwargs)) | ||
64 | 99 | ||
65 | return ElementTree.fromstring(resp.read()) | 100 | return ElementTree.fromstring(resp) |
66 | 101 | ||
67 | def _get_query(self, **kwargs): | 102 | def _get_query(self, **kwargs): |
68 | """ | 103 | """ |
@@ -82,37 +117,10 @@ class ExchangeCommand(object): | |||
82 | return output | 117 | return output |
83 | 118 | ||
84 | 119 | ||
85 | class Event(_Event): | ||
86 | |||
87 | def add_alarm(self, alarm_offset): | ||
88 | alarm = Alarm() | ||
89 | alarm.add("action", "DISPLAY") | ||
90 | alarm.add("description", "REMINDER") | ||
91 | alarm.add("trigger", timedelta(minutes=alarm_offset)) | ||
92 | self.add_component(alarm) | ||
93 | |||
94 | def _get_element_text(self, element, key): | ||
95 | value = element.find(key) | ||
96 | |||
97 | if hasattr(value, 'text'): | ||
98 | return value.text | ||
99 | |||
100 | def add_text(self, element, key, add_as=None): | ||
101 | value = self._get_element_text(element, key) | ||
102 | |||
103 | add_as = key if not add_as else add_as | ||
104 | self.add(add_as, value) | ||
105 | |||
106 | def add_date(self, element, key, add_as=None): | ||
107 | value = date_parser.parse(self._get_element_text(element, key)) | ||
108 | |||
109 | add_as = key if not add_as else add_as | ||
110 | self.add(add_as, value) | ||
111 | |||
112 | |||
113 | class FetchCalendar(ExchangeCommand): | 120 | class FetchCalendar(ExchangeCommand): |
121 | |||
114 | exchange_method = "calendar" | 122 | exchange_method = "calendar" |
115 | dav_method = "search" | 123 | dav_method = "SEARCH" |
116 | 124 | ||
117 | sql = """ | 125 | sql = """ |
118 | SELECT | 126 | SELECT |
@@ -148,9 +156,6 @@ class FetchCalendar(ExchangeCommand): | |||
148 | event.add_date(item, 'start_date', add_as='dtstart') | 156 | event.add_date(item, 'start_date', add_as='dtstart') |
149 | event.add_date(item, 'end_date', add_as='dtend') | 157 | event.add_date(item, 'end_date', add_as='dtend') |
150 | 158 | ||
151 | # TODO: Handle timezone_info | ||
152 | # TODO: Handle alarms. Previous implementation didn't work | ||
153 | |||
154 | calendar.add_component(event) | 159 | calendar.add_component(event) |
155 | 160 | ||
156 | return calendar | 161 | return calendar |