pypdb 2.2__tar.gz → 2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. pypdb-2.4/PKG-INFO +59 -0
  2. {pypdb-2.2 → pypdb-2.4}/README.md +8 -12
  3. pypdb-2.4/pypdb/clients/data/data_types.py +192 -0
  4. pypdb-2.4/pypdb/clients/data/graphql/graphql.py +35 -0
  5. pypdb-2.4/pypdb/clients/data/graphql/test_graphql.py +25 -0
  6. pypdb-2.4/pypdb/clients/data/test_data_types.py +122 -0
  7. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/fasta/fasta_client.py +6 -2
  8. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/fasta/fasta_client_test.py +1 -1
  9. pypdb-2.4/pypdb/clients/search/operators/__init__.py +0 -0
  10. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/text_operators.py +2 -1
  11. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/search_client.py +12 -5
  12. {pypdb-2.2 → pypdb-2.4}/pypdb/pypdb.py +24 -20
  13. pypdb-2.4/pypdb/util/__init__.py +0 -0
  14. pypdb-2.4/pypdb.egg-info/PKG-INFO +59 -0
  15. {pypdb-2.2 → pypdb-2.4}/pypdb.egg-info/SOURCES.txt +8 -1
  16. {pypdb-2.2 → pypdb-2.4}/setup.py +9 -1
  17. pypdb-2.4/tests/test_pypdb.py +67 -0
  18. pypdb-2.2/PKG-INFO +0 -15
  19. pypdb-2.2/pypdb.egg-info/PKG-INFO +0 -15
  20. {pypdb-2.2 → pypdb-2.4}/LICENSE +0 -0
  21. {pypdb-2.2 → pypdb-2.4}/pypdb/__init__.py +0 -0
  22. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/__init__.py +0 -0
  23. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/data/__init__.py +0 -0
  24. {pypdb-2.2/pypdb/clients/fasta → pypdb-2.4/pypdb/clients/data/graphql}/__init__.py +0 -0
  25. {pypdb-2.2/pypdb/clients/pdb → pypdb-2.4/pypdb/clients/fasta}/__init__.py +0 -0
  26. {pypdb-2.2/pypdb/clients/search → pypdb-2.4/pypdb/clients/pdb}/__init__.py +0 -0
  27. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/pdb/pdb_client.py +0 -0
  28. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/pdb/pdb_client_test.py +0 -0
  29. {pypdb-2.2/pypdb/util → pypdb-2.4/pypdb/clients/search}/__init__.py +0 -0
  30. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/chemical_operators.py +0 -0
  31. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/chemical_operators_test.py +0 -0
  32. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/seqmotif_operators.py +0 -0
  33. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/seqmotif_operators_test.py +0 -0
  34. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/sequence_operators.py +0 -0
  35. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/sequence_operators_test.py +0 -0
  36. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/structure_operators.py +0 -0
  37. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/structure_operators_test.py +0 -0
  38. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/operators/text_operators_test.py +0 -0
  39. {pypdb-2.2 → pypdb-2.4}/pypdb/clients/search/search_client_test.py +0 -0
  40. {pypdb-2.2 → pypdb-2.4}/pypdb/conftest.py +0 -0
  41. {pypdb-2.2 → pypdb-2.4}/pypdb/util/http_requests.py +0 -0
  42. {pypdb-2.2 → pypdb-2.4}/pypdb/util/test_http_requests.py +0 -0
  43. {pypdb-2.2 → pypdb-2.4}/pypdb.egg-info/.ipynb_checkpoints/dependency_links-checkpoint.txt +0 -0
  44. {pypdb-2.2 → pypdb-2.4}/pypdb.egg-info/dependency_links.txt +0 -0
  45. {pypdb-2.2 → pypdb-2.4}/pypdb.egg-info/requires.txt +0 -0
  46. {pypdb-2.2 → pypdb-2.4}/pypdb.egg-info/top_level.txt +0 -0
  47. {pypdb-2.2 → pypdb-2.4}/setup.cfg +0 -0
