1
2
3
4
5
6
7
8 """
9 webel - A Cinfony module that runs entirely on web services
10
11 webel can be used from all of CPython, Jython and IronPython.
12
13 Global variables:
14 informats - a dictionary of supported input formats
15 outformats - a dictionary of supported output formats
16 fps - a list of supported fingerprint types
17 """
18
19 import re
20 import os
21 import urllib2
22 import StringIO
23
24 try:
25 import Tkinter as tk
26 import Image as PIL
27 import ImageTk as piltk
28 except ImportError:
29 tk = None
30
31 informats = {"smi":"SMILES", "inchikey":"InChIKey", "inchi":"InChI",
32 "name":"Common name"}
33 """A dictionary of supported input formats"""
34 outformats = {"smi":"SMILES", "cdxml":"ChemDraw XML", "inchi":"InChI",
35 "sdf":"Symyx SDF", "names":"Common names", "inchikey":"InChIKey",
36 "alc":"Alchemy", "cerius":"MSI Cerius II", "charmm":"CHARMM",
37 "cif":"Crystallographic Information File",
38 "cml":"Chemical Markup Language", "ctx":"Gasteiger Clear Text",
39 "gjf":"Gaussian job file", "gromacs":"GROMACS",
40 "hyperchem":"HyperChem", "jme":"Java Molecule Editor",
41 "maestro":"Schrodinger MacroModel",
42 "mol":"Symyx mol", "mol2":"Tripos Sybyl MOL2",
43 "mrv":"ChemAxon MRV", "pdb":"Protein Data Bank",
44 "sdf3000":"Symyx SDF3000", "sln":"Sybl line notation",
45 "xyz":"XYZ", "iupac":"IUPAC name"}
46 """A dictionary of supported output formats"""
47
48 fps = ["std", "maccs", "estate"]
49 """A list of supported fingerprint types"""
50
51
52 -def _quo(text, safe="/"):
53 always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
54 'abcdefghijklmnopqrstuvwxyz'
55 '0123456789' '_.-')
56 _safemaps = {}
57 cachekey = (safe, always_safe)
58 try:
59 safe_map = _safemaps[cachekey]
60 except KeyError:
61 safe += always_safe
62 safe_map = {}
63 for i in range(256):
64 c = chr(i)
65 safe_map[c] = (c in safe) and c or ('%%%02X' % i)
66 _safemaps[cachekey] = safe_map
67 res = map(safe_map.__getitem__, text)
68 return ''.join(res)
69
71 """Curry the name of the server"""
72 def server(*urlcomponents):
73 url = "%s/" % serverurl + "/".join(urlcomponents)
74 resp = urllib2.urlopen(url)
75 return resp.read()
76 return server
77
78 rajweb = _makeserver("http://ws1.bmc.uu.se:8182/cdk")
79 nci = _makeserver("http://cactus.nci.nih.gov/chemical/structure")
80
81 _descs = None
83 """Return a list of supported descriptor types"""
84 global _descs
85 if not _descs:
86 response = rajweb("descriptors").rstrip()
87 _descs = [x.split(".")[-1] for x in response.split("\n")]
88 return _descs
89
91 """Read in a molecule from a string.
92
93 Required parameters:
94 format - see the informats variable for a list of available
95 input formats
96 string
97
98 Note: For InChIKeys a list of molecules is returned.
99
100 Example:
101 >>> input = "C1=CC=CS1"
102 >>> mymol = readstring("smi", input)
103 """
104 format = format.lower()
105 if not format in informats:
106 raise ValueError("%s is not a recognised Webel format" % format)
107
108 if format != "smi":
109 smiles = nci(_quo(string), "smiles").rstrip()
110 else:
111 smiles = string
112 if format == "inchikey":
113 return [Molecule(smile) for smile in smiles.split("\n")]
114 else:
115 mol = Molecule(smiles)
116 if format == "name":
117 mol.title = string
118 return mol
119
121 """Represent a file to which *output* is to be sent.
122
123 Although it's possible to write a single molecule to a file by
124 calling the write() method of a molecule, if multiple molecules
125 are to be written to the same file you should use the Outputfile
126 class.
127
128 Required parameters:
129 format - see the outformats variable for a list of available
130 output formats
131 filename
132
133 Optional parameters:
134 overwrite -- if the output file already exists, should it
135 be overwritten? (default is False)
136
137 Methods:
138 write(molecule)
139 close()
140 """
141 - def __init__(self, format, filename, overwrite=False):
142 self.format = format.lower()
143 self.filename = filename
144 if not overwrite and os.path.isfile(self.filename):
145 raise IOError("%s already exists. Use 'overwrite=True' to overwrite it." % self.filename)
146 if not format in outformats:
147 raise ValueError("%s is not a recognised Webel format" % format)
148 self.file = open(filename, "w")
149
150 - def write(self, molecule):
151 """Write a molecule to the output file.
152
153 Required parameters:
154 molecule
155 """
156 if self.file.closed:
157 raise IOError("Outputfile instance is closed.")
158 output = molecule.write(self.format)
159 print >> self.file, output
160
162 """Close the Outputfile to further writing."""
163 self.file.close()
164
166 """Represent a Webel Molecule.
167
168 Required parameter:
169 smiles -- a SMILES string or any type of cinfony Molecule
170
171 Attributes:
172 formula, molwt, title
173
174 Methods:
175 calcfp(), calcdesc(), draw(), write()
176
177 The underlying SMILES string can be accessed using the attribute:
178 smiles
179 """
180 _cinfony = True
181
183
184 if hasattr(smiles, "_cinfony"):
185 a, b = smiles._exchange
186 if a == 0:
187 smiles = b
188 else:
189
190 smiles = smiles.write("smi").split()[0]
191
192 self.smiles = smiles
193 self.title = ""
194
195 @property
197 @property
198 - def molwt(self): return float(rajweb("mw", _quo(self.smiles)))
199 @property
201 return (0, self.smiles)
202
204 """Calculate descriptor values.
205
206 Optional parameter:
207 descnames -- a list of names of descriptors
208
209 If descnames is not specified, all available descriptors are
210 calculated. See the descs variable for a list of available
211 descriptors.
212 """
213 if not descnames:
214 descnames = getdescs()
215 else:
216 for descname in descnames:
217 if descname not in getdescs():
218 raise ValueError("%s is not a recognised Webel descriptor type" % descname)
219 ans = {}
220 p = re.compile("""Descriptor parent="(\w*)" name="([\w\-\+\d]*)" value="([\d\.]*)""")
221 for descname in descnames:
222 longname = "org.openscience.cdk.qsar.descriptors.molecular." + descname
223 response = rajweb("descriptor", longname, _quo(self.smiles))
224 for match in p.findall(response):
225 if match[2]:
226 ans["%s_%s" % (match[0], match[1])] = float(match[2])
227 return ans
228
229 - def calcfp(self, fptype="std"):
230 """Calculate a molecular fingerprint.
231
232 Optional parameters:
233 fptype -- the fingerprint type (default is "std"). See the
234 fps variable for a list of of available fingerprint
235 types.
236 """
237 fptype = fptype.lower()
238 if fptype not in fps:
239 raise ValueError("%s is not a recognised Webel Fingerprint type" % fptype)
240 fp = rajweb("fingerprint/%s/%s" % (fptype, _quo(self.smiles))).rstrip()
241 return Fingerprint(fp)
242
243 - def write(self, format="smi", filename=None, overwrite=False):
244 """Write the molecule to a file or return a string.
245
246 Optional parameters:
247 format -- see the informats variable for a list of available
248 output formats (default is "smi")
249 filename -- default is None
250 overwite -- if the output file already exists, should it
251 be overwritten? (default is False)
252
253 If a filename is specified, the result is written to a file.
254 Otherwise, a string is returned containing the result.
255
256 To write multiple molecules to the same file you should use
257 the Outputfile class.
258 """
259 format = format.lower()
260 if not format in outformats:
261 raise ValueError("%s is not a recognised Webel format" % format)
262 if format == "smi":
263 output = self.smiles
264 elif format == "names":
265 try:
266 output = nci(_quo(self.smiles), "%s" % format).rstrip().split("\n")
267 except urllib2.URLError, e:
268 if e.code == 404:
269 output = []
270 elif format in ['inchi', 'inchikey']:
271 format = "std" + format
272 output = nci(_quo(self.smiles), "%s" % format).rstrip()
273 elif format == 'iupac':
274 format = format + "_name"
275 try:
276 output = nci(_quo(self.smiles), "%s" % format).rstrip()
277 except urllib2.URLError, e:
278 if e.code == 404:
279 output = ""
280 else:
281 output = nci(_quo(self.smiles), "file?format=%s" % format).rstrip()
282
283 if filename:
284 if not overwrite and os.path.isfile(filename):
285 raise IOError("%s already exists. Use 'overwrite=True' to overwrite it." % filename)
286 outputfile = open(filename, "w")
287 print >> outputfile, output
288 outputfile.close()
289 else:
290 return output
291
294
295 - def draw(self, show=True, filename=None):
296 """Create a 2D depiction of the molecule.
297
298 Optional parameters:
299 show -- display on screen (default is True)
300 filename -- write to file (default is None)
301
302 Tkinter and Python Imaging Library are required for
303 image display.
304 """
305 imagedata = nci(_quo(self.smiles), "image")
306 if filename:
307 print >> open(filename, "wb"), imagedata
308 if show:
309 if not tk:
310 errormessage = ("Tkinter or Python Imaging "
311 "Library not found, but is required for image "
312 "display. See installation instructions for "
313 "more information.")
314 raise ImportError, errormessage
315 root = tk.Tk()
316 root.title(self.smiles)
317 frame = tk.Frame(root, colormap="new", visual='truecolor').pack()
318 image = PIL.open(StringIO.StringIO(imagedata))
319 imagedata = piltk.PhotoImage(image)
320 label = tk.Label(frame, image=imagedata).pack()
321 quitbutton = tk.Button(root, text="Close", command=root.destroy).pack(fill=tk.X)
322 root.mainloop()
323
325 """A Molecular Fingerprint.
326
327 Required parameters:
328 fingerprint -- a string of 0's and 1's representing a binary fingerprint
329
330 Attributes:
331 fp -- the underlying fingerprint object
332 bits -- a list of bits set in the Fingerprint
333
334 Methods:
335 The "|" operator can be used to calculate the Tanimoto coeff. For example,
336 given two Fingerprints 'a', and 'b', the Tanimoto coefficient is given by:
337 tanimoto = a | b
338 """
340 self.fp = fingerprint
342 mybits = set(self.bits)
343 otherbits = set(other.bits)
344 return len(mybits&otherbits) / float(len(mybits|otherbits))
345 @property
347 return [i for i,x in enumerate(self.fp) if x=="1"]
350
352 """A Smarts Pattern Matcher
353
354 Required parameters:
355 smartspattern
356
357 Methods:
358 match(molecule)
359
360 Example:
361 >>> mol = readstring("smi","CCN(CC)CC") # triethylamine
362 >>> smarts = Smarts("[#6][#6]") # Matches an ethyl group
363 >>> smarts.match(mol)
364 True
365 """
367 """Initialise with a SMARTS pattern."""
368 self.pat = smartspattern
369 - def match(self, molecule):
370 """Does a SMARTS pattern match a particular molecule?
371
372 Required parameters:
373 molecule
374 """
375 resp = rajweb("substruct", _quo(molecule.smiles), _quo(self.pat)).rstrip()
376 return resp == "true"
377
378 if __name__=="__main__":
379 import doctest
380 doctest.run_docstring_examples(rajweb, globals())
381