1 import weakref
2 import urllib
3
4
6 """
7 Class for representing a List on an InterMine Webservice
8 ========================================================
9
10 Lists represent stored collections of data and saved result
11 sets in an InterMine data warehouse. This class is an abstraction
12 of this information, and provides mechanisms for managing the
13 data.
14
15 SYNOPSIS
16 --------
17
18 example::
19
20 from intermine.webservice import Service
21
22 flymine = Service("www.flymine.org/query", "SOMETOKEN")
23 new_list = flymine.create_list(["h", "zen", "eve", "bib"], "Gene", name="My New List")
24
25 another_list = flymine.get_list("Some other list")
26 combined_list = new_list | another_list # Same syntax as for sets
27 combined_list.name = "Union of the other lists"
28
29 for row in combined_list.to_attribute_query().results():
30 print row
31
32 OVERVIEW
33 --------
34
35 Lists are created from a webservice, and can be manipulated in various ways.
36 The operations are:
37 * Union: this | that
38 * Intersection: this & that
39 * Symmetric Difference: this ^ that
40 * Asymmetric Difference (subtraction): this - that
41 * Appending: this += that
42
43 Lists can be created from a list of identifiers that could be:
44 * stored in a file
45 * held in a list or set
46 * contained in a string
47 In all these cases the syntax is the same:
48
49 new_list = service.create_list(content, type, name="Some name", description="Some description", tags=["some", "tags"])
50
51 Lists can also be created from a query's result with the exact
52 same syntax. In the case of queries, the type is not required,
53 but the query should have just one view, and it should be an id.
54
55 query = service.new_query()
56 query.add_view("Gene.id")
57 query.add_constraint("Gene.length", "<", 100)
58 new_list = service.create_list(query, name="Short Genes")
59
60 """
61
63 """
64 Constructor
65 ===========
66
67 Do not construct these objects yourself. They should be
68 fetched from a service or constructed using the "create_list"
69 method.
70 """
71 try:
72 self.service = args["service"]
73 self.manager = weakref.proxy(args["manager"])
74 self._name = args["name"]
75 self.title = args["title"]
76 self.description = args.get("description")
77 self.list_type = args["type"]
78 self.size = int(args["size"])
79 self.date_created = args.get("dateCreated")
80 self.is_authorized = args.get("authorized")
81 if self.is_authorized is None:
82 self.is_authorized = True
83 tags = args["tags"] if "tags" in args else []
84 self.tags = frozenset(tags)
85 except KeyError:
86 raise ValueError("Missing argument")
87 self.unmatched_identifiers = set([])
88
91
93 """
94 Set the name of the list
95 ========================
96
97 Setting the list's name causes the list's name to be updated on the server.
98 """
99 if self._name == new_name:
100 return
101 uri = self.service.root + self.service.LIST_RENAME_PATH
102 params = {
103 "oldname": self._name,
104 "newname": new_name
105 }
106 uri += "?" + urllib.urlencode(params)
107 resp = self.service.opener.open(uri)
108 data = resp.read()
109 resp.close()
110 new_list = self.manager.parse_list_upload_response(data)
111 self._name = new_name
112
114 raise AttributeError("List names cannot be deleted, only changed")
115
116 name = property(get_name, set_name, del_name, "The name of this list")
117
119 if ids is not None:
120 self.unmatched_identifiers.update(ids)
121
123 string = self.name + " (" + str(self.size) + " " + self.list_type + ")"
124 if self.date_created:
125 string += " " + self.date_created
126 if self.description:
127 string += " " + self.description
128 return string
129
131 """
132 Delete this list from the webservice
133 ====================================
134
135 Calls the webservice to delete this list immediately. This
136 object should not be used after this method is called - attempts
137 to do so will raise errors.
138 """
139 self.manager.delete_lists([self])
140
142 """
143 Construct a query to fetch the items in this list
144 =================================================
145
146 Return a new query constrained to the objects in this list,
147 and with a single view column of the objects ids.
148
149 @rtype: intermine.query.Query
150 """
151 q = self.service.new_query()
152 q.add_view(self.list_type + ".id")
153 q.add_constraint(self.list_type, "IN", self.name)
154 return q
155
157 """
158 Construct a query to fetch information about the items in this list
159 ===================================================================
160
161 Return a query constrained to contain the objects in this list, with
162 all the attributes of these objects selected for output as view columns
163
164 @rtype: intermine.query.Query
165 """
166 q = self.to_query()
167 attributes = q.model.get_class(self.list_type).attributes
168 q.clear_view()
169 q.add_view(map(lambda x: self.list_type + "." + x.name, attributes))
170 return q
171
173 """
174 Intersect this list and another
175 """
176 return self.manager.intersect([self, other])
177
179 """
180 Intersect this list and another, and replace this list with the result of the
181 intersection
182 """
183 intersection = self.manager.intersect([self, other], description=self.description, tags=self.tags)
184 self.delete()
185 intersection.name = self.name
186 return intersection
187
189 """
190 Return the union of this list and another
191 """
192 return self.manager.union([self, other])
193
195 """
196 Return the union of this list and another
197 """
198 return self.manager.union([self, other])
199
201 """
202 Append other to this list.
203 """
204 return self.append(other)
205
207 name = self.name
208 data = None
209
210 try:
211 ids = open(content).read()
212 except (TypeError, IOError):
213 if isinstance(content, basestring):
214 ids = content
215 else:
216 try:
217 ids = "\n".join(map(lambda x: '"' + x + '"', iter(content)))
218 except TypeError:
219 try:
220 uri = content.get_list_append_uri()
221 except:
222 content = content.to_query()
223 uri = content.get_list_append_uri()
224 params = content.to_query_params()
225 params["listName"] = name
226 params["path"] = None
227 form = urllib.urlencode(params)
228 resp = self.service.opener.open(uri, form)
229 data = resp.read()
230
231 if data is None:
232 uri = self.service.root + self.service.LIST_APPENDING_PATH
233 query_form = {'name': name}
234 uri += "?" + urllib.urlencode(query_form)
235 data = self.service.opener.post_plain_text(uri, ids)
236
237 new_list = self.manager.parse_list_upload_response(data)
238 self.unmatched_identifiers.update(new_list.unmatched_identifiers)
239 self.size = new_list.size
240 return self
241
243 "Append the arguments to this list"
244 try:
245 return self._do_append(self.manager.union(appendix))
246 except:
247 return self._do_append(appendix)
248
250 """Calculate the symmetric difference of this list and another"""
251 return self.manager.xor([self, other])
252
254 """Calculate the symmetric difference of this list and another and replace this list with the result"""
255 diff = self.manager.xor([self, other], description=self.description, tags=self.tags)
256 self.delete()
257 diff.name = self.name
258 return diff
259
261 """Subtract the other from this list"""
262 return self.manager.subtract([self], [other])
263
265 """Replace this list with the subtraction of the other from this list"""
266 subtr = self.manager.subtract([self], [other], description=self.description, tags=self.tags)
267 self.delete()
268 subtr.name = self.name
269 return subtr
270