├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── rxclass_api
├── RxAPIWrapper.py
├── RxClassHelpers.py
└── __init__.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.p
3 | *.idea
4 | /__pycache__
5 | *.pyc
6 | /dist/
7 | /*.egg-info
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ianphorsman@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Ian Horsman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
RxClass API Wrapper with Helper Utility
2 |
3 | Getting Started
4 |
5 | This project contains a python wrapper for the RxClass API as well as a set of helper functions to
6 | obtain useful information stripped of unimportant data.
7 |
8 | Helper Functions
9 |
10 | `RxClassHelpers` contains several high level helper functions to acquaint yourself with
11 | what the RxClass API can (and can't) do. Just create an instance to get started.
12 | Warning: there are a lot of false negative results.
13 |
14 |
15 | ```
16 |
17 | pip install rxclass
18 |
19 | ```
20 |
21 | ```python
22 |
23 | # To access API helpers:
24 |
25 | from rxclass_api import RxClassHelpers
26 |
27 | # To access API wrapper functions directly:
28 | from rxclass_api import RxAPIWrapper
29 |
30 | # To start using available helper functions create a helper instance.
31 |
32 | helper = RxClassHelpers()
33 |
34 | ```
35 |
36 | Supplying a with statement will also automatically load and save gathered data given a filename.
37 |
38 | ```python
39 |
40 | helper = RxClassHelpers(filename='data')
41 | with helper:
42 | ...
43 |
44 | ```
45 |
46 | Obtaining Class's Id and Type
47 | Unique identifiers are represented as classId(s). Every class in RxClass has a classId and classType.
48 |
49 | ```python
50 |
51 | helper.get_class_by_name('fluoxetine')
52 |
53 | #=>
54 | {'classId': 'N0000007101',
55 | 'className': 'Fluoxetine',
56 | 'classType': 'CHEM'}
57 |
58 | helper.get_class_by_name('SSRI')
59 |
60 | #=>
61 | {'classId': 'N0000175696',
62 | 'className': 'Serotonin Reuptake Inhibitor',
63 | 'classType': 'EPC'}
64 |
65 | helper.get_class_by_name('Drug Hypersensitivity')
66 |
67 | #=>
68 | {'classId': 'N0000000999',
69 | 'className': 'Drug Hypersensitivity',
70 | 'classType': 'DISEASE'}
71 |
72 | ```
73 |
74 | Class Types
75 | List all the class types with descriptions.
76 |
77 | ```python
78 |
79 | helper.list_class_types()
80 |
81 | #=>
82 | ['MESHPA = MeSH Pharmacological Actions',
83 | 'VA = Class',
84 | 'PK = Pharmacokinetics',
85 | 'EPC = Established Pharmacological Classes',
86 | 'DISEASE = Indication / Condition / Disease',
87 | 'ATC1-4 = Anatomical Therapeutic Chemical',
88 | 'CHEM = Chemical Name',
89 | 'MOA = Mechanism of Action',
90 | 'PE = Physiological Effect']
91 |
92 | ```
93 |
94 | Drug Indications
95 |
96 | ```python
97 |
98 | helper.indications('bupropion')
99 |
100 | #=>
101 | ['Anorexia',
102 | 'Attention Deficit Disorder with Hyperactivity',
103 | 'Bulimia',
104 | 'Depressive Disorder',
105 | 'Drug Hypersensitivity',
106 | 'Seizures',
107 | 'Substance Withdrawal Syndrome',
108 | 'Tobacco Use Disorder']
109 |
110 | helper.indications('azithromycin')
111 |
112 | #=>
113 | ['Chlamydia Infections',
114 | 'Endocarditis, Bacterial',
115 | 'Haemophilus Infections',
116 | 'Hypersensitivity',
117 | 'Liver Diseases',
118 | 'Mycobacterium Infections, Nontuberculous',
119 | 'Neisseriaceae Infections',
120 | 'Otitis Media',
121 | 'Pharyngitis',
122 | 'Pneumonia, Bacterial',
123 | 'Pneumonia, Mycoplasma',
124 | 'Respiratory Tract Infections',
125 | 'Sexually Transmitted Diseases, Bacterial',
126 | 'Skin Diseases, Infectious',
127 | 'Staphylococcal Infections',
128 | 'Streptococcal Infections',
129 | 'Tonsillitis',
130 | 'Urethritis']
131 |
132 |
133 | ```
134 |
135 | Drug's Mechanism of Action
136 |
137 | ```python
138 |
139 | helper.mechanism_of_action('marijuana') # 'marijuana' will not be in database
140 |
141 | #=>
142 | []
143 |
144 | helper.mechanism_of_action('tetrahydrocannabinol') # chemical name will be found though
145 |
146 | #=>
147 | ['Cannabinoid Receptor Agonists']
148 |
149 | helper.similarly_acting_drugs('fluoxetine')
150 |
151 | #=>
152 | [('Serotonin Uptake Inhibitors',
153 | ['Citalopram',
154 | 'Desvenlafaxine',
155 | 'duloxetine',
156 | 'Escitalopram',
157 | 'Fluoxetine',
158 | 'Fluvoxamine',
159 | 'levomilnacipran',
160 | 'milnacipran',
161 | 'Paroxetine',
162 | 'Sertraline',
163 | 'venlafaxine']),
164 | ('Monoamine Oxidase Inhibitors',
165 | ['Isocarboxazid',
166 | 'Phenelzine',
167 | 'rasagiline',
168 | 'safinamide',
169 | 'Selegiline',
170 | 'Tranylcypromine'])]
171 |
172 | ```
173 |
174 | Drug's Physiological Effect
175 |
176 | ```python
177 |
178 | helper.physiological_effect('aripiprazole')
179 |
180 | #=>
181 | ['Decreased Dopamine Activity', 'Decreased Serotonin Activity']
182 |
183 | helper.drugs_with_similar_physiological_response('ibuprofen')
184 |
185 | #=>
186 | [('Decreased Platelet Activating Factor Production', None),
187 | ('Decreased Prostaglandin Production', ['Aspirin', 'Diclofenac']),
188 | ('Decreased Thromboxane Production', None)]
189 |
190 | helper.drugs_with_similar_physiological_response('aripiprazole') # will yield a false negative
191 |
192 | #=>
193 | [('Decreased Dopamine Activity', None),
194 | ('Decreased Serotonin Activity', None)]
195 |
196 | helper.drugs_with_physiological_effect('Decreased Dopamine Activity') # but this works
197 |
198 | #=>
199 | ('Decreased Dopamine Activity',
200 | ['acetophenazine',
201 | 'aripiprazole',
202 | 'Chlorpromazine',
203 | 'Chlorprothixene',
204 | 'Clozapine',
205 | 'deutetrabenazine',
206 | 'Fluphenazine',
207 | 'Haloperidol',
208 | 'Mesoridazine',
209 | ...])
210 |
211 | ```
212 |
213 | Drug's Pharmacokinetics
214 |
215 | ```python
216 |
217 | helper.pharmacokinetics('ibuprofen')
218 |
219 | #=>
220 | ['Hepatic Metabolism', 'Renal Excretion']
221 |
222 | helper.drugs_with_similar_pharmacokinetics('ibuprofen')
223 |
224 | #=>
225 | [('Drugs processed via Renal Excretion',
226 | ['Acetaminophen',
227 | 'Albuterol',
228 | 'Alprazolam',
229 | 'Amoxicillin',
230 | 'Aspirin',
231 | 'Atenolol',
232 | ...]),
233 | ('Drugs processed via Hepatic Metabolism',
234 | ['Acetaminophen',
235 | 'Albuterol',
236 | 'Aspirin',
237 | 'atorvastatin',
238 | ...])]
239 |
240 | helper.drugs_with_pharmacokinetics('Hepatic Metabolism')
241 |
242 | #=>
243 | ('Hepatic Metabolism',
244 | ['Acetaminophen',
245 | 'Albuterol',
246 | 'Aspirin',
247 | 'atorvastatin',
248 | 'celecoxib',
249 | 'Codeine',
250 | ...])
251 |
252 | ```
253 |
254 | Therapeutic Class
255 |
256 | ```python
257 |
258 | helper.therapeutic_class('azithromycin')
259 |
260 | #=>
261 | ['Antibiotics', 'Macrolides']
262 |
263 | helper.therapeutic_class('budesonide')
264 |
265 | #=>
266 | ['Adrenergics in combination with corticosteroids or other drugs, excl. '
267 | 'anticholinergics',
268 | 'Corticosteroids',
269 | 'Corticosteroids acting locally',
270 | 'Corticosteroids, potent (group III)',
271 | 'Glucocorticoids']
272 |
273 | ```
274 |
275 | Drug Type
276 |
277 | ```python
278 |
279 | helper.drug_type('azithromycin')
280 |
281 | #=>
282 | ['ANTIBACTERIALS,TOPICAL OPHTHALMIC', 'ERYTHROMYCINS/MACROLIDES']
283 |
284 | helper.drug_type('budesonide')
285 |
286 | #=>
287 | ['ANTI-INFLAMMATORIES,INHALATION',
288 | 'ANTI-INFLAMMATORIES,NASAL',
289 | 'ANTIASTHMA,OTHER',
290 | 'GLUCOCORTICOIDS']
291 |
292 | ```
293 |
294 | Class information of a given drug.
295 |
296 | ```python
297 |
298 | helper.drug_info('ketamine')
299 |
300 | #=>
301 | {'Drug Type': ['GENERAL ANESTHETICS,OTHER'],
302 | 'Indications': ['Aneurysm',
303 | 'Angina Pectoris',
304 | 'Burns',
305 | 'Drug Hypersensitivity',
306 | 'Heart Failure',
307 | 'Hypertension',
308 | 'Intracranial Hypertension',
309 | 'Pain',
310 | 'Psychotic Disorders',
311 | 'Thyrotoxicosis',
312 | 'Unconsciousness'],
313 | 'Mechanism of Action': [],
314 | 'Name': 'Ketamine Hydrochloride',
315 | 'Pharmacokinetics': None,
316 | 'Physiological Effects': ['Blood Pressure Alteration',
317 | 'Decreased Cerebral Cortex Organized Electrical '
318 | 'Activity',
319 | 'Decreased Midbrain Organized Electrical Activity',
320 | 'Decreased Sensory-Somatic Nervous System '
321 | 'Organized Electrical Activity',
322 | 'General Anesthesia',
323 | 'Increased Epinephrine Activity',
324 | 'Increased Norepinephrine Activity'],
325 | 'Therapeutic Class': ['Other general anesthetics']}
326 |
327 | ```
328 |
329 | Drugs that can induce a reaction or condition.
330 |
331 | ```python
332 |
333 | helper.drug_induces(vomiting')
334 |
335 | #=>
336 | ('Drugs that induce vomiting',
337 | ['Disulfiram', 'ethyl ether', 'Ipecac', 'Nitrous Oxide'])
338 |
339 | helper.drug_induces(seizure disorder')
340 |
341 | #=>
342 | ('Drugs that induce seizure disorder', ['Pentylenetetrazole'])
343 |
344 | ```
345 |
346 | Drugs that may prevent a condition or acute reaction.
347 |
348 | ```python
349 |
350 | helper.drugs_that_may('prevent', 'seizure disorder')
351 |
352 | #=>
353 | ('Drugs that may prevent seizure disorder',
354 | ['fosphenytoin', 'Magnesium Sulfate', 'Phenytoin', 'Thiamylal'])
355 |
356 | helper.drugs_that_may('prevent', 'dementia')
357 |
358 | #=>
359 | ('Drugs that may prevent alzheimer disease', ['Vitamin E'])
360 |
361 | ```
362 |
363 | Drugs that may treat a condition or acute response.
364 |
365 | ```python
366 |
367 | helper.drugs_that_may('treat', 'seizures')
368 |
369 | #=>
370 | ('Drugs that may treat seizure disorder',
371 | ['Acetazolamide',
372 | 'Amobarbital',
373 | 'Brivaracetam',
374 | 'Carbamazepine',
375 | 'clobazam',
376 | 'Clonazepam',
377 | 'clorazepate',
378 | 'Corticotropin',
379 | 'Diazepam',
380 | 'Ethosuximide',
381 | 'Ethotoin',
382 | 'Etomidate',
383 | 'ezogabine',
384 | 'felbamate',
385 | 'fosphenytoin',
386 | 'gabapentin',
387 | ...])
388 |
389 | helper.drugs_that_may('treat', 'depressive disorder')
390 |
391 | #=>
392 | ('Drugs that may treat depressive disorder',
393 | ['Alprazolam',
394 | 'Amitriptyline',
395 | 'Amoxapine',
396 | 'brexpiprazole',
397 | 'Bupropion',
398 | 'Buspirone',
399 | 'Citalopram',
400 | 'Clomipramine',
401 | 'Desipramine',
402 | 'Desvenlafaxine',
403 | 'Doxepin',
404 | 'duloxetine',
405 | 'Escitalopram',
406 | 'Fluoxetine',
407 | 'Fluvoxamine',
408 | 'Imipramine',
409 | 'Isocarboxazid',
410 | 'Isoflurane',
411 | 'Kava preparation',
412 | 'levomilnacipran',
413 | 'Lithium',
414 | 'Lorazepam',
415 | 'lurasidone',
416 | 'Maprotiline',
417 | 'Melatonin',
418 | 'Methylphenidate',
419 | 'milnacipran',
420 | ...])
421 |
422 | ```
423 |
424 | Drugs that can diagnose a condition.
425 |
426 | ```python
427 |
428 | helper.drugs_that_may('diagnose', '')
429 |
430 | #=> # yet to find a condition name that yields results from NDFRT database
431 |
432 | ```
433 |
434 | Contraindications
435 | Not as straightforward. Supply 'with' instead of 'DISEASE'.
436 |
437 | ```python
438 |
439 | helper.contraindications('with', 'Drug Hypersensitivity')
440 |
441 | #=>
442 | ('Drug Hypersensitivity contraindications',
443 | ['17-alpha-Hydroxyprogesterone',
444 | '4-Aminobenzoic Acid',
445 | 'abacavir',
446 | 'abciximab',
447 | 'Acarbose',
448 | 'Acebutolol',
449 | 'acemannan',
450 | 'Acetaminophen',
451 | 'Acetazolamide',
452 | 'Acetic Acid',
453 | ...])
454 |
455 | helper.contraindications('with', 'bulimia')
456 |
457 | #=>
458 | ('bulimia contraindications', ['Bupropion'])
459 |
460 | helper.contraindications('with', 'schizophrenia')
461 |
462 | #=>
463 | ('schizophrenia contraindications',
464 | ['Fenfluramine', 'Ginseng Preparation', 'Tetrahydrocannabinol'])
465 |
466 | ```
467 |
468 | Class Subtypes
469 | Use of this function appears to be limited currently.
470 |
471 | ```python
472 |
473 | helper.subtypes('')
474 |
475 | #=> # Let me know if you find a valid use case that works.
476 |
477 | ```
478 |
479 | Spelling Suggestions
480 |
481 | ```python
482 |
483 | helper.class_name_suggestions('amines')
484 |
485 | #=>
486 | ['amines',
487 | 'amides',
488 | 'diamines',
489 | 'azides',
490 | 'amidines',
491 | 'acids',
492 | 'ascites',
493 | 'apnea',
494 | 'anions',
495 | 'amnesia']
496 |
497 | helper.class_name_suggestions('oxetine', only_drugs=True) # returns only drug names
498 |
499 | #=>
500 | ['fluoxetine',
501 | 'paroxetine',
502 | 'reboxetine',
503 | 'duloxetine',
504 | 'oxypertine',
505 | 'oxerutins',
506 | 'oxetorone']
507 |
508 | ```
509 |
510 |
511 | Statement of Credit
512 |
513 | This product uses publicly available data from the U.S. National Library of Medicine (NLM), National Institutes of Health, Department of Health and Human Services; NLM is not responsible for the product and does not endorse or recommend this or any other product."
514 |
--------------------------------------------------------------------------------
/rxclass_api/RxAPIWrapper.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import json
3 | from pprint import pprint as pp
4 | from functools import reduce
5 |
6 | class RxAPIWrapper(object):
7 |
8 | def __init__(self):
9 | self.base_uri_interactions = 'https://rxnav.nlm.nih.gov/REST/interaction'
10 | self.base_uri_class = 'https://rxnav.nlm.nih.gov/REST/rxclass'
11 | self.base_uri_norm = 'https://rxnav.nlm.nih.gov/REST'
12 | self.drug_class_soaps = []
13 |
14 | def make_request(api_url_getter):
15 | def wrapper(self, *args):
16 | return requests.get(api_url_getter(self, *args)).json()
17 | return wrapper
18 |
19 | def sanitize(self, opts):
20 | if type(opts) is dict:
21 | return reduce(lambda acc, i: acc + "&{}={}".format(i[0], i[1]), opts.items(), "")
22 | return ''
23 |
24 | @make_request
25 | def get_interaction_uri(self):
26 | return self.base_uri_class + '/interaction.json?rxcui=' + str(341248)
27 |
28 | @make_request
29 | def find_class_by_id(self, drug_class_id):
30 | return self.base_uri_class + "/class/byId.json?classId={}".format(drug_class_id)
31 |
32 | @make_request
33 | def find_class_by_name(self, name):
34 | return self.base_uri_class + "/class/byName.json?className={}".format(name)
35 |
36 | @make_request
37 | def find_class_by_drug_name(self, drug_name, opts=None):
38 | return self.base_uri_class + "/class/byDrugName.json?drugName={}".format(drug_name) + self.sanitize(opts)
39 |
40 | @make_request
41 | def find_similar_classes_by_class(self, class_id, opts=None):
42 | return self.base_uri_class + "/class/similar.json?classId={}".format(class_id) + self.sanitize(opts)
43 |
44 | @make_request
45 | def find_similar_classes_by_drug_list(self, drug_ids, opts=None):
46 | return self.base_uri_class + "/class/similarByRxcuis?rxcuis={}".format(drug_ids) + self.sanitize(opts)
47 |
48 | @make_request
49 | def get_all_classes(self, class_types=None):
50 | return self.base_uri_class + "/allClasses.json" + self.sanitize(class_types)
51 |
52 | @make_request
53 | def get_class_contexts(self, class_id):
54 | return self.base_uri_class + "/classContext.json?classId={}".format(class_id)
55 |
56 | @make_request
57 | def get_class_graph(self, class_id):
58 | return self.base_uri_class + "/classGraph.json?classId={}".format(class_id)
59 |
60 | @make_request
61 | def get_class_members(self, class_id, opts=None):
62 | return self.base_uri_class + "/classMembers.json?classId={}".format(class_id) + self.sanitize(opts)
63 |
64 | @make_request
65 | def get_class_tree(self, class_id):
66 | return self.base_uri_class + "/classTree.json?classId={}".format(class_id)
67 |
68 | @make_request
69 | def get_class_types(self):
70 | return self.base_uri_class + "/classTypes.json"
71 |
72 | @make_request
73 | def get_relationships(self, rela_source):
74 | return self.base_uri_class + "/relas.json?relaSource={}".format(rela_source)
75 |
76 | @make_request
77 | def compare_classes(self, class_id_1, opts=None):
78 | return self.base_uri_class + "/similarInfo.json?" + self.sanitize(opts)[1:]
79 |
80 | @make_request
81 | def get_sources_of_drug_class_relations(self):
82 | return self.base_uri_class + "/relaSources.json"
83 |
84 | @make_request
85 | def get_spelling_suggestions(self, term, type_of_name):
86 | return self.base_uri_class + "/spellingsuggestions.json?term={}&type={}".format(term, type_of_name)
87 |
88 | def save(self):
89 | pp(json.dumps(self.req.json(), "/req_1.json"))
90 |
91 |
--------------------------------------------------------------------------------
/rxclass_api/RxClassHelpers.py:
--------------------------------------------------------------------------------
1 | import _pickle as picklerick
2 | import os
3 | from collections import Counter
4 | from functools import reduce
5 |
6 | from .RxAPIWrapper import RxAPIWrapper
7 |
8 |
9 | class RxClassHelpers(object):
10 |
11 | def __init__(self, save_memo=True, filename="rxclass_data"):
12 | self.save_memo = save_memo
13 | self.memo = {}
14 | self.filename = filename
15 | self.api = RxAPIWrapper()
16 | self.drug_class_types = {
17 | 'VA': 'Class',
18 | 'MOA': 'Mechanism of Action',
19 | 'PK': 'Pharmacokinetics',
20 | 'PE': 'Physiological Effect',
21 | 'CHEM': 'Chemical Name',
22 | 'MESHPA': 'MeSH Pharmacological Actions',
23 | 'EPC': 'Established Pharmacological Classes',
24 | 'ATC1-4': 'Anatomical Therapeutic Chemical',
25 | 'DISEASE': 'Indication / Condition / Disease'
26 | }
27 |
28 | def memo(func):
29 | def wrapper(self, drug_name):
30 | if drug_name not in self.memo:
31 | self.get_class_data_of_drug(drug_name)
32 | return func(self, drug_name)
33 | return wrapper
34 |
35 |
36 | def get_class_data_of_drug(self, drug_name):
37 | ret = self.api.find_class_by_drug_name(drug_name)
38 | if 'rxclassDrugInfoList' not in ret:
39 | error = "{} not found in database".format(drug_name)
40 | self.memo[drug_name] = [error]
41 | return error
42 | classes = {
43 | (source['rxclassMinConceptItem']['className'],
44 | source['rxclassMinConceptItem']['classType'],
45 | source['rxclassMinConceptItem']['classId'])
46 | for source in ret['rxclassDrugInfoList']['rxclassDrugInfo']
47 | }
48 | arranged_classes = {}
49 | for tup in classes:
50 | if tup[1] not in arranged_classes:
51 | arranged_classes[tup[1]] = [(tup[2], tup[0])]
52 | else:
53 | arranged_classes[tup[1]].append((tup[2], tup[0]))
54 | self.memo[drug_name] = arranged_classes
55 | return arranged_classes
56 |
57 |
58 | @memo
59 | def similarly_acting_drugs(self, drug_name):
60 | if 'MOA' not in self.memo[drug_name]:
61 | return "{} has no recorded mechanism of action.".format(drug_name)
62 | pairs = self.memo[drug_name]['MOA']
63 | moa_data = []
64 | def get_similar(id, name):
65 | opts = {
66 | 'relaSource': 'DAILYMED',
67 | 'rela': "has_{}".format('MOA')
68 | }
69 | ret = self.api.get_class_members(id, opts)
70 | if 'drugMemberGroup' not in ret:
71 | return name, None
72 | return name, [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
73 | for moa_id, moa_name in pairs:
74 | moa_data.append(get_similar(moa_id, moa_name))
75 | return moa_data
76 |
77 | def contraindications(self, rela, class_name):
78 | ret = self.get_class_by_name(class_name)
79 | if 'classId' not in ret:
80 | return "{} not found in database.".format(class_name)
81 | class_type_id = ret['classId']
82 | opts = {
83 | 'relaSource': 'NDFRT',
84 | 'rela': "CI_{}".format(rela)
85 | }
86 | ret = self.api.get_class_members(class_type_id, opts)
87 | title = "{} contraindications".format(class_name)
88 | if 'drugMemberGroup' not in ret:
89 | return title, None
90 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
91 | return title, drug_names
92 |
93 | def drug_induces(self, disease):
94 | ret = self.get_class_by_name(disease)
95 | if 'classId' not in ret:
96 | return "{} not found in database.".format(disease)
97 | disease_id = ret['classId']
98 | opts = {
99 | 'relaSource': 'NDFRT',
100 | 'rela': 'induces'
101 | }
102 | ret = self.api.get_class_members(disease_id, opts)
103 | title = "Drugs that induce {}".format(disease)
104 | if 'drugMemberGroup' not in ret:
105 | return title, None
106 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
107 | return title, drug_names
108 |
109 | def drugs_that_may(self, action, disease):
110 | ret = self.get_class_by_name(disease)
111 | if 'classId' not in ret:
112 | return "{} not found in database.".format(disease)
113 | disease_id = ret['classId']
114 | opts = {
115 | 'relaSource': 'NDFRT',
116 | 'rela': "may_{}".format(action)# action items allowed: prevent, diagnose, treat
117 | }
118 | ret = self.api.get_class_members(disease_id, opts)
119 | title = "Drugs that may {} {}".format(action, disease)
120 | if 'drugMemberGroup' not in ret:
121 | return title, None
122 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
123 | return title, drug_names
124 |
125 | @memo
126 | def drugs_with_similar_physiological_response(self, drug_name):
127 | if not 'PE' in self.memo[drug_name]:
128 | return "{} does not have a recorded physiological response or was not found in database.".format(drug_name)
129 | pairs = self.memo[drug_name]['PE']
130 | pe_data = []
131 | def get_similar(id, name):
132 | opts = {
133 | 'relaSource': 'DAILYMED',
134 | 'rela': 'has_PE'
135 | }
136 | ret = self.api.get_class_members(id, opts)
137 | if 'drugMemberGroup' not in ret:
138 | return name, None
139 | return name, [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
140 | for pe_id, pe_name in pairs:
141 | pe_data.append(get_similar(pe_id, pe_name))
142 | return pe_data
143 |
144 | def drugs_with_physiological_effect(self, effect):
145 | ret = self.get_class_by_name(effect)
146 | if 'classId' not in ret:
147 | return "{} not found in database.".format(effect)
148 | effect_id = ret['classId']
149 | opts = {
150 | 'relaSource': 'NDFRT',
151 | 'rela': 'has_PE'
152 | }
153 | ret = self.api.get_class_members(effect_id, opts)
154 | if 'drugMemberGroup' not in ret:
155 | return effect, None
156 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
157 | return effect, drug_names
158 |
159 | @memo
160 | def drugs_with_similar_pharmacokinetics(self, drug_name):
161 | pairs = self.memo[drug_name]['PK']
162 | pe_data = []
163 | def get_similar(id, name):
164 | opts = {
165 | 'relaSource': 'NDFRT',
166 | 'rela': 'has_PK'
167 | }
168 | ret = self.api.get_class_members(id, opts)
169 | if 'drugMemberGroup' not in ret:
170 | return name, None
171 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
172 | return name, drug_names
173 | for pk_id, pk_name in pairs:
174 | pe_data.append(get_similar(pk_id, pk_name))
175 | return pe_data
176 |
177 |
178 | def drugs_with_pharmacokinetics(self, pe_name):
179 | ret = self.get_class_by_name(pe_name)
180 | if 'classId' not in ret:
181 | return "{} not found in database.".format(pe_name)
182 | pk_id = ret['classId']
183 | opts = {
184 | 'relaSource': 'NDFRT',
185 | 'rela': 'has_PK'
186 | }
187 | ret = self.api.get_class_members(pk_id, opts)
188 | if 'drugMemberGroup' not in ret:
189 | return pe_name, None
190 | drug_names = [member['minConcept']['name'] for member in ret['drugMemberGroup']['drugMember']]
191 | return pe_name, drug_names
192 |
193 |
194 | def get_class_by_name(self, class_name):
195 | ret = self.api.find_class_by_name(class_name)
196 | if 'rxclassMinConceptList' not in ret:
197 | return ret
198 | return ret['rxclassMinConceptList']['rxclassMinConcept'][0]
199 |
200 | def get_class_by_id(self, class_id):
201 | ret = self.api.find_class_by_id(class_id)
202 | if 'rxclassMinConceptList' not in ret:
203 | return ret
204 | return ret['rxclassMinConceptList']['rxclassMinConcept'][0]
205 |
206 | def similar_classes(self, class_name, limit=10):
207 | ret = self.get_class_by_name(class_name)
208 | if 'classId' not in ret:
209 | return "{} not found in database.".format(class_name)
210 | opts = {
211 | 'relaSource': 'ATC',
212 | #'rela': 'MOA',
213 | 'scoreType': 2,
214 | 'top': limit,
215 | 'equivalenceThreshold': 0.3,
216 | 'inclusionThreshold': 0.3
217 | }
218 | ret = self.api.find_similar_classes_by_class(ret['classId'], opts)
219 |
220 |
221 |
222 | @memo
223 | def mechanism_of_action(self, drug_name):
224 | moa = set()
225 | meshpa = set()
226 | if 'MOA' in self.memo[drug_name]:
227 | moa = set([tup[1] for tup in self.memo[drug_name]['MOA']])
228 | if 'MESHPA' in self.memo[drug_name]:
229 | meshpa = set([tup[1] for tup in self.memo[drug_name]['MESHPA']])
230 |
231 | return sorted(moa & meshpa)
232 |
233 | @memo
234 | def drug_type(self, drug_name):
235 | if 'VA' not in self.memo[drug_name]:
236 | return
237 | return sorted(set([tup[1] for tup in self.memo[drug_name]['VA']]))
238 |
239 | @memo
240 | def indications(self, drug_name):
241 | if 'DISEASE' not in self.memo[drug_name]:
242 | return
243 | return sorted(set([tup[1] for tup in self.memo[drug_name]['DISEASE']]))
244 |
245 | @memo
246 | def physiological_effects(self, drug_name):
247 | if 'PE' not in self.memo[drug_name]:
248 | return
249 | return sorted(set([tup[1] for tup in self.memo[drug_name]['PE']]))
250 |
251 | @memo
252 | def pharmacokinetics(self, drug_name):
253 | if 'PK' not in self.memo[drug_name]:
254 | return
255 | return sorted(set([tup[1] for tup in self.memo[drug_name]['PK']]))
256 |
257 | @memo
258 | def therapeutic_class(self, drug_name):
259 | if 'ATC1-4' not in self.memo[drug_name]:
260 | return
261 | return sorted(set([tup[1] for tup in self.memo[drug_name]['ATC1-4']]))
262 |
263 | @memo
264 | def pharmacological_classes(self, drug_name):
265 | if 'EPC' not in self.memo[drug_name]:
266 | return
267 | return sorted(set([tup[1] for tup in self.memo[drug_name]['EPC']]))
268 |
269 | @memo
270 | def chemical_name_of_brand(self, brand_name):
271 | if 'CHEM' not in self.memo[brand_name]:
272 | return
273 | return Counter([tup[1] for tup in self.memo[brand_name]['CHEM']]).most_common(1)[0][0]
274 |
275 |
276 | def subtypes(self, class_name):
277 | ret = self.get_class_by_name(class_name)
278 | if 'classId' not in ret:
279 | return "{} not found in database".format(class_name)
280 | class_id = ret['classId']
281 | ret = self.api.get_class_tree(class_id)
282 | title = "Subtypes of {}".format(class_name)
283 | if 'rxClassTree' not in ret:
284 | return title, None
285 | subtypes = [sub['minConcept'] for sub in ret['rxClassTree']['rxClass']]
286 | return title, subtypes
287 |
288 | def class_name_suggestions(self, class_name, only_drugs=False, class_type='CLASS'):
289 | if only_drugs:
290 | class_type = 'DRUGS'
291 | ret = self.api.get_spelling_suggestions(class_name, class_type)
292 | if 'suggestionList' not in ret:
293 | return "Items similarly named to {} were not found.".format(class_name)
294 | return ret['suggestionList']['suggestion']
295 |
296 |
297 | def list_class_types(self):
298 | return reduce(lambda acc, ct: acc + ["{} = {}".format(ct[0], ct[1])], self.drug_class_types.items(), [])
299 |
300 | def drug_info(self, drug_name):
301 | return {
302 | 'Name': self.chemical_name_of_brand(drug_name),
303 | 'Drug Type': self.drug_type(drug_name),
304 | 'Therapeutic Class': self.therapeutic_class(drug_name),
305 | 'Indications': self.indications(drug_name),
306 | 'Mechanism of Action': self.mechanism_of_action(drug_name),
307 | 'Physiological Effects': self.physiological_effects(drug_name),
308 | 'Pharmacokinetics': self.pharmacokinetics(drug_name),
309 | }
310 |
311 | def save(self):
312 | with open("{}.p".format(self.filename), 'wb') as f:
313 | picklerick.dump(self.memo, f)
314 |
315 | def wipe(self):
316 | open("{}.p".format(self.filename), 'wb').close()
317 |
318 | def load(self):
319 | if not os.path.exists("{}.p".format(self.filename)):
320 | with open("{}.p".format(self.filename), 'wb') as f:
321 | picklerick.dump({}, f)
322 | with open("{}.p".format(self.filename), 'rb') as f:
323 | try:
324 | data = picklerick.load(f)
325 | except EOFError:
326 | data = {}
327 | return data
328 |
329 | def __enter__(self):
330 | self.memo = self.load()
331 | return self
332 |
333 | def __exit__(self, type, value, traceback):
334 | if self.save_memo:
335 | self.save()
336 |
--------------------------------------------------------------------------------
/rxclass_api/__init__.py:
--------------------------------------------------------------------------------
1 | from .RxAPIWrapper import RxAPIWrapper
2 | from .RxClassHelpers import RxClassHelpers
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(
4 | name='rxclass',
5 | description='Python wrapper for RxClass API',
6 | url='https://github.com/Ianphorsman/RxClassAPIWrapper',
7 | author='Ian Horsman',
8 | author_email='ianphorsman@gmail.com',
9 | license='MIT',
10 | version='0.1.2',
11 | packages=['rxclass_api']
12 | )
--------------------------------------------------------------------------------