pypdb-2.4/PKG-INFO ADDED
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.1
2
+ Name: pypdb
3
+ Version: 2.4
4
+ Summary: A Python wrapper for the RCSB Protein Data Bank (PDB) API
5
+ Home-page: https://github.com/williamgilpin/pypdb
6
+ Download-URL: https://github.com/williamgilpin/pypdb/tarball/0.6
7
+ Author: William Gilpin
8
+ Author-email: firstnamelastname@gmail.com
9
+ Keywords: protein,data,RESTful,api
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: requests
13
+
14
+ # PyPDB
15
+
16
+ A Python 3 toolkit for performing searches with the RCSB Protein Data Bank (PDB). This can be used to perform advanced searches for PDB IDs matching various criteria, as well as to look up information associated with specific PDB IDs. This tool allows standard operations that can be perfomed from within the PDB website (BLAST, PFAM lookup, etc.) to be performed from within Python scripts.
17
+
18
+ If you use this module for any published work, please consider citing the accompanying paper
19
+
20
+ Gilpin, W. "PyPDB: A Python API for the Protein Data Bank."
21
+ Bioinformatics, Oxford Journals, 2016.
22
+
23
+ ## Installation
24
+
25
+ Install using pip:
26
+
27
+ $ pip install pypdb
28
+
29
+ To install the development version, which contains the latest features and fixes, install directly from GitHub using
30
+
31
+ $ pip install git+https://github.com/williamgilpin/pypdb
32
+
33
+ If you need to install directly from setup.py,
34
+
35
+ $ python setup.py install
36
+
37
+ Test the installation, and check that the code successfully connects to the PDB, navigate to the root directory and run
38
+
39
+ $ pytest
40
+
41
+ This code has been designed and tested for Python 3.
42
+
43
+ ## Usage
44
+
45
+ ### PDB Text Search
46
+ This package can be used to get lists of PDB IDs associated with specific search terms, experiment types, structures, and other common criteria. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`search/EXAMPLES.md`](pypdb/clients/search/EXAMPLES.md).
47
+
48
+ ### PDB Data Fetch
49
+ Given a list of PDBs, this package can be used to fetch data associated with those PDBs, including their dates of deposition, lists of authors and associated publications, their sequences or structures, their top BLAST matches, and other query-specific attributes like lists of a ligands or chemical structure. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`data/EXAMPLES.md`](pypdb/clients/data/EXAMPLES.md).
50
+
51
+ ## Issues and Feature Requests
52
+
53
+ If you run into an issue, or if you find a workaround for an existing issue, we would very much appreciate it if you could post your question or code as a GitHub issue.
54
+
55
+ If posting a feature request, please check that your request is possible using [the current GUI on current RCSB website](https://www.rcsb.org/search/advanced). If so, please perform your search, and then click the link that says `JSON` in the upper right hand corner of the Advanced Search box. Please post that JSON code with your feature request.
56
+
57
+
58
+
59
+
@@ -2,24 +2,20 @@
2
2
 
3
3
  A Python 3 toolkit for performing searches with the RCSB Protein Data Bank (PDB). This can be used to perform advanced searches for PDB IDs matching various criteria, as well as to look up information associated with specific PDB IDs. This tool allows standard operations that can be perfomed from within the PDB website (BLAST, PFAM lookup, etc.) to be performed from within Python scripts.
4
4
 
5
- Examples of each function and its associated output can be found in [`demos/demos.ipynb`](demos/demos.ipynb).
6
-
7
5
  If you use this module for any published work, please consider citing the accompanying paper
8
6
 
9
- Gilpin, W. "PyPDB: A Python API for the Protein Data Bank."
7
+ Gilpin, W. "PyPDB: A Python API for the Protein Data Bank."
10
8
  Bioinformatics, Oxford Journals, 2016.
11
9
 
12
- **We very much welcome contributors and pull requests**
13
-
14
10
  ## Installation
15
11
 
16
12
  Install using pip:
17
13
 
18
- $ pip install pypdb
14
+ $ pip install pypdb
19
15
 
20
16
  To install the development version, which contains the latest features and fixes, install directly from GitHub using
21
17
 
22
- $ pip install git+git://github.com/williamgilpin/pypdb
18
+ $ pip install git+https://github.com/williamgilpin/pypdb
23
19
 
24
20
  If you need to install directly from setup.py,
25
21
 
@@ -27,17 +23,17 @@ If you need to install directly from setup.py,
27
23
 
28
24
  Test the installation, and check that the code successfully connects to the PDB, navigate to the root directory and run
29
25
 
30
- $ pytest
26
+ $ pytest
31
27
 
32
28
  This code has been designed and tested for Python 3.
33
29
 
34
30
  ## Usage
35
31
 
36
- This package can be used to get lists of PDB IDs associated with specific search terms, experiment types, structures, and other common criteria.
37
-
38
- Given a list of PDBs, this package can be used to fetch any data associated with those PDBs, including their dates of deposition, lists of authors and associated publications, their sequences or structures, their top BLAST matches, and other query-specific attributes like lists of a ligands or chemical structure.
32
+ ### PDB Text Search
33
+ This package can be used to get lists of PDB IDs associated with specific search terms, experiment types, structures, and other common criteria. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`search/EXAMPLES.md`](pypdb/clients/search/EXAMPLES.md).
39
34
 
40
- A set of demos is included in the iPython notebook **demos.ipynb**. A static version of this notebook (for viewing) is available as **demos.html**
35
+ ### PDB Data Fetch
36
+ Given a list of PDBs, this package can be used to fetch data associated with those PDBs, including their dates of deposition, lists of authors and associated publications, their sequences or structures, their top BLAST matches, and other query-specific attributes like lists of a ligands or chemical structure. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`data/EXAMPLES.md`](pypdb/clients/data/EXAMPLES.md).
41
37
 
42
38
  ## Issues and Feature Requests
43
39
 
