pax_global_header00006660000000000000000000000064126426016330014515gustar00rootroot0000000000000052 comment=fee2bbbb08eb39bbbe003f835d64e8c0c1688904 CIRpy-1.0.2/000077500000000000000000000000001264260163300125035ustar00rootroot00000000000000CIRpy-1.0.2/.bumpversion.cfg000066400000000000000000000003241264260163300156120ustar00rootroot00000000000000[bumpversion] current_version = 1.0.2 commit = True tag = True [bumpversion:file:setup.py] [bumpversion:file:cirpy.py] [bumpversion:file:docs/source/guide/install.rst] [bumpversion:file:docs/source/conf.py] CIRpy-1.0.2/.travis.yml000066400000000000000000000005321264260163300146140ustar00rootroot00000000000000language: python python: - "2.7" - "3.3" - "3.4" - "3.5" env: global: - CIRPY_TEST_DELAY=5 matrix: - OPTIONAL_DEPS=true - OPTIONAL_DEPS=false install: - pip install coveralls - if [ "$OPTIONAL_DEPS" = true ]; then pip install lxml; fi script: - coverage run --source=cirpy setup.py test after_success: - coveralls CIRpy-1.0.2/CONTRIBUTING.rst000066400000000000000000000037741264260163300151570ustar00rootroot00000000000000Contributing ============ .. sectionauthor:: Matt Swain Contributions of any kind are greatly appreciated! Feedback -------- The `Issue Tracker`_ is the best place to post any feature ideas, requests and bug reports. Contributing ------------ If you are able to contribute changes yourself, just fork the `source code`_ on GitHub, make changes and file a pull request. All contributions are welcome, no matter how big or small. Quick guide to contributing ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. `Fork the CIRpy repository on GitHub`_, then clone your fork to your local machine:: git clone https://github.com//CIRpy.git 2. Install the development requirements:: cd cirpy pip install -r requirements/development.txt 3. Create a new branch for your changes:: git checkout -b 4. Make your changes or additions. Ideally add some tests and ensure they pass. 5. Commit your changes and push to your fork on GitHub:: git add . git commit -m "" git push origin 4. `Submit a pull request`_. Tips ~~~~ - Follow the `PEP8`_ style guide. - Include docstrings as described in `PEP257`_. - Try and include tests that cover your changes. - Try to write `good commit messages`_. - Consider `squashing your commits`_ with rebase. - Read the GitHub help page on `Using pull requests`_. .. _`Issue Tracker`: https://github.com/mcs07/CIRpy/issues .. _`source code`: https://github.com/mcs07/CIRpy .. _`Fork the CIRpy repository on GitHub`: https://github.com/mcs07/CIRpy/fork .. _`Submit a pull request`: https://github.com/mcs07/CIRpy/compare/ .. _`squashing your commits`: http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html .. _`PEP8`: https://www.python.org/dev/peps/pep-0008 .. _`PEP257`: https://www.python.org/dev/peps/pep-0257 .. _`good commit messages`: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html .. _`Using pull requests`: https://help.github.com/articles/using-pull-requests CIRpy-1.0.2/LICENSE000066400000000000000000000021001264260163300135010ustar00rootroot00000000000000The MIT License Copyright (c) 2013 Matt Swain Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CIRpy-1.0.2/MANIFEST.in000066400000000000000000000002101264260163300142320ustar00rootroot00000000000000include README.rst include LICENSE include cirpy_test.py recursive-include docs * recursive-include requirements *.txt prune docs/build CIRpy-1.0.2/README.rst000066400000000000000000000041201264260163300141670ustar00rootroot00000000000000CIRpy ===== .. image:: http://img.shields.io/pypi/v/CIRpy.svg?style=flat :target: https://pypi.python.org/pypi/CIRpy .. image:: http://img.shields.io/pypi/l/CIRpy.svg?style=flat :target: https://github.com/mcs07/CIRpy/blob/master/LICENSE .. image:: http://img.shields.io/travis/mcs07/CIRpy/master.svg?style=flat :target: https://travis-ci.org/mcs07/CIRpy .. image:: http://img.shields.io/coveralls/mcs07/CIRpy/master.svg?style=flat :target: https://coveralls.io/r/mcs07/CIRpy?branch=master Introduction ------------ **CIRpy** is a Python interface for the `Chemical Identifier Resolver (CIR)`_ by the CADD Group at the NCI/NIH. CIR is a web service that will resolve any chemical identifier to another chemical representation. For example, you can pass it a chemical name and and request the corresponding SMILES string:: >>> import cirpy >>> cirpy.resolve('Aspirin', 'smiles') 'C1=CC=CC(=C1C(O)=O)OC(C)=O' CIRpy makes interacting with CIR through Python easy. There's no need to construct url requests and parse XML responses — CIRpy does all this for you. Installation ------------ Install CIRpy using:: pip install cirpy Alternatively, try one of the other `installation options`_. Documentation ------------- Full documentation is available at http://cirpy.readthedocs.org. Contribute ---------- - Feature ideas and bug reports are welcome on the `Issue Tracker`_. - Fork the `source code`_ on GitHub, make changes and file a pull request. Acknowledgements ---------------- All of CIRpy's functionality relies on the fantastic `CIR web service`_ created by the CADD Group at the NCI/NIH. License ------- CIRpy is licensed under the `MIT license`_. .. _`Chemical Identifier Resolver (CIR)`: http://cactus.nci.nih.gov/chemical/structure .. _`installation options`: http://cirpy.readthedocs.org/en/latest/guide/install.html .. _`CIR web service`: http://cactus.nci.nih.gov/chemical/structure .. _`source code`: https://github.com/mcs07/CIRpy .. _`Issue Tracker`: https://github.com/mcs07/CIRpy/issues .. _`MIT license`: https://github.com/mcs07/CIRpy/blob/master/LICENSE CIRpy-1.0.2/cirpy.py000066400000000000000000000416711264260163300142140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ CIRpy Python interface for the Chemical Identifier Resolver (CIR) by the CADD Group at the NCI/NIH. https://github.com/mcs07/CIRpy """ from __future__ import print_function from __future__ import unicode_literals from __future__ import division import functools import inspect import logging import os try: from urllib.error import HTTPError from urllib.parse import quote, urlencode from urllib.request import urlopen except ImportError: from urllib import urlencode from urllib2 import quote, urlopen, HTTPError try: from lxml import etree except ImportError: try: import xml.etree.cElementTree as etree except ImportError: import xml.etree.ElementTree as etree __author__ = 'Matt Swain' __email__ = 'm.swain@me.com' __version__ = '1.0.2' __license__ = 'MIT' log = logging.getLogger('cirpy') log.addHandler(logging.NullHandler()) API_BASE = 'https://cactus.nci.nih.gov/chemical/structure' FILE_FORMATS = { 'alc', 'cdxml', 'cerius', 'charmm', 'cif', 'cml', 'ctx', 'gjf', 'gromacs', 'hyperchem', 'jme', 'maestro', 'mol', 'mol2', 'mrv', 'pdb', 'sdf3000', 'sln', 'xyz' } def construct_api_url(input, representation, resolvers=None, get3d=False, tautomers=False, xml=True, **kwargs): """Return the URL for the desired API endpoint. :param string input: Chemical identifier to resolve :param string representation: Desired output representation :param list(str) resolvers: (Optional) Ordered list of resolvers to use :param bool get3d: (Optional) Whether to return 3D coordinates (where applicable) :param bool tautomers: (Optional) Whether to return all tautomers :param bool xml: (Optional) Whether to return full XML response :returns: CIR API URL :rtype: str """ # File formats require representation=file and the format in the querystring if representation in FILE_FORMATS: kwargs['format'] = representation representation = 'file' # Prepend input with 'tautomers:' to return all tautomers if tautomers: input = 'tautomers:%s' % input url = '%s/%s/%s' % (API_BASE, quote(input), representation) if xml: url += '/xml' if resolvers: kwargs['resolver'] = ','.join(resolvers) if get3d: kwargs['get3d'] = True if kwargs: url += '?%s' % urlencode(kwargs) return url def request(input, representation, resolvers=None, get3d=False, tautomers=False, **kwargs): """Make a request to CIR and return the XML response. :param string input: Chemical identifier to resolve :param string representation: Desired output representation :param list(string) resolvers: (Optional) Ordered list of resolvers to use :param bool get3d: (Optional) Whether to return 3D coordinates (where applicable) :param bool tautomers: (Optional) Whether to return all tautomers :returns: XML response from CIR :rtype: Element :raises HTTPError: if CIR returns an error code :raises ParseError: if CIR response is uninterpretable """ url = construct_api_url(input, representation, resolvers, get3d, tautomers, **kwargs) log.debug('Making request: %s', url) response = urlopen(url) return etree.parse(response).getroot() class Result(object): """A single result returned by CIR.""" def __init__(self, input, notation, input_format, resolver, representation, value): """ :param string input: Originally supplied input identifier that produced this result :param string notation: Identifier matched by the resolver or tautomer ID :param string input_format: Format of the input as interpreted by the resolver :param string resolver: Resolver used to produce this result :param string representation: Requested output representation :param value: Actual result value :type value: string or list(string) """ self.input = input self.representation = representation self.resolver = resolver self.input_format = input_format self.notation = notation self.value = value def __repr__(self): return 'Result(input=%r, representation=%r, resolver=%r, input_format=%r, notation=%r, value=%r)' \ % (self.input, self.representation, self.resolver, self.input_format, self.notation, self.value) def __str__(self): return self.value def __eq__(self, other): return isinstance(other, type(self)) and self.__dict__ == other.__dict__ def __getitem__(self, prop): """Allow dict-style access to attributes to ease transition from when results were dicts.""" if prop in self.__dict__: return getattr(self, prop) raise KeyError(prop) def __setitem__(self, prop, val): """Allow dict-style setting of attributes to ease transition from when results were dicts.""" setattr(self, prop, val) def __contains__(self, prop): """Allow dict-style checking of attributes to ease transition from when results were dicts.""" return prop in self.__dict__ def to_dict(self): """Return a dictionary containing Result data.""" return self.__dict__ def query(input, representation, resolvers=None, get3d=False, tautomers=False, **kwargs): """Get all results for resolving input to the specified output representation. :param string input: Chemical identifier to resolve :param string representation: Desired output representation :param list(string) resolvers: (Optional) Ordered list of resolvers to use :param bool get3d: (Optional) Whether to return 3D coordinates (where applicable) :param bool tautomers: (Optional) Whether to return all tautomers :returns: List of resolved results :rtype: list(Result) :raises HTTPError: if CIR returns an error code :raises ParseError: if CIR response is uninterpretable """ tree = request(input, representation, resolvers, get3d, tautomers, **kwargs) results = [] for data in tree.findall('.//data'): value = [item.text for item in data.findall('item')] result = Result( input=tree.attrib['string'], representation=tree.attrib['representation'], resolver=data.attrib['resolver'], input_format=data.attrib['string_class'], notation=data.attrib['notation'], value=value[0] if len(value) == 1 else value ) results.append(result) log.debug('Received %s query results', len(results)) return results def resolve(input, representation, resolvers=None, get3d=False, **kwargs): """Resolve input to the specified output representation. :param string input: Chemical identifier to resolve :param string representation: Desired output representation :param list(string) resolvers: (Optional) Ordered list of resolvers to use :param bool get3d: (Optional) Whether to return 3D coordinates (where applicable) :returns: Output representation or None :rtype: string or None :raises HTTPError: if CIR returns an error code :raises ParseError: if CIR response is uninterpretable """ # Take first result from XML query results = query(input, representation, resolvers, False, get3d, **kwargs) result = results[0].value if results else None return result def resolve_image(input, resolvers=None, fmt='png', width=300, height=300, frame=False, crop=None, bgcolor=None, atomcolor=None, hcolor=None, bondcolor=None, framecolor=None, symbolfontsize=11, linewidth=2, hsymbol='special', csymbol='special', stereolabels=False, stereowedges=True, header=None, footer=None, **kwargs): """Resolve input to a 2D image depiction. :param string input: Chemical identifier to resolve :param list(string) resolvers: (Optional) Ordered list of resolvers to use :param string fmt: (Optional) gif or png image format (default png) :param int width: (Optional) Image width in pixels (default 300) :param int height: (Optional) Image height in pixels (default 300) :param bool frame: (Optional) Whether to show border frame (default False) :param int crop: (Optional) Crop image with specified padding :param int symbolfontsize: (Optional) Atom label font size (default 11) :param int linewidth: (Optional) Bond line width (default 2) :param string bgcolor: (Optional) Background color :param string atomcolor: (Optional) Atom label color :param string hcolor: (Optional) Hydrogen atom label color :param string bondcolor: (Optional) Bond color :param string framecolor: (Optional) Border frame color :param bool hsymbol: (Optional) Hydrogens: all, special or none (default special) :param bool csymbol: (Optional) Carbons: all, special or none (default special) :param bool stereolabels: (Optional) Whether to show stereochemistry labels (default False) :param bool stereowedges: (Optional) Whether to show wedge/dash bonds (default True) :param string header: (Optional) Header text above structure :param string footer: (Optional) Footer text below structure """ # Aggregate all arguments into kwargs args, _, _, values = inspect.getargvalues(inspect.currentframe()) for arg in args: if values[arg] is not None: kwargs[arg] = values[arg] # Turn off anti-aliasing for transparent background if kwargs.get('bgcolor') == 'transparent': kwargs['antialiasing'] = False # Renamed parameters if 'stereolabels' in kwargs: kwargs['showstereo'] = kwargs.pop('stereolabels') if 'fmt' in kwargs: kwargs['format'] = kwargs.pop('fmt') # Toggle stereo wedges if 'stereowedges' in kwargs: status = kwargs.pop('stereowedges') kwargs.update({'wedges': status, 'dashes': status}) # Constant values kwargs.update({'representation': 'image', 'xml': False}) url = construct_api_url(**kwargs) log.debug('Making image request: %s', url) response = urlopen(url) return response.read() # TODO: Support twirl as fmt paramter? # TODO: ipython html repr twirl, ipython png repr image def download(input, filename, representation, overwrite=False, resolvers=None, get3d=False, **kwargs): """Convenience function to save a CIR response as a file. This is just a simple wrapper around the resolve function. :param string input: Chemical identifier to resolve :param string filename: File path to save to :param string representation: Desired output representation :param bool overwrite: (Optional) Whether to allow overwriting of an existing file :param list(string) resolvers: (Optional) Ordered list of resolvers to use :param bool get3d: (Optional) Whether to return 3D coordinates (where applicable) :raises HTTPError: if CIR returns an error code :raises ParseError: if CIR response is uninterpretable :raises IOError: if overwrite is False and file already exists """ result = resolve(input, representation, resolvers, get3d, **kwargs) # Just log and return if nothing resolved if not result: log.debug('No file to download.') return # Only overwrite an existing file if explicitly instructed to. if not overwrite and os.path.isfile(filename): raise IOError("%s already exists. Use 'overwrite=True' to overwrite it." % filename) # Ensure file ends with a newline if not result.endswith('\n'): result += '\n' with open(filename, 'w') as f: f.write(result) def memoized_property(fget): """Decorator to create memoized properties.""" attr_name = '_{0}'.format(fget.__name__) @functools.wraps(fget) def fget_memoized(self): if not hasattr(self, attr_name): setattr(self, attr_name, fget(self)) return getattr(self, attr_name) return property(fget_memoized) class Molecule(object): """Class to hold and cache the structure information for a given CIR input.""" def __init__(self, input, resolvers=None, get3d=False, **kwargs): """Initialize with a resolver input.""" self.input = input self.resolvers = resolvers self.get3d = get3d self.kwargs = kwargs log.debug('Instantiated Molecule: %s' % self) def __repr__(self): return 'Molecule(input=%r, resolvers=%r, get3d=%r, kwargs=%r)' \ % (self.input, self.resolvers, self.get3d, self.kwargs) @memoized_property def stdinchi(self): """Standard InChI.""" return resolve(self.input, 'stdinchi', self.resolvers, **self.kwargs) @memoized_property def stdinchikey(self): """Standard InChIKey.""" return resolve(self.input, 'stdinchikey', self.resolvers, **self.kwargs) @memoized_property def inchi(self): """Non-standard InChI. (Uses options DONOTADDH W0 FIXEDH RECMET NEWPS SPXYZ SAsXYZ Fb Fnud).""" return resolve(self.input, 'inchi', self.resolvers, **self.kwargs) @memoized_property def smiles(self): """SMILES string.""" return resolve(self.input, 'smiles', self.resolvers, **self.kwargs) @memoized_property def ficts(self): """FICTS NCI/CADD hashed structure identifier.""" return resolve(self.input, 'ficts', self.resolvers, **self.kwargs) @memoized_property def ficus(self): """FICuS NCI/CADD hashed structure identifier.""" return resolve(self.input, 'ficus', self.resolvers, **self.kwargs) @memoized_property def uuuuu(self): """uuuuu NCI/CADD hashed structure identifier.""" return resolve(self.input, 'uuuuu', self.resolvers, **self.kwargs) @memoized_property def hashisy(self): """CACTVS HASHISY identifier.""" return resolve(self.input, 'hashisy', self.resolvers, **self.kwargs) @memoized_property def sdf(self): """SDF file.""" return resolve(self.input, 'sdf', self.resolvers, **self.kwargs) @memoized_property def names(self): """List of chemical names.""" return resolve(self.input, 'names', self.resolvers, **self.kwargs) @memoized_property def iupac_name(self): """IUPAC approved name.""" return resolve(self.input, 'iupac_name', self.resolvers, **self.kwargs) @memoized_property def cas(self): """CAS registry numbers.""" return resolve(self.input, 'cas', self.resolvers, **self.kwargs) @memoized_property def mw(self): """Molecular weight.""" return resolve(self.input, 'mw', self.resolvers, **self.kwargs) @memoized_property def formula(self): """Molecular formula""" return resolve(self.input, 'formula', self.resolvers, **self.kwargs) @memoized_property def h_bond_donor_count(self): """Hydrogen bond donor count.""" return resolve(self.input, 'h_bond_donor_count', self.resolvers, **self.kwargs) @memoized_property def h_bond_acceptor_count(self): """Hydrogen bond acceptor count.""" return resolve(self.input, 'h_bond_acceptor_count', self.resolvers, **self.kwargs) @memoized_property def h_bond_center_count(self): """Hydrogen bond center count.""" return resolve(self.input, 'h_bond_center_count', self.resolvers, **self.kwargs) @memoized_property def rule_of_5_violation_count(self): """Rule of 5 violation count.""" return resolve(self.input, 'rule_of_5_violation_count', self.resolvers, **self.kwargs) @memoized_property def rotor_count(self): """Rotor count.""" return resolve(self.input, 'rotor_count', self.resolvers, **self.kwargs) @memoized_property def effective_rotor_count(self): """Effective rotor count.""" return resolve(self.input, 'effective_rotor_count', self.resolvers, **self.kwargs) @memoized_property def ring_count(self): """Ring count.""" return resolve(self.input, 'ring_count', self.resolvers, **self.kwargs) @memoized_property def ringsys_count(self): """Ring system count.""" return resolve(self.input, 'ringsys_count', self.resolvers, **self.kwargs) @memoized_property def image(self): """2D image depiction.""" return resolve_image(self.input, self.resolvers, **self.kwargs) @property def image_url(self): """URL of a GIF image.""" return construct_api_url(self.input, 'image', self.resolvers, False, self.get3d, False, **self.kwargs) @property def twirl_url(self): """Url of a TwirlyMol 3D viewer.""" return construct_api_url(self.input, 'twirl', self.resolvers, False, self.get3d, False, **self.kwargs) def download(self, filename, representation, overwrite=False): """Download the resolved structure as a file. :param string filename: File path to save to :param string representation: Desired output representation :param bool overwrite: (Optional) Whether to allow overwriting of an existing file """ download(self.input, filename, representation, overwrite, self.resolvers, self.get3d, **self.kwargs) CIRpy-1.0.2/cirpy_test.py000066400000000000000000000170751264260163300152540ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Unit tests for cirpy.py Python interface for the Chemical Identifier Resolver (CIR) by the CADD Group at the NCI/NIH. https://github.com/mcs07/CIRpy """ from __future__ import print_function from __future__ import unicode_literals from __future__ import division import logging import os import time import unittest try: from urllib.error import HTTPError except ImportError: from urllib2 import HTTPError try: from lxml import etree except ImportError: try: import xml.etree.cElementTree as etree except ImportError: import xml.etree.ElementTree as etree from cirpy import request, resolve, query, Molecule, Result, resolve_image logging.basicConfig(level=logging.DEBUG) class RateLimitTestCase(unittest.TestCase): """TestCase that delays before each test according to CIRPY_TEST_DELAY environment variable.""" def setUp(self): time.sleep(float(os.environ.get('CIRPY_TEST_DELAY', 0))) class TestRequest(RateLimitTestCase): """Test basic requests to CIR servers return the expected XML response.""" def test_requests(self): """Test a variety of basic requests to ensure they return the expected XML response.""" self.assertEqual(request('c1ccccc1', 'names').tag, 'request') self.assertEqual(request('Aspirin', 'smiles').tag, 'request') self.assertEqual(len(request('64-17-5', 'stdinchi')), 1) def test_no_result_request(self): """Test that an empty XML response is returned when there are no results.""" response = request('arguergbaiurg', 'smiles') self.assertEqual(response.tag, 'request') self.assertEqual(len(response), 0) def test_invalid_representation_request(self): """Test that HTTPError is raised when an invalid representation is specified.""" with self.assertRaises(HTTPError): request('Morphine', 'ogiuewrgpw') class TestQuery(RateLimitTestCase): """Test the query function returns expected results.""" def test_morphine_inchi(self): """Test morphine query for inchi returns expected result.""" results = query('morphine', 'inchi') self.assertEqual(len(results), 2) self.assertEqual(results[1].input, 'morphine') self.assertEqual(results[1].representation, 'inchi') self.assertEqual(results[1].resolver, 'name_by_cir') self.assertEqual(results[1].input_format, 'chemical name (CIR)') self.assertEqual(results[1].notation, 'Morphine') self.assertEqual(results[1].value, 'InChI=1/C17H19NO3/c1-18-7-6-17-10-3-5-13(20)16(17)21-15-12(19)4-2-9(14(15)17)8-11(10)18/h2-5,10-11,13,16,19-20H,6-8H2,1H3/t10-,11+,13?,16-,17-/m0/s1') def test_query_dict(self): """Test dict-style access to result attributes.""" results = query('Morphine', 'inchi') self.assertEqual(len(results), 2) self.assertEqual(results[1]['value'], 'InChI=1/C17H19NO3/c1-18-7-6-17-10-3-5-13(20)16(17)21-15-12(19)4-2-9(14(15)17)8-11(10)18/h2-5,10-11,13,16,19-20H,6-8H2,1H3/t10-,11+,13?,16-,17-/m0/s1') self.assertEqual(results[1]['notation'], 'Morphine') self.assertEqual(results[1]['resolver'], 'name_by_cir') def test_no_result_query(self): """Test that an empty list is returned when there are no results.""" self.assertEqual(query('sjkvhaldfu', 'smiles'), []) def test_invalid_representation_query(self): """Test that HTTPError is raised when an invalid representation is specified.""" with self.assertRaises(HTTPError): query('Morphine', 'ogiuewrgpw') def test_custom_resolvers(self): """Test expected results are returned when using custom name resolvers.""" results = query('2,4,6-trinitrotoluene', 'smiles') self.assertEqual(len(results), 2) self.assertEqual(results[0], Result(input='2,4,6-trinitrotoluene', representation='smiles', resolver='name_by_opsin', input_format='IUPAC name (OPSIN)', notation='2,4,6-trinitrotoluene', value='Cc1c(cc(cc1[N+]([O-])=O)[N+]([O-])=O)[N+]([O-])=O')) self.assertEqual(results[1], Result(input='2,4,6-trinitrotoluene', representation='smiles', resolver='name_by_cir', input_format='chemical name (CIR)', notation='2,4,6-Trinitrotoluene', value='Cc1c(cc(cc1[N+]([O-])=O)[N+]([O-])=O)[N+]([O-])=O')) def test_result_equality(self): """Test that identical result objects are considered equal.""" r1 = Result('input', 'notation', 'input_format', 'resolver', 'representation', 'value') r2 = Result('input', 'notation', 'input_format', 'resolver', 'representation', 'value') r3 = Result('input', 'notation', 'input_format', 'resolver', 'representation', 'another_value') self.assertEqual(r1, r2) self.assertNotEqual(r1, r3) class TestResolve(RateLimitTestCase): """Test the resolve function.""" def test_alanine_smiles(self): """Test that alanine smiles resolves the expected result.""" self.assertEqual(resolve('Alanine', 'smiles'), 'C[C@H](N)C(O)=O') def test_no_results_resolve(self): """Test that None is returned when there are no results.""" self.assertEqual(resolve('aruighaelirugaerg', 'inchi'), None) def test_invalid_representation_resolve(self): """Test that HTTPError is raised when an invalid representation is specified.""" with self.assertRaises(HTTPError): resolve('Morphine', 'ogiuewrgpw') def test_tnt_smiles(self): """Test that TNT smiles resolves the expected result.""" self.assertEqual( resolve('2,4,6-trinitrotoluene', 'smiles'), 'Cc1c(cc(cc1[N+]([O-])=O)[N+]([O-])=O)[N+]([O-])=O' ) def test_tnt_smiles_custom_resolvers(self): """Test custom resolvers return the expected result.""" self.assertEqual( resolve('2,4,6-trinitrotoluene', 'smiles', ['name_by_opsin', 'name_by_cir']), 'Cc1c(cc(cc1[N+]([O-])=O)[N+]([O-])=O)[N+]([O-])=O' ) self.assertEqual( resolve('2,4,6-trinitrotoluene', 'smiles', ['name_by_cir', 'name_by_opsin']), 'Cc1c(cc(cc1[N+]([O-])=O)[N+]([O-])=O)[N+]([O-])=O' ) class TestMolecule(RateLimitTestCase): """Test the Molecule class.""" def test_molecule_image(self): """Test Molecule image_url attribute.""" self.assertEqual( Molecule('C#N', ['smiles']).image_url, 'https://cactus.nci.nih.gov/chemical/structure/C%23N/image?resolver=smiles' ) class TestFiles(RateLimitTestCase): """Test resolving to file formats.""" def test_cml(self): """Test CML file format is resolved.""" cmlstring = resolve('Aspirin', 'cml') cml = etree.fromstring(cmlstring) self.assertEqual(cml.tag, '{http://www.xml-cml.org/schema/cml2/core}list') self.assertEqual(len(cml.findall('.//{http://www.xml-cml.org/schema/cml2/core}molecule')), 1) def test_pdb(self): """Test PDB file format is resolved.""" result = resolve('Aspirin', 'pdb') self.assertIn('HEADER', result) self.assertIn('ATOM', result) self.assertIn('CONECT', result) class TestImage(RateLimitTestCase): """Test resolving to image depiction.""" def test_png_format(self): """Test that response looks like valid PNG data.""" img = resolve_image('Glucose') self.assertEqual(img[:8], b'\x89PNG\x0d\x0a\x1a\x0a') def test_gif_format(self): """Test that response looks like valid GIF data.""" img = resolve_image('Glucose', fmt='gif') self.assertEqual(img[:4], b'GIF8') if __name__ == '__main__': unittest.main() CIRpy-1.0.2/docs/000077500000000000000000000000001264260163300134335ustar00rootroot00000000000000CIRpy-1.0.2/docs/Makefile000066400000000000000000000151571264260163300151040ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CIRpy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CIRpy.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/CIRpy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CIRpy" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." CIRpy-1.0.2/docs/README.rst000066400000000000000000000016721264260163300151300ustar00rootroot00000000000000CIRpy Documentation =================== This file provides a quick guide on how to compile the CIRpy documentation. You will find all the documentation source files in the ``docs/source`` directory, written in reStructuredText format. All generated documentation is saved to the ``docs/build`` directory. Requirements ------------ Sphinx is required to compile the documentation. Sphinx also requires docutils and jinja. Install them all using:: pip install Sphinx Compile the documentation ------------------------- To compile the documentation and produce HTML output, run the following command from this ``docs`` directory:: make html Documentation will be generated in HTML format and saved to the ``build/html`` directory. Open the ``index.html`` file in a browser to view it. Reset ----- To clear all generated documentation files and start over from scratch, run:: make clean This will not delete any of the source files. CIRpy-1.0.2/docs/source/000077500000000000000000000000001264260163300147335ustar00rootroot00000000000000CIRpy-1.0.2/docs/source/api.rst000066400000000000000000000011261264260163300162360ustar00rootroot00000000000000.. _api: API documentation ================= .. sectionauthor:: Matt Swain .. module:: cirpy This part of the documentation is automatically generated from the CIRpy source code and comments. Resolve ------- .. autofunction:: resolve Query ----- .. autofunction:: query Result ------ .. autoclass:: Result :members: Images ------ .. autofunction:: resolve_image Request ------- .. autofunction:: request Download -------- .. autofunction:: download API URLs -------- .. autofunction:: construct_api_url Molecule -------- .. autoclass:: Molecule :members: CIRpy-1.0.2/docs/source/conf.py000066400000000000000000000250141264260163300162340ustar00rootroot00000000000000# -*- coding: utf-8 -*- # CIRpy documentation build configuration file, created by sphinx-quickstart on Tue Mar 24 16:12:38 2015. # This file is execfile()d with the current directory set to its containing dir. # Note that not all possible configuration values are present in this autogenerated file. # All configuration values have a default; values that are commented out serve to show the default. import sys import os # on_rtd is whether we are on readthedocs.org on_rtd = os.environ.get('READTHEDOCS', None) == 'True' # If extensions (or modules to document with autodoc) are in another directory, add these directories to sys.path here. # If the directory is relative to the documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions coming with Sphinx # (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'CIRpy' copyright = u'2015, Matt Swain' # The version info for the project you're documenting, acts as replacement for |version| and |release|, also used in # various other places throughout the built documents. # # The short X.Y version. version = '1.0.2' # The full version, including alpha/beta/rc tags. release = '1.0.2' # The language for content autogenerated by Sphinx. Refer to documentation for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and directories to ignore when looking for source # files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for a list of builtin themes. if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] #html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme further. For a list of options available # for each theme, see the documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the docs. This file should be a Windows icon # file (.ico) being 16x16 or 32x32 pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, relative to this directory. They are # copied after the builtin static files, so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or .htaccess) here, relative to this directory. # These files are copied directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will contain a tag referring to it. The # value of this option must be the base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'CIRpydoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). 'papersize': 'a4paper', # The font size ('10pt', '11pt' or '12pt'). 'pointsize': '12pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'CIRpy.tex', u'CIRpy Documentation', u'Matt Swain', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, not chapters. latex_use_parts = False # If true, show page references after internal links. latex_show_pagerefs = True # If true, show URL addresses after external links. latex_show_urls = True # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. latex_domain_indices = False # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples (source start file, name, description, authors, manual section). man_pages = [ ('index', 'cirpy', u'CIRpy Documentation', [u'Matt Swain'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, dir menu entry, description, category) texinfo_documents = [ ('index', 'CIRpy', u'CIRpy Documentation', u'Matt Swain', 'CIRpy', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = u'CIRpy' epub_author = u'Matt Swain' epub_publisher = u'Matt Swain' epub_copyright = u'2015, Matt Swain' # The basename for the epub file. It defaults to the project name. #epub_basename = u'CIRpy' # The HTML theme for the epub output. Since the default themes are not optimized for small screen space, using the same # theme for HTML and epub output is usually not wise. This defaults to 'epub', a theme designed to save visual space. #epub_theme = 'epub' # The language of the text. It defaults to the language option # or en if the language is not set. #epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. #epub_identifier = '' # A unique identification for the text. #epub_uid = '' # A tuple containing the cover image and cover page html template filenames. #epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. #epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_pre_files = [] # HTML files shat should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. #epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # The depth of the table of contents in toc.ncx. #epub_tocdepth = 3 # Allow duplicate toc entries. #epub_tocdup = True # Choose between 'default' and 'includehidden'. #epub_tocscope = 'default' # Fix unsupported image types using the PIL. #epub_fix_images = False # Scale large images. #epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. #epub_show_urls = 'inline' # If false, no index is generated. #epub_use_index = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'python': ('http://docs.python.org/', None)} # Sort autodoc members by the order they appear in the source code autodoc_member_order = 'bysource' # Concatenate the class and __init__ docstrings together autoclass_content = 'both' CIRpy-1.0.2/docs/source/guide/000077500000000000000000000000001264260163300160305ustar00rootroot00000000000000CIRpy-1.0.2/docs/source/guide/contributing.rst000066400000000000000000000000721264260163300212700ustar00rootroot00000000000000.. _contributing: .. include:: ../../../CONTRIBUTING.rst CIRpy-1.0.2/docs/source/guide/gettingstarted.rst000066400000000000000000000045611264260163300216200ustar00rootroot00000000000000.. _gettingstarted: Getting started =============== This page gives a introduction on how to get started with CIRpy. Before we start, make sure you have :ref:`installed CIRpy `. Basic usage ----------- The simplest way to use CIRpy is with the ``resolve`` function:: >>> import cirpy >>> cirpy.resolve('Aspirin', 'smiles') 'C1=CC=CC(=C1C(O)=O)OC(C)=O' The first parameter is the input string and the second parameter is the desired output representation. The main output representations for the second parameter are:: stdinchi stdinchikey inchi smiles ficts ficus uuuuu hashisy sdf names iupac_name cas formula All return a string, apart from ``names`` and ``cas``, which return a list of strings. File formats ------------ Output can additionally be returned in a variety of file formats that are specified using the second parameter in the same way:: >>> cirpy.resolve('c1ccccc1', 'cif') "data_C6H6\n#\n_chem_comp.id\t'C6H6'\n#\nloop_\n_chem_comp_atom.comp_id\n..." The full list of file formats:: alc # Alchemy format cdxml # CambridgeSoft ChemDraw XML format cerius # MSI Cerius II format charmm # Chemistry at HARvard Macromolecular Mechanics file format cif # Crystallographic Information File cml # Chemical Markup Language ctx # Gasteiger Clear Text format gjf # Gaussian input data file gromacs # GROMACS file format hyperchem # HyperChem file format jme # Java Molecule Editor format maestro # Schroedinger MacroModel structure file format mol # Symyx molecule file mol2 # Tripos Sybyl MOL2 format mrv # ChemAxon MRV format pdb # Protein Data Bank sdf3000 # Symyx Structure Data Format 3000 sln # SYBYL Line Notation xyz # xyz file format Properties ---------- A number of calculated structure-based properties can be returned, also specified using the second parameter:: >>> cirpy.resolve('coumarin 343', 'h_bond_acceptor_count') '5' The full list of properties:: mw # (Molecular weight) h_bond_donor_count h_bond_acceptor_count h_bond_center_count rule_of_5_violation_count rotor_count effective_rotor_count ring_count ringsys_count CIRpy-1.0.2/docs/source/guide/install.rst000066400000000000000000000030741264260163300202340ustar00rootroot00000000000000.. _install: Installation ============ CIRpy supports Python versions 2.7, 3.3, 3.4 and 3.5. There are no required dependencies. Option 1: Use pip (recommended) ------------------------------- The easiest and recommended way to install is using pip:: pip install cirpy This will download the latest version of CIRpy, and place it in your `site-packages` folder so it is automatically available to all your python scripts. If you don't already have pip installed, you can `install it using get-pip.py`_:: curl -O https://raw.github.com/pypa/pip/master/contrib/get-pip.py python get-pip.py Option 2: Download the latest release ------------------------------------- Alternatively, `download the latest release`_ manually and install yourself:: tar -xzvf CIRpy-1.0.2.tar.gz cd CIRpy-1.0.2 python setup.py install The setup.py command will install CIRpy in your `site-packages` folder so it is automatically available to all your python scripts. Option 3: Clone the repository ------------------------------ The latest development version of CIRpy is always `available on GitHub`_. This version is not guaranteed to be stable, but may include new features that have not yet been released. Simply clone the repository and install as usual:: git clone https://github.com/mcs07/CIRpy.git cd CIRpy python setup.py install .. _`install it using get-pip.py`: http://www.pip-installer.org/en/latest/installing.html .. _`download the latest release`: https://github.com/mcs07/CIRpy/releases/ .. _`available on GitHub`: https://github.com/mcs07/CIRpy CIRpy-1.0.2/docs/source/guide/misc.rst000066400000000000000000000054621264260163300175240ustar00rootroot00000000000000.. _misc: Miscellaneous ============= Tautomers --------- To get all possible resolved tautomers, use the ``tautomers`` parameter:: tautomers = query('warfarin', 'smiles', tautomers=True) The Molecule object ------------------- The Molecule class provides an easy way to collect and store various structure representations and properties for a given input:: from cirpy import Molecule mol = Molecule('N[C@@H](C)C(=O)O') ``mol`` then has the following properties:: mol.stdinchi mol.stdinchikey mol.smiles mol.ficts mol.ficus mol.uuuuu mol.hashisy mol.sdf mol.names mol.iupac_name mol.cas mol.image_url # The url of a GIF image mol.twirl_url # The url of a TwirlyMol 3D viewer mol.mw # Molecular weight mol.formula mol.h_bond_donor_count mol.h_bond_acceptor_count mol.h_bond_center_count mol.rule_of_5_violation_count mol.rotor_count mol.effective_rotor_count mol.ring_count mol.ringsys_count The first time you access each one of these properties, a request is made to the CIR servers. The result is cached, however, so subsequent access is much faster. Downloading files ----------------- A convenience function is provided to facilitate downloading the CIR output to a file:: cirpy.download('Aspirin', 'test.sdf', 'sdf') cirpy.download('Aspirin', 'test.sdf', 'sdf', overwrite=True) This works in the same way as the ``resolve`` function, but also accepts a filename. There is an optional ``overwrite`` parameter to specify whether any existing file should be overwritten. Constructing API URLs --------------------- Construct API URLs:: >>> cirpy.construct_api_url('Porphyrin', 'smiles') 'http://cactus.nci.nih.gov/chemical/structure/Porphyrin/smiles/xml' Logging ------- CIRpy can generate logging statements if required. Just set the desired logging level:: import logging logging.basicConfig(level=logging.DEBUG) The logger is named 'cirpy'. There is more information on logging in the `Python logging documentation`_. Pattern matching ---------------- .. note:: It looks like the ``name_pattern`` resolver no longer works. There is an additional ``name_pattern`` resolver that allows for Google-like searches. For example:: results = query('Morphine','smiles', ['name_pattern']) The ``notation`` attribute of each ``Result`` will show you the name of the match (e.g. "Morphine N-oxide", "Morphine Sulfate") and the ``value`` attribute will be the representation specified in the query (SMILES in the above example). `Read more about pattern matching on the CIR blog`_. .. _`Python logging documentation`: http://docs.python.org/2/howto/logging.html .. _`Read more about pattern matching on the CIR blog`: http://cactus.nci.nih.gov/blog/?p=1456 CIRpy-1.0.2/docs/source/guide/query.rst000066400000000000000000000025211264260163300177270ustar00rootroot00000000000000.. _query: Queries ======= The ``resolve`` function will only return the top match for a given input. However, sometimes multiple resolvers will match an input (e.g. the name resolvers), and individual resolvers can even return multiple results. The ``query`` function will return every result:: >>> cirpy.query('CCO', 'stdinchikey') [Result(resolver='smiles', value='InChIKey=LFQSCWFLJHTTHZ-UHFFFAOYSA-N'), Result(input='CCO', resolver='name_by_cir', value='InChIKey=BGDMJXZYDKFEGJ-UHFFFAOYSA-N')] As with the ``resolve`` function, it is possible to specify which resolvers are used:: >>> cirpy.query('2,4,6-trinitrotoluene', 'formula', ['name_by_opsin','name_by_cir']) [Result(resolver='name_by_opsin', value='C7H5N3O6'), Result(resolver='name_by_cir', value='C7H5N3O6')] Results ------- The ``query`` function results a list of ``Result`` objects. Each ``Result`` has a ``value`` attribute that corresponds to what the ``resolve`` function would return:: >>> results = cirpy.query('2,4,6-trinitrotoluene', 'formula') >>> results[0] Result(resolver='name_by_opsin', value='C7H5N3O6') >>> results[0].value 'C7H5N3O6' Each ``Result`` also has ``input``, ``representation``, ``resolver``, ``input_format`` and ``notation`` attributes. :ref:`See the full API documentation for information on these attributes `. CIRpy-1.0.2/docs/source/guide/resolvers.rst000066400000000000000000000041061264260163300206070ustar00rootroot00000000000000.. _resolvers: Resolvers ========= CIR interprets input strings using a series of "resolvers" in a specific order. Each one is tried in turn until one successfully interprets the input. The available resolvers are not well documented, but the ones that I can identify, roughly in the order that they are tried by default, are:: smiles stdinchikey stdinchi ncicadd_identifier # (for FICTS, FICuS, uuuuu) hashisy cas_number name_by_opsin name_by_cir Customizing resolvers --------------------- You can customize which resolvers are used (and the order they are used in), by supplying a list of resolvers as a third parameter to the ``resolve`` function: >>> cirpy.resolve('Aspirin', 'sdf', ['cas_number', 'name_by_cir', 'name_by_opsin']) 'C9H8O4\nAPtclcactv03241513052D 0 0.00000 0.00000\n \n 21 21...' >>> cirpy.resolve('C1=CC=CC(=C1C(O)=O)OC(C)=O', 'names', ['smiles', 'stdinchi']) ['2-acetyloxybenzoic acid', '2-Acetoxybenzoic acid', '50-78-2', ...] Manually specifying the resolvers can be useful when an ambiguous input identifier could be interpreted as multiple different formats, but you know which format it is. Resolving names --------------- By default, CIR resolves names first by using OPSIN, and if that fails, using a lookup in its own name index. With CIRpy you can customize which of these resolvers are used, and also specify the order of precedence. Just use the ``resolve`` function with a third parameter - a list containing any of the strings ``name_by_opsin``, ``name_by_cir`` in the order in which they should be tried:: >>> cirpy.resolve('Morphine', 'smiles', ['name_by_opsin']) 'CN1CC[C@]23[C@H]4Oc5c(O)ccc(C[C@@H]1[C@@H]2C=C[C@@H]4O)c35' >>> cirpy.resolve('Morphine', 'smiles', ['name_by_cir','name_by_opsin']) 'CN1CC[C@]23[C@H]4Oc5c(O)ccc(C[C@@H]1[C@@H]2C=CC4O)c35' `Read more about resolving names on the CIR blog`_. .. note:: The ``chemspider_id`` and ``name_by_chemspider`` resolvers no longer exist. .. _`Read more about resolving names on the CIR blog`: http://cactus.nci.nih.gov/blog/?p=1386 CIRpy-1.0.2/docs/source/index.rst000066400000000000000000000032501264260163300165740ustar00rootroot00000000000000.. CIRpy documentation master file, created by sphinx-quickstart on Tue Mar 24 16:12:38 2015. CIRpy ===== .. sectionauthor:: Matt Swain **CIRpy** is a Python interface for the `Chemical Identifier Resolver (CIR)`_ by the CADD Group at the NCI/NIH. CIR is a web service that will resolve any chemical identifier to another chemical representation. For example, you can pass it a chemical name and and request the corresponding SMILES string:: >>> import cirpy >>> cirpy.resolve('Aspirin', 'smiles') 'C1=CC=CC(=C1C(O)=O)OC(C)=O' CIRpy makes interacting with CIR through Python easy. There's no need to construct url requests and parse XML responses — CIRpy does all this for you. Features -------- - Resolve chemical identifiers such as names, CAS registry numbers, SMILES strings and SDF files to any other chemical representation. - Get calculated properties such as molecular weight and hydrogen bond donor and acceptor counts. - Download chemical file formats such as SDF, XYZ, CIF and CDXML. - Get 2D compound depictions as a GIF or PNG images. - Supports Python versions 2.7 – 3.4. - Released under the `MIT license`_. User guide ---------- A step-by-step guide to getting started with CIRpy. .. toctree:: :maxdepth: 2 guide/install guide/gettingstarted guide/resolvers guide/query guide/misc guide/contributing API documentation ----------------- Comprehensive API documentation with information on every function, class and method. .. toctree:: :maxdepth: 2 api .. _`Chemical Identifier Resolver (CIR)`: http://cactus.nci.nih.gov/chemical/structure .. _`MIT license`: https://github.com/mcs07/CIRpy/blob/master/LICENSE CIRpy-1.0.2/requirements/000077500000000000000000000000001264260163300152265ustar00rootroot00000000000000CIRpy-1.0.2/requirements/common.txt000066400000000000000000000000141264260163300172520ustar00rootroot00000000000000lxml>=3.5.0 CIRpy-1.0.2/requirements/dev.txt000066400000000000000000000001001264260163300165340ustar00rootroot00000000000000-r common.txt bumpversion>=0.5.3 coverage>=4.0.3 coveralls>=1.1 CIRpy-1.0.2/setup.py000066400000000000000000000030361264260163300142170ustar00rootroot00000000000000#!/usr/bin/env python import os from setuptools import setup if os.path.exists('README.rst'): long_description = open('README.rst').read() else: long_description = '''CIRpy is a Python interface for the Chemical Identifier Resolver web service that is provided by the CADD Group at the NCI/NIH.''' setup( name='CIRpy', version='1.0.2', author='Matt Swain', author_email='m.swain@me.com', license='MIT', url='https://github.com/mcs07/CIRpy', py_modules=['cirpy'], description='Python wrapper for the NCI Chemical Identifier Resolver (CIR).', long_description=long_description, keywords='python rest api chemistry cheminformatics', extras_require={'lxml': ['lxml']}, test_suite='cirpy_test', classifiers=[ 'Intended Audience :: Science/Research', 'Intended Audience :: Healthcare Industry', 'Intended Audience :: Developers', 'Topic :: Scientific/Engineering', 'Topic :: Scientific/Engineering :: Bio-Informatics', 'Topic :: Scientific/Engineering :: Chemistry', 'Topic :: Database :: Front-Ends', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Internet', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', ], )