2 YouMagine communication module.
3 This module handles all communication with the YouMagine API.
5 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
8 import httplib as httpclient
12 class httpUploadDataStream(object):
14 For http uploads we need a readable/writable datasteam to use with the httpclient.HTTPSConnection class.
15 This is used to facilitate file uploads towards Youmagine.
17 def __init__(self, progressCallback):
21 self._progressCallback = progressCallback
23 def write(self, data):
28 for n in xrange(0, blocks):
29 self._dataList.append(data[n*2048:n*2048+2048])
30 self._dataList.append(data[blocks*2048:])
31 self._totalLength += size
34 if self._readPos >= len(self._dataList):
36 ret = self._dataList[self._readPos]
38 if self._progressCallback is not None:
39 self._progressCallback(float(self._readPos / len(self._dataList)))
43 return self._totalLength
45 class Youmagine(object):
47 Youmagine connection object. Has various functions to communicate with Youmagine.
48 These functions are blocking and thus this class should be used from a thread.
50 def __init__(self, authToken, progressCallback = None):
51 self._hostUrl = 'api.youmagine.com'
52 self._viewUrl = 'www.youmagine.com'
53 self._authUrl = 'https://www.youmagine.com/integrations/cura/authorized_integrations/new'
54 self._authToken = authToken
58 self._hostReachable = True
59 self._progressCallback = progressCallback
70 ('3D printer parts and enhancements', 11),
74 ('Creative Commons - Attribution Share Alike', 'ccbysa'),
75 ('Creative Commons - Attribution Non-Commercial ShareAlike', 'ccbyncsa'),
76 ('Creative Commons - Attribution No Derivatives', 'ccbynd'),
77 ('Creative Commons - Attribution Non-Commercial No Derivatives', 'ccbyncsa'),
81 def getAuthorizationUrl(self):
84 def getCategories(self):
85 return map(lambda n: n[0], self._categories)
87 def getLicenses(self):
88 return map(lambda n: n[0], self._licenses)
90 def setAuthToken(self, token):
91 self._authToken = token
95 def getAuthToken(self):
96 return self._authToken
98 def isHostReachable(self):
99 return self._hostReachable
101 def viewUrlForDesign(self, id):
102 return 'https://%s/designs/%d' % (self._viewUrl, id)
104 def editUrlForDesign(self, id):
105 return 'https://%s/designs/%d/edit' % (self._viewUrl, id)
107 def isAuthorized(self):
108 if self._authToken is None:
110 if self._userName is None:
111 #No username yet, try to request the username to see if the authToken is valid.
112 result = self._request('GET', '/authorized_integrations/%s/whoami.json' % (self._authToken))
114 if 'error' in result:
115 self._authToken = None
117 self._userName = result['screen_name']
118 self._userID = result['id']
121 def createDesign(self, name, description, category, license):
122 excerpt = description
124 if len(excerpt) >= 300:
125 lines = textwrap.wrap(excerpt, 300)
127 description = '\n'.join(lines[1:])
128 res = self._request('POST', '/designs.json', {'design[name]': name, 'design[excerpt]': excerpt, 'design[description]': description, 'design[design_category_id]': filter(lambda n: n[0] == category, self._categories)[0][1], 'design[license]': filter(lambda n: n[0] == license, self._licenses)[0][1]})
134 def publishDesign(self, id):
135 res = self._request('PUT', '/designs/%d/mark_as/publish.json' % (id), {'ignore': 'me'})
140 def createDocument(self, designId, name, contents):
141 res = self._request('POST', '/designs/%d/documents.json' % (designId), {'document[name]': name, 'document[description]': 'Uploaded from Cura'}, {'document[file]': (name, contents)})
147 def createImage(self, designId, name, contents):
148 res = self._request('POST', '/designs/%d/images.json' % (designId), {'image[name]': name, 'image[description]': 'Uploaded from Cura'}, {'image[file]': (name, contents)})
154 def listDesigns(self):
155 res = self._request('GET', '/users/%s/designs.json' % (self._userID))
158 def _request(self, method, url, postData = None, files = None):
160 if self._authToken is not None:
161 url += '?auth_token=%s' % (self._authToken)
162 error = 'Failed to connect to %s' % self._hostUrl
163 for n in xrange(0, retryCount):
164 if self._http is None:
165 self._http = httpclient.HTTPSConnection(self._hostUrl)
167 if files is not None:
168 boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T'
169 s = httpUploadDataStream(self._progressCallback)
170 for k, v in files.iteritems():
173 s.write('--%s\r\n' % (boundary))
174 s.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (k, filename))
175 s.write('Content-Type: application/octet-stream\r\n')
176 s.write('Content-Transfer-Encoding: binary\r\n')
178 s.write(fileContents)
181 for k, v in postData.iteritems():
182 s.write('--%s\r\n' % (boundary))
183 s.write('Content-Disposition: form-data; name="%s"\r\n' % (k))
187 s.write('--%s--\r\n' % (boundary))
189 self._http.request(method, url, s, {"Content-type": "multipart/form-data; boundary=%s" % (boundary), "Content-Length": len(s)})
190 elif postData is not None:
191 self._http.request(method, url, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
193 self._http.request(method, url)
198 response = self._http.getresponse()
199 responseText = response.read()
204 if responseText == '':
206 return json.loads(responseText)
208 print response.getheaders()
210 error = 'Failed to decode JSON response'
211 self._hostReachable = False
212 return {'error': error}
215 class FakeYoumagine(Youmagine):
217 Fake Youmagine class to test without internet, acts the same as the YouMagine class, but without going to the internet.
218 Assists in testing UI features.
220 def __init__(self, authToken, callback):
221 super(FakeYoumagine, self).__init__(authToken)
222 self._authUrl = 'file:///C:/Models/output.html'
223 self._authToken = None
225 def isAuthorized(self):
226 if self._authToken is None:
228 if self._userName is None:
229 self._userName = 'FakeYoumagine'
233 def isHostReachable(self):
236 def createDesign(self, name, description, category, license):
239 def publishDesign(self, id):
242 def createDocument(self, designId, name, contents):
243 print "Create document: %s" % (name)
244 f = open("C:/models/%s" % (name), "wb")
249 def createImage(self, designId, name, contents):
250 print "Create image: %s" % (name)
251 f = open("C:/models/%s" % (name), "wb")
256 def listDesigns(self):
259 def _request(self, method, url, postData = None, files = None):
260 print "Err: Tried to do request: %s %s" % (method, url)