@@ -0,0 +1,192 @@
1
+ """
2
+ Class for data types that can be accessed in the PDB DATA-API
3
+ https://data.rcsb.org/#data-organization
4
+
5
+ Namely:
6
+ - entry
7
+ - polymer entity
8
+ - branched entity
9
+ - non-polymer entity
10
+ - polymer instance
11
+ - branched instance
12
+ - non-polymer instance
13
+ - assembly
14
+ - chemical component
15
+ (currently not implemented:)
16
+ - PubMed integrated data
17
+ - UniProt integrated data
18
+ - DrugBank integrated data
19
+ """
20
+ from dataclasses import dataclass, field
21
+ from enum import Enum
22
+
23
+ #TODO: handle batch requests
24
+
25
+ from pypdb.clients.data.graphql.graphql import search_graphql
26
+
27
+ class DataType(Enum):
28
+ ENTRY = "entries"
29
+ POLYMER_ENTITY = "polymer_entities"
30
+ BRANCHED_ENTITY = "branched_entities"
31
+ NONPOLYMER_ENTITY = "nonpolymer_entities"
32
+ POLYMER_ENTITY_INSTANCE = "polymer_entity_instances"
33
+ BRANCHED_ENTITY_INSTANCE = "branched_entity_instances"
34
+ NONPOLYMER_ENTITY_INSTANCE = "nonpolymer_entity_instances"
35
+ ASSEMBLY = "assemblies"
36
+ CHEMICAL_COMPONENT = "chem_comps"
37
+
38
+ @dataclass
39
+ class DataFetcher:
40
+ """
41
+ General class that will host various data types, as detailed above.
42
+ """
43
+
44
+ id: str | list
45
+ data_type: DataType
46
+
47
+ properties: dict = field(default_factory=dict)
48
+ json_query: dict = field(default_factory=dict)
49
+ response: dict = field(default_factory=dict)
50
+
51
+ def __post_init__(self):
52
+ """
53
+ Check types of IDs given, format accordingly.
54
+ """
55
+
56
+ if isinstance(self.id, str):
57
+ self.id = [self.id]
58
+
59
+ if "entit" in self.data_type.value and "instance" not in self.data_type.value:
60
+ for id in self.id:
61
+ if '_' not in id:
62
+ print(f"WARNING: {id} not valid for {self.data_type.value}.")
63
+ elif "instance" in self.data_type.value:
64
+ for id in self.id:
65
+ if '.' not in id:
66
+ print(f"WARNING: {id} not valid for {self.data_type.value}.")
67
+ elif self.data_type == DataType.ASSEMBLY:
68
+ for id in self.id:
69
+ if '-' not in id:
70
+ print(f"WARNING: {id} not valid for {self.data_type.value}.")
71
+
72
+ def add_property(self, property):
73
+ """
74
+ Add property to the list of data to fetch from the PDB.
75
+
76
+ property is a python dict, with keys as properties, and
77
+ values as subproperties.
78
+
79
+ e.g.:
80
+
81
+ {"cell": ["volume", "angle_beta"], "exptl": ["method"]}
82
+
83
+ If the user is trying to add a property that already exists,
84
+ the subproperties are merged.
85
+ """
86
+ # check input data type
87
+ if not isinstance(property, dict):
88
+ raise TypeError
89
+ # check data types of keys in dict
90
+ if not all([isinstance(key, str) for key in property.keys()]):
91
+ raise TypeError
92
+ # check that values are lists of strings
93
+ for key, value in property.items():
94
+ if isinstance(value, str):
95
+ property[key] = [value]
96
+ elif not isinstance(value, list):
97
+ raise TypeError
98
+ else:
99
+ if not all([isinstance(val, str) for val in value]):
100
+ raise TypeError
101
+
102
+ # add properties to the dict
103
+ for key, value in property.items():
104
+ if key not in self.properties:
105
+ self.properties[key] = value
106
+ else:
107
+ self.properties[key] += value
108
+ self.properties[key] = list(set(self.properties[key]))
109
+
110
+ def generate_json_query(self):
111
+ """
112
+ Given IDs, data type, and properties to fetch, create JSON query that
113
+ will utilize graphql.
114
+ """
115
+ if not self.properties:
116
+ print("ERROR: no properties given to generate JSON query.")
117
+ raise ValueError
118
+
119
+ if self.data_type == DataType.ENTRY:
120
+ q_str = "entry_ids"
121
+ elif "entit" in self.data_type.value:
122
+ if "instance" in self.data_type.value:
123
+ q_str = "instance_ids"
124
+ else:
125
+ q_str = "entity_ids"
126
+ elif self.data_type == DataType.ASSEMBLY:
127
+ q_str = "assembly_ids"
128
+ elif self.data_type == DataType.CHEMICAL_COMPONENT:
129
+ q_str = "comp_ids"
130
+
131
+ data_str = f"{self.data_type.value}({q_str}: [" + ",".join(f"\"{w}\"" for w in self.id) + "])"
132
+
133
+ props_string = ""
134
+ for key, val in self.properties.items():
135
+ if len(val) == 0:
136
+ props_string += f"{key},"
137
+ else:
138
+ props_string += f"{key} {{" + ",".join(val) + "}"
139
+
140
+ self.json_query = {'query': "{" + data_str + "{" + props_string + "}}"}
141
+
142
+
143
+ def fetch_data(self):
144
+ """
145
+ Once the JSON query is created, fetch data from the PDB, using graphql.
146
+ """
147
+ if not self.json_query:
148
+ self.generate_json_query()
149
+
150
+ response = search_graphql(self.json_query)
151
+
152
+ if "errors" in response:
153
+ print("ERROR encountered in fetch_data().")
154
+ for error in response['errors']:
155
+ print(error['message'])
156
+
157
+ return
158
+
159
+ self.response = response
160
+
161
+ if len(self.response['data'][self.data_type.value]) != len(self.id):
162
+ print("WARNING: one or more IDs not found in the PDB.")
163
+
164
+ def return_data_as_df_dict(self):
165
+ """
166
+ Return the fetched data as a dict usable by pandas or polars.
167
+ """
168
+ if not self.response:
169
+ return None
170
+
171
+ data = self.response['data'][self.data_type.value]
172
+
173
+ # flatten data dictionary by joining property and subproperty names
174
+ data_flat = {}
175
+ for i, entry in enumerate(data):
176
+ id = self.id[i]
177
+ curr_dict = {}
178
+ for key, values in entry.items():
179
+ if isinstance(values, list):
180
+ v = values[0]
181
+ else:
182
+ v = values
183
+ if isinstance(v, str):
184
+ new_key = f"{key}"
185
+ curr_dict[new_key] = v
186
+ else:
187
+ for subprop, val in v.items():
188
+ new_key = f"{key}.{subprop}"
189
+ curr_dict[new_key] = val
190
+ data_flat[id] = curr_dict
191
+
192
+ return data_flat
@@ -0,0 +1,35 @@
1
+ """Contains logic to perform arbitrary GraphQL searches against RCSB.
2
+
3
+ For the differences between the GraphQL and RESTful searches, see:
4
+ https://data.rcsb.org/index.html#gql-vs-rest
5
+ """
6
+ import requests
7
+ import warnings
8
+ from typing import Any # DO NOT APPROVE: fix this to actual type
9
+
10
+ RSCB_GRAPHQL_URL = "https://data.rcsb.org/graphql?query="
11
+
12
+
13
+ def search_graphql(graphql_json_query: str) -> Any:
14
+ """Performs RCSB search with JSON query using GraphQL.
15
+
16
+ For details on what the RCSB GraphQL interface is, see:
17
+ https://data.rcsb.org/index.html#gql-api
18
+
19
+ This function should return the equivalent information as this site:
20
+ https://data.rcsb.org/graphql/index.html
21
+
22
+ Args:
23
+ graphql_json_query: GraphQL JSON query, as a string. Whitespace doesn't
24
+ matter. e.g. "{entry(entry_id:"4HHB"){exptl{method}}}"
25
+ """
26
+
27
+ response = requests.post(url=RSCB_GRAPHQL_URL,
28
+ json=graphql_json_query)
29
+
30
+ if not response.ok:
31
+ warnings.warn(f"It appears request failed with: {response.text}")
32
+ response.raise_for_status()
33
+
34
+
35
+ return response.json()
@@ -0,0 +1,25 @@
1
+ """Unit tests for RCSB DATA API Python wrapper."""
2
+ import unittest
3
+ from unittest import mock
4
+ import requests
5
+
6
+ from pypdb.clients.data.graphql import graphql
7
+
8
+ class TestGraphQL(unittest.TestCase):
9
+ @mock.patch.object(requests, "post")
10
+ def test_simple_search(self, mock_post):
11
+ json_query = {'query': '{ entry(entry_id: "4HHB"){struct {title}} }'}
12
+ expected_return_json_as_dict = {'data': {'entry': {'struct': {'title': 'THE CRYSTAL STRUCTURE OF HUMAN DEOXYHAEMOGLOBIN AT 1.74 ANGSTROMS RESOLUTION'}}}}
13
+
14
+ mock_response = mock.create_autospec(requests.Response, instance=True)
15
+ mock_response.json.return_value = expected_return_json_as_dict
16
+ mock_post.return_value = mock_response
17
+
18
+ results = graphql.search_graphql(json_query)
19
+
20
+ mock_post.assert_called_once_with(url=graphql.RSCB_GRAPHQL_URL, json=json_query)
21
+ self.assertEqual(results, expected_return_json_as_dict)
22
+
23
+
24
+ if __name__ == '__main__':
25
+ unittest.main()
@@ -0,0 +1,122 @@
1
+ """
2
+ Unit tests for the data_types classes.
3
+ """
4
+ import unittest
5
+ from unittest import mock
6
+ import requests
7
+ from pypdb.clients.data.graphql.graphql import RSCB_GRAPHQL_URL
8
+
9
+ from pypdb.clients.data.data_types import DataFetcher, DataType
10
+
11
+ class TestEntry(unittest.TestCase):
12
+ def test_create(self):
13
+ entry = DataFetcher("4HHB", DataType.ENTRY)
14
+
15
+ self.assertTrue(isinstance(entry.properties, dict))
16
+ self.assertTrue(not entry.properties)
17
+
18
+ self.assertTrue(isinstance(entry.json_query, dict))
19
+ self.assertTrue(not entry.json_query)
20
+
21
+ self.assertTrue(isinstance(entry.response, dict))
22
+ self.assertTrue(not entry.response)
23
+
24
+ self.assertEqual(entry.id, ["4HHB"])
25
+
26
+ def test_generate_json_query(self):
27
+ entry = DataFetcher("4HHB", DataType.ENTRY)
28
+
29
+ property = {"exptl":["method", "details"]}
30
+
31
+ entry.add_property(property)
32
+
33
+ self.assertIsNotNone(entry.properties)
34
+
35
+ entry.generate_json_query()
36
+
37
+ self.assertTrue(isinstance(entry.json_query, dict))
38
+ self.assertTrue("query" in entry.json_query)
39
+
40
+ def test_fetch_entry(self):
41
+ entry = DataFetcher("4HHB", DataType.ENTRY)
42
+ property = {"exptl":["method", "details"]}
43
+
44
+ entry.add_property(property)
45
+
46
+ entry.fetch_data()
47
+
48
+ self.assertTrue(entry.response)
49
+
50
+ def test_return_data_as_pandas_df(self):
51
+ entry = DataFetcher(["4HHB", "12CA", "3PQR"], DataType.ENTRY)
52
+ property = {"exptl":["method", "details"]}
53
+
54
+ entry.add_property(property)
55
+
56
+ entry.fetch_data()
57
+ df = entry.return_data_as_pandas_df()
58
+
59
+ self.assertTrue(df is not None)
60
+
61
+ def test_assembly_fetch(self):
62
+ assembly = DataFetcher(["4HHB-1", "12CA-1", "3PQR-1"], DataType.ASSEMBLY)
63
+ property = {"rcsb_assembly_info": ["entry_id", "assembly_id", "polymer_entity_instance_count"]}
64
+
65
+ assembly.add_property(property)
66
+ assembly.fetch_data()
67
+
68
+ self.assertFalse(not assembly.response)
69
+
70
+ def test_polymer_entity_fetch(self):
71
+ fetcher = DataFetcher(["2CPK_1","3WHM_1","2D5Z_1"], DataType.POLYMER_ENTITY)
72
+
73
+ property = {"rcsb_id": [],
74
+ "rcsb_entity_source_organism": ["ncbi_taxonomy_id", "ncbi_scientific_name"],
75
+ "rcsb_cluster_membership": ["cluster_id", "identity"]}
76
+
77
+ fetcher.add_property(property)
78
+ fetcher.fetch_data()
79
+
80
+ self.assertFalse(not fetcher.response)
81
+
82
+ df = fetcher.return_data_as_pandas_df()
83
+ self.assertFalse(df is None)
84
+
85
+ def test_polymer_instance_fetch(self):
86
+ fetcher = DataFetcher(["4HHB.A", "12CA.A", "3PQR.A"], DataType.POLYMER_ENTITY_INSTANCE)
87
+ property = {"rcsb_id": [],
88
+ "rcsb_polymer_instance_annotation": ["annotation_id", "name", "type"]}
89
+ fetcher.add_property(property)
90
+ fetcher.fetch_data()
91
+
92
+ self.assertFalse(not fetcher.response)
93
+
94
+ df = fetcher.return_data_as_pandas_df()
95
+ self.assertFalse(df is None)
96
+
97
+ def test_branched_entity_fetch(self):
98
+ fetcher = DataFetcher(["5FMB_2", "6L63_3"], DataType.BRANCHED_ENTITY)
99
+ property = {"pdbx_entity_branch": ["type"],
100
+ "pdbx_entity_branch_descriptor": ["type", "descriptor"]}
101
+
102
+ fetcher.add_property(property)
103
+ fetcher.fetch_data()
104
+
105
+ self.assertFalse(not fetcher.response)
106
+
107
+ df = fetcher.return_data_as_pandas_df()
108
+ self.assertFalse(df is None)
109
+
110
+ def test_chem_comps_fetch(self):
111
+ fetcher = DataFetcher(["NAG","EBW"], DataType.CHEMICAL_COMPONENT)
112
+ property = {"rcsb_id":[], "chem_comp": ["type", "formula_weight","name","formula"],
113
+ "rcsb_chem_comp_info":["initial_release_date"]}
114
+ fetcher.add_property(property)
115
+ fetcher.fetch_data()
116
+ self.assertFalse(not fetcher.response)
117
+
118
+ df = fetcher.return_data_as_pandas_df()
119
+ self.assertFalse(df is None)
120
+
121
+ if __name__ == '__main__':
122
+ unittest.main()
@@ -51,18 +51,22 @@ def _parse_fasta_text_to_list(raw_fasta_text: str) -> List[FastaSequence]:
51
51
  return fasta_list
