1 import weakref
2
3
4 try:
5 import json
6 except ImportError:
7 import simplejson as json
8
9 import urllib
10 import codecs
11
12 from .list import List
15 """
16 A Class for Managing List Content and Operations
17 ================================================
18
19 This class serves as a delegate for the intermine.webservice.Service class,
20 managing list content and operations.
21
22 This class is not meant to be called itself, but rather for its
23 methods to be called by the service object.
24 """
25
26 DEFAULT_LIST_NAME = "my_list_"
27 DEFAULT_DESCRIPTION = "List created with Python client library"
28
29 INTERSECTION_PATH = '/lists/intersect/json'
30 UNION_PATH = '/lists/union/json'
31 DIFFERENCE_PATH = '/lists/diff/json'
32 SUBTRACTION_PATH = '/lists/subtract/json'
33
35 self.service = weakref.proxy(service)
36 self.lists = None
37 self._temp_lists = set()
38
40 """Update the list information with the latest details from the server"""
41 self.lists = {}
42 url = self.service.root + self.service.LIST_PATH
43 sock = self.service.opener.open(url)
44 data = sock.read()
45 sock.close()
46 list_info = json.loads(data)
47 if not list_info.get("wasSuccessful"):
48 raise ListServiceError(list_info.get("error"))
49 for l in list_info["lists"]:
50 l = ListManager.safe_dict(l)
51 self.lists[l["name"]] = List(service=self.service, manager=self, **l)
52
53 @staticmethod
55 """Recursively clone json structure with UTF-8 dictionary keys"""
56 if isinstance(d, dict):
57 return dict([(k.encode('utf-8'), v) for k,v in d.iteritems()])
58 else:
59 return d
60
62 """Return a list from the service by name, if it exists"""
63 if self.lists is None:
64 self.refresh_lists()
65 return self.lists.get(name)
66
70
72 """Get all the lists on a webservice"""
73 if self.lists is None:
74 self.refresh_lists()
75 return self.lists.values()
76
78 """Get all the names of the lists in a particular webservice"""
79 if self.lists is None:
80 self.refresh_lists()
81 return self.lists.keys()
82
84 """
85 Return the number of lists accessible at the given webservice.
86 This number will vary depending on who you are authenticated as.
87 """
88 return len(self.get_all_list_names())
89
100
101 - def create_list(self, content, list_type="", name=None, description=None, tags=[]):
102 """
103 Create a new list in the webservice
104 ===================================
105
106 If no name is given, the list will be considered to be a temporary
107 list, and will be automatically deleted when the program ends. To prevent
108 this happening, give the list a name, either on creation, or by renaming it.
109
110 @rtype: intermine.lists.List
111 """
112 if description is None:
113 description = self.DEFAULT_DESCRIPTION
114
115 if name is None:
116 name = self.get_unused_list_name()
117
118 try:
119 ids = open(content).read()
120 except (TypeError, IOError):
121 if isinstance(content, basestring):
122 ids = content
123 else:
124 try:
125 ids = "\n".join(map(lambda x: '"' + x + '"', iter(content)))
126 except TypeError:
127 try:
128 uri = content.get_list_upload_uri()
129 except:
130 content = content.to_query()
131 uri = content.get_list_upload_uri()
132 params = content.to_query_params()
133 params["listName"] = name
134 params["description"] = description
135 form = urllib.urlencode(params)
136 resp = self.service.opener.open(uri, form)
137 data = resp.read()
138 resp.close()
139 return self.parse_list_upload_response(data)
140
141 uri = self.service.root + self.service.LIST_CREATION_PATH
142 query_form = {'name': name, 'type': list_type, 'description': description, 'tags': ";".join(tags)}
143 uri += "?" + urllib.urlencode(query_form)
144 data = self.service.opener.post_plain_text(uri, ids)
145 return self.parse_list_upload_response(data)
146
148 """
149 Intepret the response from the webserver to a list request, and return the List it describes
150 """
151 try:
152 response_data = json.loads(response)
153 except ValueError:
154 raise ListServiceError("Error parsing response: " + response)
155 if not response_data.get("wasSuccessful"):
156 raise ListServiceError(response_data.get("error"))
157 self.refresh_lists()
158 new_list = self.get_list(response_data["listName"])
159 failed_matches = response_data.get("unmatchedIdentifiers")
160 new_list._add_failed_matches(failed_matches)
161 return new_list
162
164 """Delete the given lists from the webserver"""
165 for l in lists:
166 if isinstance(l, List):
167 name = l.name
168 else:
169 name = str(l)
170 if name not in self.get_all_list_names():
171 continue
172 uri = self.service.root + self.service.LIST_PATH
173 query_form = {'name': name}
174 uri += "?" + urllib.urlencode(query_form)
175 response = self.service.opener.delete(uri)
176 response_data = json.loads(response)
177 if not response_data.get("wasSuccessful"):
178 raise ListServiceError(response_data.get("error"))
179 self.refresh_lists()
180
182 """Delete all the lists considered temporary (those created without names)"""
183 self.delete_lists(self._temp_lists)
184 self._temp_lists = set()
185
186 - def intersect(self, lists, name=None, description=None, tags=[]):
187 """Calculate the intersection of a given set of lists, and return the list representing the result"""
188 return self._do_operation(self.INTERSECTION_PATH, "Intersection", lists, name, description, tags)
189
190 - def union(self, lists, name=None, description=None, tags=[]):
191 """Calculate the union of a given set of lists, and return the list representing the result"""
192 return self._do_operation(self.UNION_PATH, "Union", lists, name, description, tags)
193
194 - def xor(self, lists, name=None, description=None, tags=[]):
195 """Calculate the symmetric difference of a given set of lists, and return the list representing the result"""
196 return self._do_operation(self.DIFFERENCE_PATH, "Difference", lists, name, description, tags)
197
198 - def subtract(self, lefts, rights, name=None, description=None, tags=[]):
199 """Calculate the subtraction of rights from lefts, and return the list representing the result"""
200 left_names = self.make_list_names(lefts)
201 right_names = self.make_list_names(rights)
202 if description is None:
203 description = "Subtraction of " + ' and '.join(right_names) + " from " + ' and '.join(left_names)
204 if name is None:
205 name = self.get_unused_list_name()
206 uri = self.service.root + self.SUBTRACTION_PATH
207 uri += '?' + urllib.urlencode({
208 "name": name,
209 "description": description,
210 "references": ';'.join(left_names),
211 "subtract": ';'.join(right_names),
212 "tags": ";".join(tags)
213 })
214 resp = self.service.opener.open(uri)
215 data = resp.read()
216 resp.close()
217 return self.parse_list_upload_response(data)
218
219 - def _do_operation(self, path, operation, lists, name, description, tags):
220 list_names = self.make_list_names(lists)
221 if description is None:
222 description = operation + " of " + ' and '.join(list_names)
223 if name is None:
224 name = self.get_unused_list_name()
225 uri = self.service.root + path
226 uri += '?' + urllib.urlencode({
227 "name": name,
228 "lists": ';'.join(list_names),
229 "description": description,
230 "tags": ";".join(tags)
231 })
232 resp = self.service.opener.open(uri)
233 data = resp.read()
234 resp.close()
235 return self.parse_list_upload_response(data)
236
237
239 """Turn a list of things into a list of list names"""
240 list_names = []
241 for l in lists:
242 try:
243 t = l.list_type
244 list_names.append(l.name)
245 except AttributeError:
246 try:
247 m = l.model
248 list_names.append(self.create_list(l).name)
249 except AttributeError:
250 list_names.append(str(l))
251
252 return list_names
253
255 """Errors thrown when something goes wrong with list requests"""
256 pass
257