forked from OCA/report-print-send
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pingen.py
227 lines (181 loc) · 7.21 KB
/
pingen.py
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Guewen Baconnier
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import requests
import logging
import urlparse
import json
import pytz
from datetime import datetime
from requests.packages.urllib3.filepost import encode_multipart_formdata
_logger = logging.getLogger(__name__)
POST_SENDING_STATUS = {
100: 'Ready/Pending',
101: 'Processing',
102: 'Waiting for confirmation',
200: 'Sent',
300: 'Some error occured and object wasn\'t sent',
400: 'Sending cancelled',
}
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' # this is the format used by pingen API
TZ = pytz.timezone('Europe/Zurich') # this is the timezone of the pingen API
def pingen_datetime_to_utc(dt):
""" Convert a date/time used by pingen.com to UTC timezone
:param dt: pingen date/time as string (as received from the API)
to convert to UTC
:return: datetime in the UTC timezone
"""
utc = pytz.utc
dt = datetime.strptime(dt, DATETIME_FORMAT)
localized_dt = TZ.localize(dt, is_dst=True)
return localized_dt.astimezone(utc)
class PingenException(RuntimeError):
"""There was an ambiguous exception that occurred while handling your
request."""
class ConnectionError(PingenException):
"""An Error occured with the pingen API"""
class APIError(PingenException):
"""An Error occured with the pingen API"""
class Pingen(object):
""" Interface to the pingen.com API """
def __init__(self, token, staging=True):
self._token = token
self.staging = staging
self._session = None
super(Pingen, self).__init__()
@property
def url(self):
if self.staging:
return 'https://stage-api.pingen.com'
return 'https://api.pingen.com'
@property
def session(self):
""" Build a requests session """
if self._session is not None:
return self._session
self._session = requests.Session(
params={'token': self._token},
# with safe_mode, requests catch errors and
# returns a blank response with an error
config={'safe_mode': True},
# verify = False required for staging environment
# because the SSL certificate is wrong
verify=not self.staging)
return self._session
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""Dispose of any internal state. """
if self._session:
self._session.close()
def _send(self, method, endpoint, **kwargs):
""" Send a request to the pingen API using requests
Add necessary boilerplate to call pingen.com API
(authentication, configuration, ...)
:param boundmethod method: requests method to call
:param str endpoint: endpoint to call
:param kwargs: additional arguments forwarded to the requests method
"""
complete_url = urlparse.urljoin(self.url, endpoint)
response = method(complete_url, **kwargs)
if not response.ok:
raise ConnectionError(
"%s: %s" % (response.json['errorcode'],
response.json['errormessage']))
if response.json['error']:
raise APIError(
"%s: %s" % (response.json['errorcode'], response.json['errormessage']))
return response
def push_document(self, filename, filestream, send=None, speed=None, color=None):
""" Upload a document to pingen.com and eventually ask to send it
:param str filename: name of the file to push
:param StringIO filestream: file to push
:param boolean send: if True, the document will be sent by pingen.com
:param int/str speed: sending speed of the document if it is send
1 = Priority, 2 = Economy
:param int/str color: type of print, 0 = B/W, 1 = Color
:return: tuple with 3 items:
1. document_id on pingen.com
2. post_id on pingen.com if it has been sent or None
3. dict of the created item on pingen (details)
"""
data = {
'send': send,
'speed': speed,
'color': color,
}
# we cannot use the `files` param alongside
# with the `datas`param when data is a
# JSON-encoded data. We have to construct
# the entire body and send it to `data`
# https://github.com/kennethreitz/requests/issues/950
formdata = {
'file': (filename, filestream.read()),
'data': json.dumps(data),
}
multipart, content_type = encode_multipart_formdata(formdata)
response = self._send(
self.session.post,
'document/upload',
headers={'Content-Type': content_type},
data=multipart)
rjson = response.json
document_id = rjson['id']
if rjson.get('send'):
# confusing name but send_id is the posted id
posted_id = rjson['send'][0]['send_id']
item = rjson['item']
return document_id, posted_id, item
def send_document(self, document_id, speed=None, color=None):
""" Send a uploaded document to pingen.com
:param int document_id: id of the document to send
:param int/str speed: sending speed of the document if it is send
1 = Priority, 2 = Economy
:param int/str color: type of print, 0 = B/W, 1 = Color
:return: id of the post on pingen.com
"""
data = {
'speed': speed,
'color': color,
}
response = self._send(
self.session.post,
'document/send',
params={'id': document_id},
data={'data': json.dumps(data)})
return response.json['id']
def post_infos(self, post_id):
""" Return the information of a post
:param int post_id: id of the document to send
:return: dict of infos of the post
"""
response = self._send(
self.session.get,
'post/get',
params={'id': post_id})
return response.json['item']
@staticmethod
def is_posted(post_infos):
""" return True if the post has been sent
:param dict post_infos: post infos returned by `post_infos`
"""
return post_infos['status'] == 200