52
52
 
53
53
 
54
- def get_fasta_from_rcsb_entry(rcsb_id: str) -> List[FastaSequence]:
54
+ def get_fasta_from_rcsb_entry(rcsb_id: str,
55
+ verbosity: bool = True,
56
+ ) -> List[FastaSequence]:
55
57
  """Fetches FASTA sequence associated with PDB structure from RCSB.
56
58
 
57
59
  Args:
58
60
  rcsb_id: RCSB accession code of the structure of interest. E.g. `"5RU3"`
61
+ verbosity: Print out the search query to the console (default: True)
59
62
 
60
63
  Returns:
61
64
  Dictionary containing FASTA result, from polymer entity id to the
62
65
  `FastaSequence` object associated with that entity.
63
66
  """
64
67
 
65
- print("Querying RCSB for the '{}' FASTA file.".format(rcsb_id))
68
+ if verbosity:
69
+ print("Querying RCSB for the '{}' FASTA file.".format(rcsb_id))
66
70
  response = requests.get(FASTA_BASE_URL + rcsb_id)
67
71
 
68
72
  if not response.ok:
@@ -16,7 +16,7 @@ class TestFastaLogic(unittest.TestCase):
16
16
  mock_response.text = "fake_fasta_response"
17
17
  mock_get.return_value = mock_response
