chiark / gitweb /
Update youmagine api to fix compatibility with new youmagine release.
[cura.git] / Cura / util / youmagine.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import json
5 import httplib as httpclient
6 import urllib
7
8 class httpUploadDataStream(object):
9         def __init__(self, progressCallback):
10                 self._dataList = []
11                 self._totalLength = 0
12                 self._readPos = 0
13                 self._progressCallback = progressCallback
14
15         def write(self, data):
16                 size = len(data)
17                 if size < 1:
18                         return
19                 blocks = size / 2048
20                 for n in xrange(0, blocks):
21                         self._dataList.append(data[n*2048:n*2048+2048])
22                 self._dataList.append(data[blocks*2048:])
23                 self._totalLength += size
24
25         def read(self, size):
26                 if self._readPos >= len(self._dataList):
27                         return None
28                 ret = self._dataList[self._readPos]
29                 self._readPos += 1
30                 if self._progressCallback is not None:
31                         self._progressCallback(float(self._readPos / len(self._dataList)))
32                 return ret
33
34         def __len__(self):
35                 return self._totalLength
36
37 class Youmagine(object):
38         def __init__(self, authToken, progressCallback = None):
39                 self._hostUrl = 'api.youmagine.com'
40                 self._viewUrl = 'www.youmagine.com'
41                 self._authUrl = 'https://www.youmagine.com/integrations/cura/authorized_integrations/new'
42                 self._authToken = authToken
43                 self._userName = None
44                 self._userID = None
45                 self._http = None
46                 self._hostReachable = True
47                 self._progressCallback = progressCallback
48                 self._categories = [
49                         ('Art', 2),
50                         ('Fashion', 3),
51                         ('For your home', 4),
52                         ('Gadget', 5),
53                         ('Games', 6),
54                         ('Jewelry', 7),
55                         ('Maker/DIY', 8),
56                         ('Miniatures', 9),
57                         ('Toys', 10),
58                         ('3D printer parts and enhancements', 11),
59                         ('Other', 1),
60                 ]
61                 self._licenses = [
62                         ('Creative Commons - Attribution Share Alike', 'ccbysa'),
63                         ('Creative Commons - Attribution Non-Commercial ShareAlike', 'ccbyncsa'),
64                         ('Creative Commons - Attribution No Derivatives', 'ccbynd'),
65                         ('Creative Commons - Attribution Non-Commercial No Derivatives', 'ccbyncsa'),
66                         ('GPLv3', 'gplv3'),
67                 ]
68
69         def getAuthorizationUrl(self):
70                 return self._authUrl
71
72         def getCategories(self):
73                 return map(lambda n: n[0], self._categories)
74
75         def getLicenses(self):
76                 return map(lambda n: n[0], self._licenses)
77
78         def setAuthToken(self, token):
79                 self._authToken = token
80                 self._userName = None
81                 self._userID = None
82
83         def getAuthToken(self):
84                 return self._authToken
85
86         def isHostReachable(self):
87                 return self._hostReachable
88
89         def viewUrlForDesign(self, id):
90                 return 'https://%s/designs/%d' % (self._viewUrl, id)
91
92         def editUrlForDesign(self, id):
93                 return 'https://%s/designs/%d/edit' % (self._viewUrl, id)
94
95         def isAuthorized(self):
96                 if self._authToken is None:
97                         return False
98                 if self._userName is None:
99                         #No username yet, try to request the username to see if the authToken is valid.
100                         result = self._request('GET', '/authorized_integrations/%s/whoami.json' % (self._authToken))
101
102                         if 'error' in result:
103                                 self._authToken = None
104                                 return False
105                         self._userName = result['screen_name']
106                         self._userID = result['id']
107                 return True
108
109         def createDesign(self, name, description, category, license):
110                 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]})
111                 if 'id' in res:
112                         return res['id']
113                 print res
114                 return None
115
116         def publishDesign(self, id):
117                 res = self._request('PUT', '/designs/%d/mark_as/publish.json' % (id), {'ignore': 'me'})
118                 if res is not None:
119                         return False
120                 return True
121
122         def createDocument(self, designId, name, contents):
123                 res = self._request('POST', '/designs/%d/documents.json' % (designId), {'document[name]': name, 'document[description]': 'Uploaded from Cura'}, {'document[file]': (name, contents)})
124                 if 'id' in res:
125                         return res['id']
126                 print res
127                 return None
128
129         def createImage(self, designId, name, contents):
130                 res = self._request('POST', '/designs/%d/images.json' % (designId), {'image[name]': name, 'image[description]': 'Uploaded from Cura'}, {'image[file]': (name, contents)})
131                 if 'id' in res:
132                         return res['id']
133                 print res
134                 return None
135
136         def listDesigns(self):
137                 res = self._request('GET', '/users/%s/designs.json' % (self._userID))
138                 return res
139
140         def _request(self, method, url, postData = None, files = None):
141                 retryCount = 2
142                 if self._authToken is not None:
143                         url += '?auth_token=%s' % (self._authToken)
144                 error = 'Failed to connect to %s' % self._hostUrl
145                 for n in xrange(0, retryCount):
146                         if self._http is None:
147                                 self._http = httpclient.HTTPSConnection(self._hostUrl)
148                         try:
149                                 if files is not None:
150                                         boundary = 'wL36Yn8afVp8Ag7AmP8qZ0SA4n1v9T'
151                                         s = httpUploadDataStream(self._progressCallback)
152                                         for k, v in files.iteritems():
153                                                 filename = v[0]
154                                                 fileContents = v[1]
155                                                 s.write('--%s\r\n' % (boundary))
156                                                 s.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (k, filename))
157                                                 s.write('Content-Type: application/octet-stream\r\n')
158                                                 s.write('Content-Transfer-Encoding: binary\r\n')
159                                                 s.write('\r\n')
160                                                 s.write(fileContents)
161                                                 s.write('\r\n')
162
163                                         for k, v in postData.iteritems():
164                                                 s.write('--%s\r\n' % (boundary))
165                                                 s.write('Content-Disposition: form-data; name="%s"\r\n' % (k))
166                                                 s.write('\r\n')
167                                                 s.write(str(v))
168                                                 s.write('\r\n')
169                                         s.write('--%s--\r\n' % (boundary))
170
171                                         self._http.request(method, url, s, {"Content-type": "multipart/form-data; boundary=%s" % (boundary), "Content-Length": len(s)})
172                                 elif postData is not None:
173                                         self._http.request(method, url, urllib.urlencode(postData), {"Content-type": "application/x-www-form-urlencoded"})
174                                 else:
175                                         self._http.request(method, url)
176                         except IOError:
177                                 self._http.close()
178                                 continue
179                         try:
180                                 response = self._http.getresponse()
181                                 responseText = response.read()
182                         except:
183                                 self._http.close()
184                                 continue
185                         try:
186                                 if responseText == '':
187                                         return None
188                                 return json.loads(responseText)
189                         except ValueError:
190                                 print response.getheaders()
191                                 print responseText
192                                 error = 'Failed to decode JSON response'
193                 self._hostReachable = False
194                 return {'error': error}
195
196
197 #Fake Youmagine class to test without internet
198 class FakeYoumagine(Youmagine):
199         def __init__(self, authToken, callback):
200                 super(FakeYoumagine, self).__init__(authToken)
201                 self._authUrl = 'file:///C:/Models/output.html'
202                 self._authToken = None
203
204         def isAuthorized(self):
205                 if self._authToken is None:
206                         return False
207                 if self._userName is None:
208                         self._userName = 'FakeYoumagine'
209                         self._userID = '1'
210                 return True
211
212         def isHostReachable(self):
213                 return True
214
215         def createDesign(self, name, description, category, license):
216                 return 1
217
218         def publishDesign(self, id):
219                 pass
220
221         def createDocument(self, designId, name, contents):
222                 print "Create document: %s" % (name)
223                 f = open("C:/models/%s" % (name), "wb")
224                 f.write(contents)
225                 f.close()
226                 return 1
227
228         def createImage(self, designId, name, contents):
229                 print "Create image: %s" % (name)
230                 f = open("C:/models/%s" % (name), "wb")
231                 f.write(contents)
232                 f.close()
233                 return 1
234
235         def listDesigns(self):
236                 return []
237
238         def _request(self, method, url, postData = None, files = None):
239                 print "Err: Tried to do request: %s %s" % (method, url)
240
241 def main():
242         ym = Youmagine('j3rY9kQF62ptuZF7vqbR')
243         if not ym.isAuthorized():
244                 print "Failed to authorize"
245                 return
246         for design in ym.listDesigns():
247                 print design['name']