bacdive 0.3.1__tar.gz → 2.0.0__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.
@@ -1,108 +1,146 @@
1
- Metadata-Version: 2.1
2
- Name: bacdive
3
- Version: 0.3.1
4
- Summary: BacDive-API - Programmatic Access to the BacDive Database
5
- Home-page: https://bacdive.dsmz.de/
6
- Author: Julia Koblitz
7
- Author-email: julia.koblitz@dsmz.de
8
- Keywords: microbiology bacteria strains phenotypes
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Intended Audience :: Science/Research
12
- Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
13
- Requires-Python: >=3.6
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: python-keycloak
17
- Requires-Dist: requests>=2.25.1
18
- Requires-Dist: urllib3>=1.26.5
19
-
20
- # BacDive API
21
-
22
- Using the BacDive API requires registration. Registration is free but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
23
-
24
- Please register [here](https://api.bacdive.dsmz.de/login).
25
-
26
- The Python package can be initialized using your login credentials:
27
-
28
-
29
- ```python
30
- import bacdive
31
-
32
- client = bacdive.BacdiveClient('name@mail.example', 'password')
33
-
34
- # the search method fetches all BacDive-IDs matching your query
35
- # and returns the number of IDs found
36
- count = client.search(taxonomy='Bacillus subtilis subtilis')
37
- print(count, 'strains found.')
38
-
39
- # the retrieve method lets you iterate over all strains
40
- # and returns the full entry as dict
41
- # Entries can be further filtered using a list of keys (e.g. ['keywords'])
42
- for strain in client.retrieve():
43
- print(strain)
44
- ```
45
-
46
- ## Example queries:
47
-
48
- ```python
49
- # Search by BacDive-IDs (either semicolon separated or as list):
50
- query = {"id": 24493}
51
- query = {"id": "24493;12;132485"}
52
- query = {"id": [24493, 12, 132485]}
53
-
54
- # Search by culture collection number
55
- query = {"culturecolno": "DSM 26640"}
56
-
57
- # Search by taxonomy (either as full name or as list):
58
- # With genus name, species epithet (optional), and subspecies (optional).
59
- query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
60
- query = {"taxonomy": ("Escherichia", "coli")}
61
-
62
- # Search by sequence accession numbers:
63
- query = {"16s": "AF000162"} # 16S sequence
64
- query = {"genome": "GCA_006094295"} # genome sequence
65
-
66
- # run query
67
- client.search(**query)
68
- ```
69
-
70
- ## Filtering
71
-
72
- Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
73
-
74
- ```python
75
- filter=['keywords', 'culture collection no.']
76
- result = client.retrieve(filter)
77
- print({k:v for x in result for k,v in x.items()})
78
- ```
79
-
80
- The printed result will look like this:
81
-
82
- ```python
83
- {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
84
- {'culture collection no.': 'DSM 4393, pC194, SB202'}],
85
- '1162': [{'keywords': ['human pathogen', 'Bacteria']},
86
- {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
87
- 'pUB110'}],
88
- '1163': [{'keywords': ['human pathogen', 'Bacteria']},
89
- {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
90
- '1164': [{'keywords': 'Bacteria'},
91
- {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
92
- ...
93
- ```
94
-
95
- ## New in v0.3
96
-
97
- We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
98
-
99
- ```python
100
- client.includePredictions()
101
- ```
102
-
103
- You can exclude predictions again by calling:
104
-
105
- ```python
106
- client.excludePredictions()
107
- ```
108
-
1
+ Metadata-Version: 2.4
2
+ Name: bacdive
3
+ Version: 2.0.0
4
+ Summary: BacDive-API - Programmatic Access to the BacDive Database
5
+ Home-page: https://bacdive.dsmz.de/
6
+ Author: Julia Koblitz
7
+ Author-email: julia.koblitz@dsmz.de
8
+ Keywords: microbiology bacteria strains phenotypes
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
13
+ Requires-Python: >=3.6
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests>=2.25.1
17
+ Requires-Dist: urllib3>=1.26.5
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: keywords
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
29
+
30
+ # BacDive API v2
31
+
32
+ Using the BacDive API does not require registration anymore, but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
33
+
34
+ ```python
35
+ import bacdive
36
+
37
+ client = bacdive.BacdiveClient()
38
+
39
+ # [optional] You may define the search type as one of the following:
40
+ # 'exact' (default), 'contains', 'startswith', 'endswith'
41
+ client.setSearchType('exact')
42
+
43
+ # The search method fetches all BacDive-IDs matching your query
44
+ # and returns the number of IDs found
45
+ count = client.search(taxonomy='Bacillus subtilis subtilis')
46
+ print(count, 'strains found.')
47
+
48
+ # the retrieve method lets you iterate over all strains
49
+ # and returns the full entry as dict
50
+ # Entries can be further filtered using a list of keys (e.g. ['keywords'])
51
+ for strain in client.retrieve():
52
+ print(strain)
53
+ ```
54
+
55
+ ## Example queries:
56
+
57
+ ```python
58
+ # Search by BacDive-IDs (either semicolon separated or as list):
59
+ query = {"id": 24493}
60
+ query = {"id": "24493;12;132485"}
61
+ query = {"id": [24493, 12, 132485]}
62
+
63
+ # Search by culture collection number
64
+ query = {"culturecolno": "DSM 26640"}
65
+ # New in v1.0: Search by culture collection number with multiple numbers:
66
+ query = {"culturecolno": ["DSM 26640", "DSM 26646"]}
67
+ query = {"culturecolno": "DSM 26640;DSM 26646"} # semicolon may be used as separator
68
+
69
+ # Search by culture collection number with search type 'startswith':
70
+ client.setSearchType('startswith')
71
+ query = {"culturecolno": "DSM"}
72
+
73
+ # Search by taxonomy (either as full name or as list):
74
+ # With genus name, species epithet (optional), and subspecies (optional).
75
+ query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
76
+ query = {"taxonomy": ("Escherichia", "coli")}
77
+
78
+ # Search by 16S sequence accession numbers:
79
+ query = {"16s": "AF000162"}
80
+ # New in v1.0: Search by 16S sequence with multiple sequence accession numbers:
81
+ query = {"16s": ["AB681963", "JN566021", "AY027686"]}
82
+ # New in v1.0: Search by 16S sequence with search type 'startswith':
83
+ client.setSearchType('startswith')
84
+ query = {"16s": "AB"}
85
+
86
+ # Search by genome sequence accession numbers:
87
+ query = {"genome": "GCA_006094295"}
88
+ # New in v1.0: Search by genome sequence with multiple sequence accession numbers:
89
+ query = {"genome": ["GCA_003332855", "GCA_024623325", "GCA_017377855"]}
90
+ # New in v1.0: Search by genome sequence with search type 'startswith':
91
+ client.setSearchType('startswith')
92
+ query = {"genome": "DSM"}
93
+
94
+
95
+ # run query
96
+ client.search(**query)
97
+ ```
98
+
99
+ ## Filtering
100
+
101
+ Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
102
+
103
+ ```python
104
+ filter=['keywords', 'culture collection no.']
105
+ result = client.retrieve(filter)
106
+ print({k:v for x in result for k,v in x.items()})
107
+ ```
108
+
109
+ The printed result will look like this:
110
+
111
+ ```python
112
+ {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
113
+ {'culture collection no.': 'DSM 4393, pC194, SB202'}],
114
+ '1162': [{'keywords': ['human pathogen', 'Bacteria']},
115
+ {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
116
+ 'pUB110'}],
117
+ '1163': [{'keywords': ['human pathogen', 'Bacteria']},
118
+ {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
119
+ '1164': [{'keywords': 'Bacteria'},
120
+ {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
121
+ ...
122
+ ```
123
+
124
+ ## Hints for more advanced queries
125
+ If you have more advanced queries that are currently not covered by the API, we recommend you to use the [Bac*Dive* Advanced Search](https://bacdive.dsmz.de/advsearch), which is very flexible and powerful. You can then download the resulting table as CSV (button at the top right), import the CSV into your Python script, and use the BacDive-IDs to download all relevant information via the API.
126
+
127
+
128
+ ## New in v0.3
129
+
130
+ We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
131
+
132
+ ```python
133
+ client.includePredictions()
134
+ ```
135
+
136
+ You can exclude predictions again by calling:
137
+
138
+ ```python
139
+ client.excludePredictions()
140
+ ```
141
+
142
+ ## New in v1.0
143
+
144
+ Thanks to [phenolophthaleinum](https://github.com/phenolophthaleinum) for improving the error handling and Joaquim Sardá for improving the BacDive-API and adding new search possibilities.
145
+
146
+ Examples for search type definitions and array requests are included in the examples above.
@@ -0,0 +1,117 @@
1
+ # BacDive API v2
2
+
3
+ Using the BacDive API does not require registration anymore, but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
4
+
5
+ ```python
6
+ import bacdive
7
+
8
+ client = bacdive.BacdiveClient()
9
+
10
+ # [optional] You may define the search type as one of the following:
11
+ # 'exact' (default), 'contains', 'startswith', 'endswith'
12
+ client.setSearchType('exact')
13
+
14
+ # The search method fetches all BacDive-IDs matching your query
15
+ # and returns the number of IDs found
16
+ count = client.search(taxonomy='Bacillus subtilis subtilis')
17
+ print(count, 'strains found.')
18
+
19
+ # the retrieve method lets you iterate over all strains
20
+ # and returns the full entry as dict
21
+ # Entries can be further filtered using a list of keys (e.g. ['keywords'])
22
+ for strain in client.retrieve():
23
+ print(strain)
24
+ ```
25
+
26
+ ## Example queries:
27
+
28
+ ```python
29
+ # Search by BacDive-IDs (either semicolon separated or as list):
30
+ query = {"id": 24493}
31
+ query = {"id": "24493;12;132485"}
32
+ query = {"id": [24493, 12, 132485]}
33
+
34
+ # Search by culture collection number
35
+ query = {"culturecolno": "DSM 26640"}
36
+ # New in v1.0: Search by culture collection number with multiple numbers:
37
+ query = {"culturecolno": ["DSM 26640", "DSM 26646"]}
38
+ query = {"culturecolno": "DSM 26640;DSM 26646"} # semicolon may be used as separator
39
+
40
+ # Search by culture collection number with search type 'startswith':
41
+ client.setSearchType('startswith')
42
+ query = {"culturecolno": "DSM"}
43
+
44
+ # Search by taxonomy (either as full name or as list):
45
+ # With genus name, species epithet (optional), and subspecies (optional).
46
+ query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
47
+ query = {"taxonomy": ("Escherichia", "coli")}
48
+
49
+ # Search by 16S sequence accession numbers:
50
+ query = {"16s": "AF000162"}
51
+ # New in v1.0: Search by 16S sequence with multiple sequence accession numbers:
52
+ query = {"16s": ["AB681963", "JN566021", "AY027686"]}
53
+ # New in v1.0: Search by 16S sequence with search type 'startswith':
54
+ client.setSearchType('startswith')
55
+ query = {"16s": "AB"}
56
+
57
+ # Search by genome sequence accession numbers:
58
+ query = {"genome": "GCA_006094295"}
59
+ # New in v1.0: Search by genome sequence with multiple sequence accession numbers:
60
+ query = {"genome": ["GCA_003332855", "GCA_024623325", "GCA_017377855"]}
61
+ # New in v1.0: Search by genome sequence with search type 'startswith':
62
+ client.setSearchType('startswith')
63
+ query = {"genome": "DSM"}
64
+
65
+
66
+ # run query
67
+ client.search(**query)
68
+ ```
69
+
70
+ ## Filtering
71
+
72
+ Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
73
+
74
+ ```python
75
+ filter=['keywords', 'culture collection no.']
76
+ result = client.retrieve(filter)
77
+ print({k:v for x in result for k,v in x.items()})
78
+ ```
79
+
80
+ The printed result will look like this:
81
+
82
+ ```python
83
+ {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
84
+ {'culture collection no.': 'DSM 4393, pC194, SB202'}],
85
+ '1162': [{'keywords': ['human pathogen', 'Bacteria']},
86
+ {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
87
+ 'pUB110'}],
88
+ '1163': [{'keywords': ['human pathogen', 'Bacteria']},
89
+ {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
90
+ '1164': [{'keywords': 'Bacteria'},
91
+ {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
92
+ ...
93
+ ```
94
+
95
+ ## Hints for more advanced queries
96
+ If you have more advanced queries that are currently not covered by the API, we recommend you to use the [Bac*Dive* Advanced Search](https://bacdive.dsmz.de/advsearch), which is very flexible and powerful. You can then download the resulting table as CSV (button at the top right), import the CSV into your Python script, and use the BacDive-IDs to download all relevant information via the API.
97
+
98
+
99
+ ## New in v0.3
100
+
101
+ We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
102
+
103
+ ```python
104
+ client.includePredictions()
105
+ ```
106
+
107
+ You can exclude predictions again by calling:
108
+
109
+ ```python
110
+ client.excludePredictions()
111
+ ```
112
+
113
+ ## New in v1.0
114
+
115
+ Thanks to [phenolophthaleinum](https://github.com/phenolophthaleinum) for improving the error handling and Joaquim Sardá for improving the BacDive-API and adding new search possibilities.
116
+
117
+ Examples for search type definitions and array requests are included in the examples above.
@@ -1,232 +1,267 @@
1
- '''
2
- Using the BacDive API requires registration. Registrations is free but the
3
- usage of BacDive data is only permitted when in compliance with the BacDive
4
- terms of use. See https://bacdive.dsmz.de/about for details.
5
-
6
- Please register at https://api.bacdive.dsmz.de/login.
7
- '''
8
-
9
- from keycloak.exceptions import KeycloakAuthenticationError
10
- from keycloak import KeycloakOpenID
11
- import requests
12
- import json
13
-
14
-
15
- class BacdiveClient():
16
- def __init__(self, user, password, public=True):
17
- ''' Initialize client and authenticate on the server '''
18
- self.result = {}
19
- self.public = public
20
-
21
- self.predictions = False
22
-
23
- client_id = "api.bacdive.public"
24
- if self.public:
25
- server_url = "https://sso.dsmz.de/auth/"
26
- else:
27
- server_url = "https://sso.dmz.dsmz.de/auth/"
28
- try:
29
- self.keycloak_openid = KeycloakOpenID(
30
- server_url=server_url,
31
- client_id=client_id,
32
- realm_name="dsmz")
33
-
34
- # Get tokens
35
- token = self.keycloak_openid.token(user, password)
36
- self.access_token = token['access_token']
37
- self.refresh_token = token['refresh_token']
38
- print("-- Authentication successful --")
39
- except KeycloakAuthenticationError as e:
40
- print("ERROR - Authentication failed:", e)
41
-
42
- def includePredictions(self):
43
- self.predictions = True
44
-
45
- def excludePredictions(self):
46
- self.predictions = False
47
-
48
- def do_api_call(self, url):
49
- ''' Initialize API call on given URL and returns result as json '''
50
- if self.public:
51
- baseurl = "https://api.bacdive.dsmz.de/"
52
- else:
53
- baseurl = "http://api.bacdive-dev.dsmz.local/"
54
-
55
- if not url.startswith("http"):
56
- # if base is missing add default:
57
- url = baseurl + url
58
- resp = self.do_request(url)
59
-
60
- if resp.status_code == 500 or resp.status_code == 400:
61
- return json.loads(resp.content)
62
- elif (resp.status_code == 401):
63
- msg = json.loads(resp.content)
64
-
65
- if msg['message'] == "Expired token":
66
- # Access token might have expired (15 minutes life time).
67
- # Get new tokens using refresh token and try again.
68
- token = self.keycloak_openid.refresh_token(self.refresh_token)
69
- self.access_token = token['access_token']
70
- self.refresh_token = token['refresh_token']
71
- return self.do_api_call(url)
72
-
73
- return msg
74
- else:
75
- return json.loads(resp.content)
76
-
77
- def do_request(self, url):
78
- ''' Perform request with authentication '''
79
- headers = {
80
- "Accept": "application/json",
81
- "Authorization": "Bearer {token}".format(token=self.access_token)
82
- }
83
-
84
- if self.predictions:
85
- if "?" in url:
86
- url += "&predictions=1"
87
- else:
88
- url += "?predictions=1"
89
- resp = requests.get(url, headers=headers)
90
- return resp
91
-
92
-
93
- def filterResult(self, d, keys):
94
- ''' Helper function to filter nested dict by keys '''
95
- if not isinstance(d, dict):
96
- yield None
97
- for k, v in d.items():
98
- if k in keys:
99
- yield {k: v}
100
- if isinstance(v, dict):
101
- yield from self.filterResult(v, keys)
102
- elif isinstance(v, list):
103
- for i in v:
104
- if isinstance(i, dict):
105
- yield from self.filterResult(i, keys)
106
-
107
- def retrieve(self, filter=None):
108
- ''' Yields all the received entries and does next call if result is incomplete '''
109
- ids = ";".join([str(i) for i in self.result['results']])
110
- entries = self.do_api_call('fetch/'+ids)['results']
111
- for el in entries:
112
- if isinstance(el, dict):
113
- entry = el
114
- el = entry.get("id")
115
- else:
116
- entry = entries[el]
117
- if filter:
118
- entry = {el: [i for i in self.filterResult(entry, filter)]}
119
- yield entry
120
- if self.result['next']:
121
- self.result = self.do_api_call(self.result['next'])
122
- yield from self.retrieve(filter)
123
-
124
- def getIDByCultureno(self, culturecolnumber):
125
- ''' Initialize search by culture collection number '''
126
- item = culturecolnumber.strip()
127
- result = self.do_api_call('culturecollectionno/'+str(item))
128
- return result
129
-
130
- def getIDsByTaxonomy(self, genus, species_epithet=None, subspecies_epithet=None):
131
- ''' Initialize search by taxonomic names '''
132
- item = genus.strip()
133
- if species_epithet:
134
- item += "/" + species_epithet
135
- if subspecies_epithet:
136
- item += "/" + subspecies_epithet
137
- result = self.do_api_call("taxon/"+item)
138
- return result
139
-
140
- def getIDsBy16S(self, seq_acc_num):
141
- ''' Initialize search by 16S sequence accession '''
142
- item = seq_acc_num.strip()
143
- result = self.do_api_call('sequence_16s/'+str(item))
144
- return result
145
-
146
- def getIDsByGenome(self, seq_acc_num):
147
- ''' Initialize search by genome sequence accession '''
148
- item = seq_acc_num.strip()
149
- result = self.do_api_call('sequence_genome/'+str(item))
150
- return result
151
-
152
- def search(self, **params):
153
- ''' Initialize search with *one* of the following parameters:
154
-
155
- id -- BacDive-IDs either as a semicolon seperated string or list
156
- taxonomy -- Taxonomic names either as string or list
157
- sequence -- Sequence accession number of unknown type
158
- genome -- Genome sequence accession number
159
- 16s -- 16S sequence accession number
160
- culturecolno -- Culture collection number (mind the space!)
161
- '''
162
- params = list(params.items())
163
- allowed = ['id', 'taxonomy', 'sequence',
164
- 'genome', '16s', 'culturecolno']
165
- if len(params) != 1:
166
- print(
167
- "ERROR: Exacly one parameter is required. Please choose one of the following:")
168
- print(", ".join(allowed))
169
- return 0
170
- querytype, query = params[0]
171
- querytype = querytype.lower()
172
- if querytype not in allowed:
173
- print(
174
- "ERROR: The given query type is not allowed. Please choose one of the following:")
175
- print(", ".join(allowed))
176
- return 0
177
- if querytype == 'id':
178
- if type(query) == type(1):
179
- query = str(query)
180
- if type(query) == type(""):
181
- query = query.split(';')
182
- self.result = {'count': len(query), 'next': None,
183
- 'previous': None, 'results': query}
184
- elif querytype == 'taxonomy':
185
- if type(query) == type(""):
186
- query = [i for i in query.split(" ") if i != "subsp."]
187
- if len(query) > 3:
188
- print("Your query contains more than three taxonomical units.")
189
- print(
190
- "This query supports only genus, species epithet (optional), and subspecies (optional).")
191
- print("They can be defined as list, tuple or string (space separated).")
192
- return 0
193
- self.result = self.getIDsByTaxonomy(*query)
194
- elif querytype == 'sequence':
195
- self.result = self.getIDsByGenome(query)
196
- if self.result['count'] == 0:
197
- self.result = self.getIDsBy16S(query)
198
- elif querytype == 'genome':
199
- self.result = self.getIDsByGenome(query)
200
- elif querytype == '16s':
201
- self.result = self.getIDsBy16S(query)
202
- elif querytype == 'culturecolno':
203
- self.result = self.getIDByCultureno(query)
204
-
205
- if not self.result:
206
- print("ERROR: Something went wrong. Please check your query and try again")
207
- return 0
208
- if not 'count' in self.result:
209
- print("ERROR:", self.result.get("title"))
210
- print(self.result.get("message"))
211
- return 0
212
- if self.result['count'] == 0:
213
- print("Your search did not receive any results.")
214
- return 0
215
- return self.result['count']
216
-
217
-
218
-
219
-
220
- if __name__ == "__main__":
221
- client = BacdiveClient('mail.address@server.example', 'password')
222
-
223
- # the prepare method fetches all BacDive-IDs matching your query
224
- # and returns the number of IDs found
225
- count = client.search(taxonomy='Bacillus subtilis subtilis')
226
- print(count, 'entries found.')
227
-
228
- # The retrieve method lets you iterate over all entries
229
- # and returns the full entry as dict
230
- # Entries can be further filtered using a list of keys (e.g. ['keywords'])
231
- for entry in client.retrieve():
232
- print(entry)
1
+ '''
2
+ This package is for using the BacDive API V2 (2026-02).
3
+ Registration is not required anymore.
4
+ '''
5
+
6
+ from requests.adapters import HTTPAdapter
7
+ from urllib3.util.retry import Retry
8
+ import requests
9
+ import json
10
+ import time
11
+
12
+
13
+ class ReportRetry(Retry):
14
+ ''' Wrapper for retry strategy to report retries'''
15
+ def __init__(self, url=None, *args, **kwargs):
16
+ self.url = url
17
+ self.retry_count = 0
18
+ super().__init__(*args, **kwargs)
19
+
20
+ def increment(self, *args, **kwargs):
21
+ self.retry_count += 1
22
+ print(f"Retrying API request for {self.url}. Attempt number {self.retry_count}.")
23
+ return super().increment(*args, **kwargs)
24
+
25
+
26
+ class BacdiveClient():
27
+ def __init__(self, user=None, password=None, public=True, max_retries=10, retry_delay=50, request_timeout=300):
28
+ ''' Initialize client and authenticate on the server '''
29
+ self.result = {}
30
+ self.public = public
31
+ self.max_retries = max_retries
32
+ self.retry_delay = retry_delay # in seconds
33
+ self.request_timeout = request_timeout # in seconds
34
+
35
+ self.predictions = False
36
+ self.search_type = False
37
+
38
+ client_id = "api.bacdive.public"
39
+ if self.public:
40
+ server_url = "https://sso.dsmz.de/auth/"
41
+ else:
42
+ server_url = "https://sso.dmz.dsmz.de/auth/"
43
+
44
+ def includePredictions(self):
45
+ self.predictions = True
46
+
47
+ def excludePredictions(self):
48
+ self.predictions = False
49
+
50
+ def setSearchType(self, search_type):
51
+ if search_type:
52
+ allowed = ['exact', 'contains', 'startswith', 'endswith']
53
+ if search_type not in allowed:
54
+ print("WARNING - Search Type is not allowed.")
55
+ self.search_type = "exact"
56
+ else:
57
+ self.search_type = search_type
58
+ else:
59
+ self.search_type = False
60
+
61
+ def do_api_call(self, url):
62
+ ''' Initialize API call on given URL and returns result as json '''
63
+ if self.public:
64
+ baseurl = "https://api.bacdive.dsmz.de/v2/"
65
+ else:
66
+ baseurl = "http://api.bacdive-dev.dsmz.local/v2/"
67
+
68
+ if not url.startswith("http"):
69
+ # if base is missing add default:
70
+ url = baseurl + url
71
+ resp = self.do_request(url)
72
+
73
+ if resp.status_code == 500 or resp.status_code == 400 or resp.status_code == 503:
74
+ print(f"Error {resp.status_code}: {resp.content}")
75
+ return json.loads(resp.content)
76
+ elif (resp.status_code == 401):
77
+ msg = json.loads(resp.content)
78
+
79
+ return msg
80
+ else:
81
+ return json.loads(resp.content)
82
+
83
+ def do_request(self, url):
84
+ ''' Perform request'''
85
+ headers = {
86
+ "Accept": "application/json",
87
+ }
88
+
89
+ if self.predictions:
90
+ if "?" in url:
91
+ url += "&predictions=1"
92
+ else:
93
+ url += "?predictions=1"
94
+ # session with retry strategy
95
+ retry_strategy = ReportRetry(
96
+ url=url,
97
+ total=self.max_retries,
98
+ backoff_factor=1, # how much to increase delay between each try
99
+ status_forcelist=[429, 500, 502, 503, 504] # retry on
100
+ )
101
+ adapter = HTTPAdapter(max_retries=retry_strategy)
102
+ http = requests.Session()
103
+ http.mount("https://", adapter)
104
+ http.mount("http://", adapter)
105
+
106
+ resp = http.get(url, headers=headers, timeout=self.request_timeout) # timeout in seconds
107
+ return resp
108
+
109
+ def filterResult(self, d, keys):
110
+ ''' Helper function to filter nested dict by keys '''
111
+ if not isinstance(d, dict):
112
+ yield None
113
+ for k, v in d.items():
114
+ if k in keys:
115
+ yield {k: v}
116
+ if isinstance(v, dict):
117
+ yield from self.filterResult(v, keys)
118
+ elif isinstance(v, list):
119
+ for i in v:
120
+ if isinstance(i, dict):
121
+ yield from self.filterResult(i, keys)
122
+
123
+ def retrieve(self, filter=None):
124
+ ''' Yields all the received entries and does next call if result is incomplete '''
125
+ ids = ";".join([str(i) for i in self.result['results']])
126
+ entries = self.do_api_call('fetch/'+ids)['results'] if ids else []
127
+ for el in entries:
128
+ if isinstance(el, dict):
129
+ entry = el
130
+ el = entry.get("id")
131
+ else:
132
+ entry = entries[el]
133
+ if filter:
134
+ entry = {el: [i for i in self.filterResult(entry, filter)]}
135
+ yield entry
136
+ if self.result['next']:
137
+ self.result = self.do_api_call(self.result['next'])
138
+ yield from self.retrieve(filter)
139
+
140
+ def getIDByCultureno(self, culturecolnumber):
141
+ ''' Initialize search by culture collection number '''
142
+ item = culturecolnumber.strip()
143
+ result = self.do_api_call('culturecollectionno/'+str(item))
144
+ return result
145
+
146
+ def getIDsByTaxonomy(self, genus, species_epithet=None, subspecies_epithet=None):
147
+ ''' Initialize search by taxonomic names '''
148
+ item = genus.strip()
149
+ if species_epithet:
150
+ item += "/" + species_epithet
151
+ if subspecies_epithet:
152
+ item += "/" + subspecies_epithet
153
+ result = self.do_api_call("taxon/"+item)
154
+ return result
155
+
156
+ def getIDsBy16S(self, seq_acc_num):
157
+ ''' Initialize search by 16S sequence accession '''
158
+ item = seq_acc_num.strip()
159
+ result = self.do_api_call('sequence_16s/'+str(item))
160
+ return result
161
+
162
+ def getIDsByGenome(self, seq_acc_num):
163
+ ''' Initialize search by genome sequence accession '''
164
+ item = seq_acc_num.strip()
165
+ result = self.do_api_call('sequence_genome/'+str(item))
166
+ return result
167
+
168
+ def search(self, **params):
169
+ ''' Initialize search with *one* of the following parameters:
170
+
171
+ id -- BacDive-IDs either as a semicolon seperated string or list
172
+ taxonomy -- Taxonomic names either as string or list
173
+ sequence -- Sequence accession number of unknown type
174
+ genome -- Genome sequence accession number
175
+ 16s -- 16S sequence accession number
176
+ culturecolno -- Culture collection number (mind the space!)
177
+ '''
178
+ params = list(params.items())
179
+ allowed = ['id', 'taxonomy', 'sequence',
180
+ 'genome', '16s', 'culturecolno']
181
+ if len(params) != 1:
182
+ print(
183
+ "ERROR: Exacly one parameter is required. Please choose one of the following:")
184
+ print(", ".join(allowed))
185
+ return 0
186
+ querytype, query = params[0]
187
+ querytype = querytype.lower()
188
+ if querytype not in allowed:
189
+ print(
190
+ "ERROR: The given query type is not allowed. Please choose one of the following:")
191
+ print(", ".join(allowed))
192
+ return 0
193
+ if querytype == 'id':
194
+ if type(query) == type(1):
195
+ query = str(query)
196
+ if type(query) == type(""):
197
+ query = query.split(';')
198
+ self.result = {'count': len(query), 'next': None,
199
+ 'previous': None, 'results': query}
200
+ elif querytype == 'taxonomy':
201
+ if type(query) == type(""):
202
+ query = [i for i in query.split(" ") if i != "subsp."]
203
+ if len(query) > 3:
204
+ print("Your query contains more than three taxonomical units.")
205
+ print(
206
+ "This query supports only genus, species epithet (optional), and subspecies (optional).")
207
+ print("They can be defined as list, tuple or string (space separated).")
208
+ return 0
209
+ self.result = self.getIDsByTaxonomy(*query)
210
+ elif querytype == 'sequence':
211
+ query = self.parseSearchTypeQuery(query)
212
+ self.result = self.getIDsByGenome(query)
213
+ if self.result['count'] == 0:
214
+ self.result = self.getIDsBy16S(query)
215
+ elif querytype == 'genome':
216
+ query = self.parseSearchTypeQuery(query)
217
+ self.result = self.getIDsByGenome(query)
218
+ elif querytype == '16s':
219
+ query = self.parseSearchTypeQuery(query)
220
+ self.result = self.getIDsBy16S(query)
221
+ elif querytype == 'culturecolno':
222
+ query = self.parseSearchTypeQuery(query)
223
+ self.result = self.getIDByCultureno(query)
224
+
225
+ if not self.result:
226
+ print("ERROR: Something went wrong. Please check your query and try again")
227
+ return 0
228
+ if not 'count' in self.result:
229
+ print("ERROR:", self.result.get("title"))
230
+ print(self.result.get("message"))
231
+ return 0
232
+ if self.result['count'] == 0:
233
+ print("Your search did not receive any results.")
234
+ return 0
235
+ return self.result['count']
236
+
237
+ def parseSearchTypeQuery(self, query):
238
+ if type(query) == type(1):
239
+ query = str(query)
240
+ if type(query) == type(""):
241
+ query = query.split(';')
242
+
243
+ item = ";".join([ str(i).strip() for i in query ])
244
+
245
+ if self.search_type:
246
+ if "?" in item:
247
+ item += "&search_type=" + self.search_type
248
+ else:
249
+ item += "?search_type=" + self.search_type
250
+
251
+ return item
252
+
253
+
254
+
255
+ if __name__ == "__main__":
256
+ client = BacdiveClient('mail.address@server.example', 'password')
257
+
258
+ # the prepare method fetches all BacDive-IDs matching your query
259
+ # and returns the number of IDs found
260
+ count = client.search(taxonomy='Bacillus subtilis subtilis')
261
+ print(count, 'entries found.')
262
+
263
+ # The retrieve method lets you iterate over all entries
264
+ # and returns the full entry as dict
265
+ # Entries can be further filtered using a list of keys (e.g. ['keywords'])
266
+ for entry in client.retrieve():
267
+ print(entry)
@@ -1,108 +1,146 @@
1
- Metadata-Version: 2.1
2
- Name: bacdive
3
- Version: 0.3.1
4
- Summary: BacDive-API - Programmatic Access to the BacDive Database
5
- Home-page: https://bacdive.dsmz.de/
6
- Author: Julia Koblitz
7
- Author-email: julia.koblitz@dsmz.de
8
- Keywords: microbiology bacteria strains phenotypes
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Intended Audience :: Science/Research
12
- Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
13
- Requires-Python: >=3.6
14
- Description-Content-Type: text/markdown
15
- License-File: LICENSE
16
- Requires-Dist: python-keycloak
17
- Requires-Dist: requests>=2.25.1
18
- Requires-Dist: urllib3>=1.26.5
19
-
20
- # BacDive API
21
-
22
- Using the BacDive API requires registration. Registration is free but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
23
-
24
- Please register [here](https://api.bacdive.dsmz.de/login).
25
-
26
- The Python package can be initialized using your login credentials:
27
-
28
-
29
- ```python
30
- import bacdive
31
-
32
- client = bacdive.BacdiveClient('name@mail.example', 'password')
33
-
34
- # the search method fetches all BacDive-IDs matching your query
35
- # and returns the number of IDs found
36
- count = client.search(taxonomy='Bacillus subtilis subtilis')
37
- print(count, 'strains found.')
38
-
39
- # the retrieve method lets you iterate over all strains
40
- # and returns the full entry as dict
41
- # Entries can be further filtered using a list of keys (e.g. ['keywords'])
42
- for strain in client.retrieve():
43
- print(strain)
44
- ```
45
-
46
- ## Example queries:
47
-
48
- ```python
49
- # Search by BacDive-IDs (either semicolon separated or as list):
50
- query = {"id": 24493}
51
- query = {"id": "24493;12;132485"}
52
- query = {"id": [24493, 12, 132485]}
53
-
54
- # Search by culture collection number
55
- query = {"culturecolno": "DSM 26640"}
56
-
57
- # Search by taxonomy (either as full name or as list):
58
- # With genus name, species epithet (optional), and subspecies (optional).
59
- query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
60
- query = {"taxonomy": ("Escherichia", "coli")}
61
-
62
- # Search by sequence accession numbers:
63
- query = {"16s": "AF000162"} # 16S sequence
64
- query = {"genome": "GCA_006094295"} # genome sequence
65
-
66
- # run query
67
- client.search(**query)
68
- ```
69
-
70
- ## Filtering
71
-
72
- Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
73
-
74
- ```python
75
- filter=['keywords', 'culture collection no.']
76
- result = client.retrieve(filter)
77
- print({k:v for x in result for k,v in x.items()})
78
- ```
79
-
80
- The printed result will look like this:
81
-
82
- ```python
83
- {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
84
- {'culture collection no.': 'DSM 4393, pC194, SB202'}],
85
- '1162': [{'keywords': ['human pathogen', 'Bacteria']},
86
- {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
87
- 'pUB110'}],
88
- '1163': [{'keywords': ['human pathogen', 'Bacteria']},
89
- {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
90
- '1164': [{'keywords': 'Bacteria'},
91
- {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
92
- ...
93
- ```
94
-
95
- ## New in v0.3
96
-
97
- We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
98
-
99
- ```python
100
- client.includePredictions()
101
- ```
102
-
103
- You can exclude predictions again by calling:
104
-
105
- ```python
106
- client.excludePredictions()
107
- ```
108
-
1
+ Metadata-Version: 2.4
2
+ Name: bacdive
3
+ Version: 2.0.0
4
+ Summary: BacDive-API - Programmatic Access to the BacDive Database
5
+ Home-page: https://bacdive.dsmz.de/
6
+ Author: Julia Koblitz
7
+ Author-email: julia.koblitz@dsmz.de
8
+ Keywords: microbiology bacteria strains phenotypes
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
13
+ Requires-Python: >=3.6
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: requests>=2.25.1
17
+ Requires-Dist: urllib3>=1.26.5
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: keywords
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: requires-python
28
+ Dynamic: summary
29
+
30
+ # BacDive API v2
31
+
32
+ Using the BacDive API does not require registration anymore, but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
33
+
34
+ ```python
35
+ import bacdive
36
+
37
+ client = bacdive.BacdiveClient()
38
+
39
+ # [optional] You may define the search type as one of the following:
40
+ # 'exact' (default), 'contains', 'startswith', 'endswith'
41
+ client.setSearchType('exact')
42
+
43
+ # The search method fetches all BacDive-IDs matching your query
44
+ # and returns the number of IDs found
45
+ count = client.search(taxonomy='Bacillus subtilis subtilis')
46
+ print(count, 'strains found.')
47
+
48
+ # the retrieve method lets you iterate over all strains
49
+ # and returns the full entry as dict
50
+ # Entries can be further filtered using a list of keys (e.g. ['keywords'])
51
+ for strain in client.retrieve():
52
+ print(strain)
53
+ ```
54
+
55
+ ## Example queries:
56
+
57
+ ```python
58
+ # Search by BacDive-IDs (either semicolon separated or as list):
59
+ query = {"id": 24493}
60
+ query = {"id": "24493;12;132485"}
61
+ query = {"id": [24493, 12, 132485]}
62
+
63
+ # Search by culture collection number
64
+ query = {"culturecolno": "DSM 26640"}
65
+ # New in v1.0: Search by culture collection number with multiple numbers:
66
+ query = {"culturecolno": ["DSM 26640", "DSM 26646"]}
67
+ query = {"culturecolno": "DSM 26640;DSM 26646"} # semicolon may be used as separator
68
+
69
+ # Search by culture collection number with search type 'startswith':
70
+ client.setSearchType('startswith')
71
+ query = {"culturecolno": "DSM"}
72
+
73
+ # Search by taxonomy (either as full name or as list):
74
+ # With genus name, species epithet (optional), and subspecies (optional).
75
+ query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
76
+ query = {"taxonomy": ("Escherichia", "coli")}
77
+
78
+ # Search by 16S sequence accession numbers:
79
+ query = {"16s": "AF000162"}
80
+ # New in v1.0: Search by 16S sequence with multiple sequence accession numbers:
81
+ query = {"16s": ["AB681963", "JN566021", "AY027686"]}
82
+ # New in v1.0: Search by 16S sequence with search type 'startswith':
83
+ client.setSearchType('startswith')
84
+ query = {"16s": "AB"}
85
+
86
+ # Search by genome sequence accession numbers:
87
+ query = {"genome": "GCA_006094295"}
88
+ # New in v1.0: Search by genome sequence with multiple sequence accession numbers:
89
+ query = {"genome": ["GCA_003332855", "GCA_024623325", "GCA_017377855"]}
90
+ # New in v1.0: Search by genome sequence with search type 'startswith':
91
+ client.setSearchType('startswith')
92
+ query = {"genome": "DSM"}
93
+
94
+
95
+ # run query
96
+ client.search(**query)
97
+ ```
98
+
99
+ ## Filtering
100
+
101
+ Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
102
+
103
+ ```python
104
+ filter=['keywords', 'culture collection no.']
105
+ result = client.retrieve(filter)
106
+ print({k:v for x in result for k,v in x.items()})
107
+ ```
108
+
109
+ The printed result will look like this:
110
+
111
+ ```python
112
+ {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
113
+ {'culture collection no.': 'DSM 4393, pC194, SB202'}],
114
+ '1162': [{'keywords': ['human pathogen', 'Bacteria']},
115
+ {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
116
+ 'pUB110'}],
117
+ '1163': [{'keywords': ['human pathogen', 'Bacteria']},
118
+ {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
119
+ '1164': [{'keywords': 'Bacteria'},
120
+ {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
121
+ ...
122
+ ```
123
+
124
+ ## Hints for more advanced queries
125
+ If you have more advanced queries that are currently not covered by the API, we recommend you to use the [Bac*Dive* Advanced Search](https://bacdive.dsmz.de/advsearch), which is very flexible and powerful. You can then download the resulting table as CSV (button at the top right), import the CSV into your Python script, and use the BacDive-IDs to download all relevant information via the API.
126
+
127
+
128
+ ## New in v0.3
129
+
130
+ We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
131
+
132
+ ```python
133
+ client.includePredictions()
134
+ ```
135
+
136
+ You can exclude predictions again by calling:
137
+
138
+ ```python
139
+ client.excludePredictions()
140
+ ```
141
+
142
+ ## New in v1.0
143
+
144
+ Thanks to [phenolophthaleinum](https://github.com/phenolophthaleinum) for improving the error handling and Joaquim Sardá for improving the BacDive-API and adding new search possibilities.
145
+
146
+ Examples for search type definitions and array requests are included in the examples above.
@@ -1,3 +1,2 @@
1
- python-keycloak
2
1
  requests>=2.25.1
3
2
  urllib3>=1.26.5
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="bacdive",
8
- version="0.3.1",
8
+ version="2.0.0",
9
9
  description="BacDive-API - Programmatic Access to the BacDive Database",
10
10
  long_description=long_description,
11
11
  long_description_content_type="text/markdown",
@@ -22,7 +22,6 @@ setuptools.setup(
22
22
  ],
23
23
  keywords="microbiology bacteria strains phenotypes",
24
24
  install_requires=[
25
- "python-keycloak",
26
25
  "requests>=2.25.1",
27
26
  "urllib3>=1.26.5"
28
27
  ]
bacdive-0.3.1/README.md DELETED
@@ -1,89 +0,0 @@
1
- # BacDive API
2
-
3
- Using the BacDive API requires registration. Registration is free but the usage of BacDive data is only permitted when in compliance with the BacDive terms of use. See [About BacDive](https://bacdive.dsmz.de/about) for details.
4
-
5
- Please register [here](https://api.bacdive.dsmz.de/login).
6
-
7
- The Python package can be initialized using your login credentials:
8
-
9
-
10
- ```python
11
- import bacdive
12
-
13
- client = bacdive.BacdiveClient('name@mail.example', 'password')
14
-
15
- # the search method fetches all BacDive-IDs matching your query
16
- # and returns the number of IDs found
17
- count = client.search(taxonomy='Bacillus subtilis subtilis')
18
- print(count, 'strains found.')
19
-
20
- # the retrieve method lets you iterate over all strains
21
- # and returns the full entry as dict
22
- # Entries can be further filtered using a list of keys (e.g. ['keywords'])
23
- for strain in client.retrieve():
24
- print(strain)
25
- ```
26
-
27
- ## Example queries:
28
-
29
- ```python
30
- # Search by BacDive-IDs (either semicolon separated or as list):
31
- query = {"id": 24493}
32
- query = {"id": "24493;12;132485"}
33
- query = {"id": [24493, 12, 132485]}
34
-
35
- # Search by culture collection number
36
- query = {"culturecolno": "DSM 26640"}
37
-
38
- # Search by taxonomy (either as full name or as list):
39
- # With genus name, species epithet (optional), and subspecies (optional).
40
- query = {"taxonomy": "Bacillus subtilis subsp. subtilis"}
41
- query = {"taxonomy": ("Escherichia", "coli")}
42
-
43
- # Search by sequence accession numbers:
44
- query = {"16s": "AF000162"} # 16S sequence
45
- query = {"genome": "GCA_006094295"} # genome sequence
46
-
47
- # run query
48
- client.search(**query)
49
- ```
50
-
51
- ## Filtering
52
-
53
- Results from the `retrieve` Method of both clients can be further filtered. The result contains a list of matched keyword dicts:
54
-
55
- ```python
56
- filter=['keywords', 'culture collection no.']
57
- result = client.retrieve(filter)
58
- print({k:v for x in result for k,v in x.items()})
59
- ```
60
-
61
- The printed result will look like this:
62
-
63
- ```python
64
- {'1161': [{'keywords': ['human pathogen', 'Bacteria']},
65
- {'culture collection no.': 'DSM 4393, pC194, SB202'}],
66
- '1162': [{'keywords': ['human pathogen', 'Bacteria']},
67
- {'culture collection no.': 'DSM 4514, ATCC 37015, BD170, NCIB 11624, '
68
- 'pUB110'}],
69
- '1163': [{'keywords': ['human pathogen', 'Bacteria']},
70
- {'culture collection no.': 'DSM 4554, ATCC 37128, BGSC 1E18, pE194'}],
71
- '1164': [{'keywords': 'Bacteria'},
72
- {'culture collection no.': 'DSM 4750, 1E7, BGSC 1E7, pE194-cop6'}],
73
- ...
74
- ```
75
-
76
- ## New in v0.3
77
-
78
- We added AI-based predictions to the Bac*Dive* database. Predicted traits are excluded by default. To include them, you have to call the method `includePredictions()`:
79
-
80
- ```python
81
- client.includePredictions()
82
- ```
83
-
84
- You can exclude predictions again by calling:
85
-
86
- ```python
87
- client.excludePredictions()
88
- ```
89
-
File without changes
File without changes
File without changes