18
18
 
19
- fasta_client.get_fasta_from_rcsb_entry("6TML")
19
+ fasta_client.get_fasta_from_rcsb_entry("6TML", verbosity=True)
20
20
  mock_get.assert_called_once_with(
21
21
  "https://www.rcsb.org/fasta/entry/6TML")
22
22
  mock_parse_fasta.assert_called_once_with("fake_fasta_response")
File without changes
@@ -169,7 +169,8 @@ class RangeOperator:
169
169
  "operator": "range",
170
170
  "attribute": self.attribute,
171
171
  "negation": self.negation,
172
- "value": [self.from_value, self.to_value]
172
+ "value": {"from": self.from_value,
173
+ "to": self.to_value},
173
174
  }
174
175
 
175
176
 
@@ -132,7 +132,8 @@ def perform_search(
132
132
  return_type: ReturnType = ReturnType.ENTRY,
133
133
  request_options: Optional[RequestOptions] = None,
134
134
  return_with_scores: bool = False,
135
- return_raw_json_dict: bool = False
135
+ return_raw_json_dict: bool = False,
136
+ verbosity: bool = True,
136
137
  ) -> Union[List[str], List[ScoredResult], RawJSONDictResponse]:
137
138
  """Performs search specified by `search_operator`.
138
139
  Returns entity strings of type `return_type` that match the resulting hits.
@@ -156,6 +157,7 @@ def perform_search(
156
157
  (if this is true, returns List[ScoredResult] instead of List[str])
157
158
  return_raw_json_dict: If True, this function returns the raw JSON
158
159
  response from RCSB, instead of a
160
+ verbosity: Print out the search query to the console (default: True)
159
161
 
160
162
  Returns:
161
163
  List of entity ids, corresponding to entities that match the given
@@ -184,7 +186,8 @@ def perform_search(
184
186
  return_type=return_type,
185
187
  request_options=request_options,
186
188
  return_with_scores=return_with_scores,
187
- return_raw_json_dict=return_raw_json_dict)
189
+ return_raw_json_dict=return_raw_json_dict,
190
+ verbosity=verbosity)
188
191
 
189
192
 
190
193
  _SEARCH_OPERATORS = text_operators.TEXT_SEARCH_OPERATORS + [
@@ -197,7 +200,8 @@ def perform_search_with_graph(
197
200
  return_type: ReturnType = ReturnType.ENTRY,
198
201
  request_options: Optional[RequestOptions] = None,
199
202
  return_with_scores: bool = False,
200
- return_raw_json_dict: bool = False
203
+ return_raw_json_dict: bool = False,
204
+ verbosity: bool = True,
201
205
  ) -> Union[List[str], RawJSONDictResponse, List[ScoredResult]]:
202
206
  """Performs specified search using RCSB's search node logic.
203
207
 
