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
11 class httpUploadDataStream(object):
13 For http uploads we need a readable/writable datasteam to use with the httpclient.HTTPSConnection class.
14 This is used to facilitate file uploads towards Youmagine.
16 def __init__(self, progressCallback):
20 self._progressCallback = progressCallback
22 def write(self, data):
27 for n in xrange(0, blocks):
28 self._dataList.append(data[n*2048:n*2048+2048])
29 self._dataList.append(data[blocks*2048:])
30 self._totalLength += size
33 if self._readPos >= len(self._dataList):
35 ret = self._dataList[self._readPos]
37 if self._progressCallback is not None:
38 self._progressCallback(float(self._readPos / len(self._dataList)))
42 return self._totalLength
44 class Youmagine(object):
46 Youmagine connection object. Has various functions to communicate with Youmagine.
47 These functions are blocking and thus this class should be used from a thread.
49 def __init__(self, authToken, progressCallback = None):
50 self._hostUrl = 'api.youmagine.com'
51 self._viewUrl = 'www.youmagine.com'
52 self._authUrl = 'https://www.youmagine.com/integrations/cura/authorized_integrations/new'
53 self._authToken = authToken
57 self._hostReachable = True
58 self._progressCallback = progressCallback
69 ('3D printer parts and enhancements', 11),
73 ('Creative Commons - Attribution Share Alike', 'ccbysa'),
74 ('Creative Commons - Attribution Non-Commercial ShareAlike', 'ccbyncsa'),
75 ('Creative Commons - Attribution No Derivatives', 'ccbynd'),
76 ('Creative Commons - Attribution Non-Commercial No Derivatives', 'ccbyncsa'),
80 def getAuthorizationUrl(self):
83 def getCategories(self):
84 return map(lambda n: n[0], self._categories)
86 def getLicenses(self):
87 return map(lambda n: n[0], self._licenses)
89 def setAuthToken(self, token):
90 self._authToken = token
94 def getAuthToken(self):
95 return self._authToken
97 def isHostReachable(self):
98 return self._hostReachable
100 def viewUrlForDesign(self, id):
101 return 'https://%s/designs/%d' % (self._viewUrl, id)
103 def editUrlForDesign(self, id):
104 return 'https://%s/designs/%d/edit' % (self._viewUrl, id)
106 def isAuthorized(self):
107 if self._authToken is None:
109 if self._userName is None:
110 #No username yet, try to request the username to see if the authToken is valid.
111 result = self._request('GET', '/authorized_integrations/%s/whoami.json' % (self._authToken))
113 if 'error' in result:
114 self._authToken = None
116 self._userName = result['screen_name']
117 self._userID = result['id']
120 def createDesign(self, name, description, category, license):
121 res = self._request('POST', '/designs.json', {'design[name]': name, 'design[excerpt]': 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]})
127 def publishDesign(self, id):
128 res = self._request('PUT', '/designs/%d/mark_as/publish.json' % (id), {'ignore': 'me'})
133 def createDocument(self, designId, name, contents):
134 res = self._request('POST', '/designs/%d/documents.json' % (designId), {'document[name]': name, 'document[description]': 'Uploaded from Cura'}, {'document[file]': (name, contents)})
140 def createImage(self, designId, name, contents):
141 res = self._request('POST', '/designs/%d/images.json' % (designId), {'image[name]': name, 'image[description]': 'Uploaded from Cura'}, {'image[file]': (name, contents)})
147 def listDesigns(self):
148 res = self._request('GET', '/users/%s/designs.json' % (self._userID))
151 def _request(self, method, url, postData = None, files = None):
153 if self._authToken is not None:
154 url += '?auth_token=%s' % (self._authToken)
155 error = 'Failed to connect to %s' % self._hostUrl
156 for n in xrange(0, retryCount):
157 if self._http is None:
158 self._http = httpclient.HTTPSConnection(self._hostUrl)
160 if files is not None:
161 boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T'
162 s = httpUploadDataStream(self._progressCallback)
163 for k, v in files.iteritems():
166 s.write('--%s\r\n' % (boundary))
167 s.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (k, filename))
168 s.write('Content-Type: application/octet-stream\r\n')
169 s.write('Content-Transfer-Encoding: binary\r\n')
171 s.write(fileContents)
174 for k, v in postData.iteritems():
175 s.write('--%s\r\n' % (boundary))
176 s.write('Content-Disposition: form-data; name="%s"\r\n' % (k))
180 s.write('--%s--\r\n' % (boundary))
182 self._http.request(method, url, s, {"Content-type": "multipart/form-data; boundary=%s" % (boundary), "Content-Length": len(s)})
183 elif postData is not None:
184 self._http.request(method, url, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
186 self._http.request(method, url)
191 response = self._http.getresponse()
192 responseText = response.read()
197 if responseText == '':
199 return json.loads(responseText)
201 print response.getheaders()
203 error = 'Failed to decode JSON response'
204 self._hostReachable = False
205 return {'error': error}
208 class FakeYoumagine(Youmagine):
210 Fake Youmagine class to test without internet, acts the same as the YouMagine class, but without going to the internet.
211 Assists in testing UI features.
213 def __init__(self, authToken, callback):
214 super(FakeYoumagine, self).__init__(authToken)
215 self._authUrl = 'file:///C:/Models/output.html'
216 self._authToken = None
218 def isAuthorized(self):
219 if self._authToken is None:
221 if self._userName is None:
222 self._userName = 'FakeYoumagine'
226 def isHostReachable(self):
229 def createDesign(self, name, description, category, license):
232 def publishDesign(self, id):
235 def createDocument(self, designId, name, contents):
236 print "Create document: %s" % (name)
237 f = open("C:/models/%s" % (name), "wb")
242 def createImage(self, designId, name, contents):
243 print "Create image: %s" % (name)
244 f = open("C:/models/%s" % (name), "wb")
249 def listDesigns(self):
252 def _request(self, method, url, postData = None, files = None):
253 print "Err: Tried to do request: %s %s" % (method, url)