@@ -222,6 +226,7 @@ def perform_search_with_graph(
222
226
  get the top X hits that are similar to a certain protein sequence.
223
227
  return_raw_json_dict: Whether to return raw JSON response.
224
228
  (for example, to analyze the scores of various matches)
229
+ verbosity: Print out the search query to the console (default: True)
225
230
 
226
231
  Returns:
227
232
  List of strings, corresponding to hits in the database. Will be of the
@@ -234,6 +239,7 @@ def perform_search_with_graph(
234
239
  if type(query_object) in _SEARCH_OPERATORS:
235
240
  cast_query_object = _QueryNode(query_object) # type: ignore
236
241
  else:
242
+ # print(type(query_object))
237
243
  cast_query_object = query_object # type: ignore
238
244
 
239
245
  if request_options is not None:
@@ -247,8 +253,9 @@ def perform_search_with_graph(
247
253
  "return_type": return_type.value
248
254
  }
249
255
 
250
- print("Querying RCSB Search using the following parameters:\n %s \n" %
251
- json.dumps(rcsb_query_dict))
256
+ if verbosity:
257
+ print("Querying RCSB Search using the following parameters:\n %s \n" %
258
+ json.dumps(rcsb_query_dict))
252
259
 
253
260
  response = requests.post(url=SEARCH_URL_ENDPOINT,
254
261
  data=json.dumps(rcsb_query_dict))
@@ -17,7 +17,6 @@ If you find this code useful, please consider citing the paper:
17
17
  '''
18
18
  from collections import OrderedDict, Counter
19
19
  from itertools import repeat, chain
20
- import requests
21
20
  import time
22
21
  import re
23
22
  import json
@@ -139,6 +138,9 @@ class Query(object):
139
138
  elif query_type == "pfam":
140
139
  query_type = "text"
141
140
  query_subtype = "pfam"
141
+ elif query_type == "uniprot":
142
+ query_type = "text"
143
+ query_subtype = "uniprot"
142
144
  else:
143
145
  query_subtype = None
144
146
 
@@ -258,6 +260,14 @@ class Query(object):
258
260
  "attribute":
259
261
  "rcsb_polymer_entity_annotation.annotation_id"
260
262
  }
263
+ if query_subtype == "uniprot":
264
+ query_params['parameters'] = {
265
+ "operator": "exact_match",
266
+ "negation": False,
267
+ "value": str(search_term),
268
+ "attribute":
269
+ "rcsb_polymer_entity_container_identifiers.reference_sequence_identifiers.database_accession"
270
+ }
261
271
 
262
272
  self.scan_params = dict()
263
273
  self.scan_params["query"] = query_params
@@ -489,13 +499,13 @@ def get_pdb_file(pdb_id: str, filetype='pdb', compression=False):
489
499
  "See `pypdb/clients/pdb/pdb_client.py` for a near-identical "
490
500
  "function to use", DeprecationWarning)
491
501
 
492
- if filetype is 'pdb':
502
+ if filetype == 'pdb':
493
503
  filetype_enum = pdb_client.PDBFileType.PDB
494
- elif filetype is 'cif':
504
+ elif filetype == 'cif':
495
505
  filetype_enum = pdb_client.PDBFileType.CIF
496
- elif filetype is 'xml':
506
+ elif filetype == 'xml':
497
507
  filetype_enum = pdb_client.PDBFileType.XML
498
- elif filetype is 'structfact':
508
+ elif filetype == 'structfact':
499
509
  filetype_enum = pdb_client.PDBFileType.STRUCTFACT
500
510
  else:
501
511
  warnings.warn(
@@ -505,14 +515,14 @@ def get_pdb_file(pdb_id: str, filetype='pdb', compression=False):
505
515
 
506
516
 
507
517
  # https://data.rcsb.org/migration-guide.html#chem-comp-description
508
- # def describe_chemical(chem_id):
518
+ def describe_chemical(chem_id):
509
519
  # """
510
520
 
511
521
  # Parameters
512
522
  # ----------
513
523
 
514
524
  # chem_id : string
515
- # A 4 character string representing the full chemical sequence of interest (ie, NAG)
525
+ # A 3 character string representing the full chemical sequence of interest (ie, NAG)
516
526
 
517
527
  # Returns
518
528
  # -------
@@ -523,19 +533,13 @@ def get_pdb_file(pdb_id: str, filetype='pdb', compression=False):
523
533
  # Examples
524
534
  # --------
525
535
  # >>> chem_desc = describe_chemical('NAG')
526
- # >>> print(chem_desc)
527
- # {'describeHet': {'ligandInfo': {'ligand': {'@molecularWeight': '221.208',
528
- # 'InChIKey': 'OVRNDRQMDRJTHS-FMDGEEDCSA-N', '@type': 'D-saccharide',
529
- # 'chemicalName': 'N-ACETYL-D-GLUCOSAMINE', '@chemicalID': 'NAG',
530
- # 'smiles': 'CC(=O)N[C@@H]1[C@H]([C@@H]([C@H](O[C@H]1O)CO)O)O', '
531
- # InChI': 'InChI=1S/C8H15NO6/c1-3(11)9-5-7(13)6(12)4(2-10)15-8(5)14/
532
- # h4-8,10,12-14H,2H2,1H3,(H,9,11)/t4-,5-,6-,7-,8-/m1/s1',
533
- # 'formula': 'C8 H15 N O6'}}}}
534
-
536
+ # >>> print(chem_desc["rcsb_chem_comp_descriptor"]["smiles"])
537
+ # 'CC(=O)NC1C(C(C(OC1O)CO)O)O'
535
538
  # """
536
- # out = get_info(chem_id, url_root = 'http://www.rcsb.org/pdb/rest/describeHet?chemicalID=')
537
- # out = to_dict(out)
538
- # return out
539
+ if (len(chem_id) > 3):
540
+ raise Exception("Ligand id with more than 3 characters provided")
541
+
542
+ return get_info(chem_id, url_root = 'https://data.rcsb.org/rest/v1/core/chemcomp/')
539
543
 
540
544
  # def get_ligands(pdb_id):
541
545
  # """Return ligands of given PDB ID
@@ -653,7 +657,7 @@ def get_pdb_file(pdb_id: str, filetype='pdb', compression=False):
653
657
  # return remove_at_sign(out['sequenceCluster'])
654
658
 
655
659
 
656
- def get_blast(pdb_id, chain_id='A', identity_cutoff=0.99):
660
+ def get_blast(pdb_id, chain_id='A', identity_cutoff=0.99, verbosity=True):
657
661
  """
658
662
  ---
659
663
  WARNING: this function is deprecated and slated to be deleted due to RCSB
File without changes
@@ -0,0 +1,59 @@
1
+ Metadata-Version: 2.1
2
+ Name: pypdb
3
+ Version: 2.4
4
+ Summary: A Python wrapper for the RCSB Protein Data Bank (PDB) API
5
+ Home-page: https://github.com/williamgilpin/pypdb
6
+ Download-URL: https://github.com/williamgilpin/pypdb/tarball/0.6
7
+ Author: William Gilpin
8
+ Author-email: firstnamelastname@gmail.com
9
+ Keywords: protein,data,RESTful,api
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: requests
13
+
14
+ # PyPDB
15
+
16
+ A Python 3 toolkit for performing searches with the RCSB Protein Data Bank (PDB). This can be used to perform advanced searches for PDB IDs matching various criteria, as well as to look up information associated with specific PDB IDs. This tool allows standard operations that can be perfomed from within the PDB website (BLAST, PFAM lookup, etc.) to be performed from within Python scripts.
17
+
18
+ If you use this module for any published work, please consider citing the accompanying paper
19
+
20
+ Gilpin, W. "PyPDB: A Python API for the Protein Data Bank."
21
+ Bioinformatics, Oxford Journals, 2016.
22
+
23
+ ## Installation
24
+
25
+ Install using pip:
26
+
27
+ $ pip install pypdb
28
+
29
+ To install the development version, which contains the latest features and fixes, install directly from GitHub using
30
+
31
+ $ pip install git+https://github.com/williamgilpin/pypdb
32
+
33
+ If you need to install directly from setup.py,
34
+
35
+ $ python setup.py install
36
+
37
+ Test the installation, and check that the code successfully connects to the PDB, navigate to the root directory and run
38
+
39
+ $ pytest
40
+
41
+ This code has been designed and tested for Python 3.
42
+
43
+ ## Usage
44
+
45
+ ### PDB Text Search
46
+ This package can be used to get lists of PDB IDs associated with specific search terms, experiment types, structures, and other common criteria. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`search/EXAMPLES.md`](pypdb/clients/search/EXAMPLES.md).
47
+
48
+ ### PDB Data Fetch
49
+ Given a list of PDBs, this package can be used to fetch data associated with those PDBs, including their dates of deposition, lists of authors and associated publications, their sequences or structures, their top BLAST matches, and other query-specific attributes like lists of a ligands or chemical structure. To use the simple API, see the examples in [`demos/demos.ipynb`](demos/demos.ipynb). For advanced search and query logic, see the examples in [`data/EXAMPLES.md`](pypdb/clients/data/EXAMPLES.md).
50
+
51
+ ## Issues and Feature Requests
52
+
53
+ If you run into an issue, or if you find a workaround for an existing issue, we would very much appreciate it if you could post your question or code as a GitHub issue.
54
+
55
+ If posting a feature request, please check that your request is possible using [the current GUI on current RCSB website](https://www.rcsb.org/search/advanced). If so, please perform your search, and then click the link that says `JSON` in the upper right hand corner of the Advanced Search box. Please post that JSON code with your feature request.
56
+
57
+
58
+
59
+
@@ -13,6 +13,11 @@ pypdb.egg-info/top_level.txt
13
13
  pypdb.egg-info/.ipynb_checkpoints/dependency_links-checkpoint.txt
14
14
  pypdb/clients/__init__.py
15
15
  pypdb/clients/data/__init__.py
16
+ pypdb/clients/data/data_types.py
17
+ pypdb/clients/data/test_data_types.py
18
+ pypdb/clients/data/graphql/__init__.py
19
+ pypdb/clients/data/graphql/graphql.py
20
+ pypdb/clients/data/graphql/test_graphql.py
16
21
  pypdb/clients/fasta/__init__.py
17
22
  pypdb/clients/fasta/fasta_client.py
18
23
  pypdb/clients/fasta/fasta_client_test.py
@@ -22,6 +27,7 @@ pypdb/clients/pdb/pdb_client_test.py
22
27
  pypdb/clients/search/__init__.py
23
28
  pypdb/clients/search/search_client.py
24
29
  pypdb/clients/search/search_client_test.py
30
+ pypdb/clients/search/operators/__init__.py
25
31
  pypdb/clients/search/operators/chemical_operators.py
26
32
  pypdb/clients/search/operators/chemical_operators_test.py
27
33
  pypdb/clients/search/operators/seqmotif_operators.py
@@ -34,4 +40,5 @@ pypdb/clients/search/operators/text_operators.py
34
40
  pypdb/clients/search/operators/text_operators_test.py
35
41
  pypdb/util/__init__.py
36
42
  pypdb/util/http_requests.py
37
- pypdb/util/test_http_requests.py
43
+ pypdb/util/test_http_requests.py
44
+ tests/test_pypdb.py
@@ -1,5 +1,10 @@
1
1
  from setuptools import setup
2
2
 
3
+ # read the contents of the README file so that PyPI can use it as the long description
4
+ from pathlib import Path
5
+ this_directory = Path(__file__).parent
6
+ long_description = (this_directory / "README.md").read_text()
7
+
3
8
  modules_list = [
4
9
  "pypdb",
5
10
  "pypdb.util",
@@ -7,6 +12,7 @@ modules_list = [
7
12
  "pypdb.clients.search",
8
13
  "pypdb.clients.search.operators",
9
14
  "pypdb.clients.data",
15
+ "pypdb.clients.data.graphql",
10
16
  "pypdb.clients.fasta",
11
17
  "pypdb.clients.pdb",
12
18
  ]
@@ -15,7 +21,7 @@ setup(
15
21
  name='pypdb',
16
22
  packages=modules_list, # same as 'name'
17
23
  py_modules=modules_list,
18
- version='2.02',
24
+ version='2.04',
19
25
  install_requires=['requests'],
20
26
  description='A Python wrapper for the RCSB Protein Data Bank (PDB) API',
21
27
  author='William Gilpin',
@@ -24,4 +30,6 @@ setup(
24
30
  download_url='https://github.com/williamgilpin/pypdb/tarball/0.6',
25
31
  keywords=['protein', 'data', 'RESTful', 'api'],
26
32
  classifiers=[],
33
+ long_description=long_description,
34
+ long_description_content_type='text/markdown'
27
35
  )
@@ -0,0 +1,67 @@
1
+ import unittest
2
+
3
+ ## Import from local directory
4
+ import sys
5
+ sys.path.insert(0, '../pypdb')
6
+ from pypdb import *
7
+
8
+ # TODO(ejwilliams): Write generic logic, to execute `test_*.py` files
9
+ # within the pypdb directory (removing need for sys.path hack)
10
+
11
+ # aa_index[s] for s in seq_dict[k] if s in aa_index.keys()]
12
+
13
+ class TestSearchFunctions(unittest.TestCase):
14
+
15
+ def test_searchterm(self):
16
+ found_pdbs = Query('ribosome').search()
17
+ self.assertTrue(len(found_pdbs) > 0)
18
+ self.assertTrue(type(found_pdbs[0]) == str)
19
+
20
+ # an error page would be a longer string
21
+ self.assertTrue(len(found_pdbs[0]) < 10)
22
+
23
+ def test_pubmed(self):
24
+ found_pdbs = Query(27499440, "PubmedIdQuery").search()
25
+ self.assertTrue(len(found_pdbs) > 0)
26
+
27
+ def test_treeentity(self):
28
+ found_pdbs = Query('6239', 'TreeEntityQuery').search()
29
+ self.assertTrue(len(found_pdbs) > 0)
30
+
31
+ def test_exptype(self):
32
+ found_pdbs = Query('SOLID-STATE NMR', 'ExpTypeQuery').search()
33
+ self.assertTrue(len(found_pdbs) > 0)
34
+
35
+ def test_structure(self):
36
+ found_pdbs = Query('2E8D', 'structure').search()
37
+ self.assertTrue(len(found_pdbs) > 0)
38
+
39
+ def test_advancedauthor(self):
40
+ found_pdbs = Query('Perutz, M.F.', 'AdvancedAuthorQuery').search()
41
+ self.assertTrue(len(found_pdbs) > 0)
42
+
43
+ def test_organism(self):
44
+ found_pdbs = Query('Dictyostelium', 'OrganismQuery').search()
45
+ self.assertTrue(len(found_pdbs) > 0)
46
+
47
+
48
+
49
+ # def test_blast(self):
50
+ # found_pdbs = blast_from_sequence(
51
+ # 'MTKIANKYEVIDNVEKLEKALKRLREAQSVYATYTQEQVDKIFFEAAMAANKMRIPLAKMAVE'
52
+ # + 'ETGMGVVEDKVIKNHYASEYIYNAYKNTKTCGVIEEDPAFGIKKIAEPLGVIAAVIPTTNP'
53
+ # + 'TSTAIFKTLIALKTRNAIIISPHPRAKNSTIEAAKIVLEAAVKAGAPEGIIGWIDVPSLEL'
54
+ # + 'TNLVMREADVILATGGPGLVKAAYSSGKPAIGVGAGNTPAIIDDSADIVLAVNSIIHSKTF'
55
+ # + 'DNGMICASEQSVIVLDGVYKEVKKEFEKRGCYFLNEDETEKVRKTIIINGALNAKIVGQKA'
56
+ # + 'HTIANLAGFEVPETTKILIGEVTSVDISEEFAHEKLCPVLAMYRAKDFDDALDKAERLVAD'
57
+ # + 'GGFGHTSSLYIDTVTQKEKLQKFSERMKTCRILVNTPSSQGGIGDLYNFKLAPSL',
58
+ # 1e-20)
59
+ # self.assertTrue(len(found_pdbs) > 0)
60
+ # self.assertTrue(type(found_pdbs[0][0]) == str)
61
+
62
+ # # an error page would be a longer string
63
+ # self.assertTrue(len(found_pdbs[0][0]) < 10)
64
+
65
+
66
+ if __name__ == '__main__':
67
+ unittest.main()
pypdb-2.2/PKG-INFO DELETED
@@ -1,15 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pypdb
3
- Version: 2.2
4
- Summary: A Python wrapper for the RCSB Protein Data Bank (PDB) API
5
- Home-page: https://github.com/williamgilpin/pypdb
6
- Download-URL: https://github.com/williamgilpin/pypdb/tarball/0.6
7
- Author: William Gilpin
8
- Author-email: firstnamelastname@gmail.com
9
- License: UNKNOWN
10
- Keywords: protein,data,RESTful,api
11
- Platform: UNKNOWN
12
- License-File: LICENSE
13
-
14
- UNKNOWN
15
-
@@ -1,15 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: pypdb
3
- Version: 2.2
4
- Summary: A Python wrapper for the RCSB Protein Data Bank (PDB) API
5
- Home-page: https://github.com/williamgilpin/pypdb
6
- Download-URL: https://github.com/williamgilpin/pypdb/tarball/0.6
7
- Author: William Gilpin
8
- Author-email: firstnamelastname@gmail.com
9
- License: UNKNOWN
10
- Keywords: protein,data,RESTful,api
11
- Platform: UNKNOWN
12
- License-File: LICENSE
13
-
14
- UNKNOWN
15
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes