Passed
Push — dependabot/pip/flake8-bugbear-... ( 16d864...b4f9fc )
by
unknown
01:45
created

Code.of_nullable()   A

Complexity

Conditions 4

Size

Total Lines 11
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 11
nop 2
dl 0
loc 11
rs 9.85
c 0
b 0
f 0
1
from __future__ import annotations
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
import enum
3
import re
4
from dataclasses import dataclass
5
from datetime import date
6
from typing import Union, Optional, FrozenSet, Sequence, Mapping, Set
7
8
from pocketutils.core.dot_dict import NestedDotDict
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.dot_dict'
Loading history...
9
10
from mandos.model import MandosResources, CleverEnum
0 ignored issues
show
Unused Code introduced by
Unused CleverEnum imported from mandos.model
Loading history...
11
from mandos.model.pubchem_support._nav_fns import Mapx
12
13
hazards = {
14
    d["code"]: d for d in NestedDotDict.read_toml(MandosResources.path("hazards.toml"))["signals"]
15
}
16
17
18
@dataclass(frozen=True, repr=True, eq=True, order=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
19
class ComputedProperty:
20
    key: str
21
    value: Union[int, str, float, bool]
22
    unit: Optional[str]
23
    ref: str
24
25
    def req_is(self, type_) -> Union[int, str, float, bool]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
26
        if not isinstance(self.value, type_):
27
            raise TypeError(f"{self.key}->{self.value} has {type(self.value)}, not {type_}")
28
        return self.value
29
30
    @property
31
    def as_str(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
32
        return f"{self.value} {self.unit}"
33
34
35
class Code(str):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
36
    @property
37
    def type_name(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
38
        return self.__class__.__name__.lower()
39
40
    @classmethod
41
    def of(cls, value: Union[str, int, float]):
0 ignored issues
show
Coding Style Naming introduced by
Method name "of" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
42
        if isinstance(value, float):
43
            try:
44
                value = int(value)
45
            except ArithmeticError:
46
                value = str(value)
47
        value = str(value).strip()
48
        return cls(value)
49
50
    @classmethod
51
    def of_nullable(cls, value: Union[None, str, int, float]):
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
52
        if value is None:
53
            return None
54
        if isinstance(value, float):
55
            try:
56
                value = int(value)
57
            except ArithmeticError:
58
                value = str(value)
59
        value = str(value).strip()
60
        return cls(value)
61
62
63
class Codes:
64
    """
65
    These turn out to be extremely useful for documenting return types.
66
    For example, ``DrugbankInteraction`` might have a ``gene`` field,
67
    which can be described as a ``GenecardSymbol`` if known.
68
    """
69
70
    class ChemIdPlusOrganism(Code):
71
        """
72
        E.g. 'women', 'frog', 'infant', or 'domestic animals - goat/sheep'
73
        """
74
75
        @property
76
        def is_human(self) -> bool:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
77
            return str(self) in {"women", "woman", "men", "man", "infant", "infants", "human"}
78
79
    class ChemIdPlusEffect(Code):
80
        """
81
        E.g. 'BEHAVIORAL: MUSCLE WEAKNESS'
82
        """
83
84
        @property
85
        def category(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
86
            return self[: self.index(":")]
87
88
        @property
89
        def subcategory(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
90
            return self[self.index(":") :]
91
92
    class EcNumber(Code):
93
        """
94
        e.g. 'EC:4.6.1.1'
95
        """
96
97
    class GeneId(Code):
98
        """
99
        GeneCard, UniProt gene name, etc.
100
        e.g. 'slc1a2'
101
        """
102
103
    class ClinicaltrialId(Code):
104
        """
105
        From clinicaltrials.gov
106
        """
107
108
    class GenericDiseaseCode(Code):
109
        """
110
        From clinicaltrials.gov; pure int
111
        """
112
113
    class GenecardSymbol(GeneId):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
114
        """"""
115
116
    class UniprotId(GeneId):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
117
        """"""
118
119
    class PubchemCompoundId(Code):
120
        """
121
        e.g. 2352
122
        """
123
124
        @property
125
        def value(self) -> int:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
126
            return int(self)
127
128
    class AtcCode(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
129
        """"""
130
131
    class PubmedId(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
132
        """"""
133
134
    class Doi(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
135
        """"""
136
137
    class MeshCode(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
138
        """"""
139
140
    class PdbId(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
141
        """"""
142
143
    class MeshHeading(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
144
        """"""
145
146
    class MeshSubheading(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
147
        """"""
148
149
    class DrugbankCompoundId(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
150
        """"""
151
152
    class DeaSchedule(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
153
        """"""
154
155
        @property
156
        def value(self) -> int:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
157
            return Mapx.roman_to_arabic(1, 5)(self)
158
159
    class GhsCode(Code):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
160
        """"""
161
162
163
class CoOccurrenceType(enum.Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
164
    chemical = enum.auto()
165
    gene = enum.auto()
166
    disease = enum.auto()
167
168
    @property
169
    def x_name(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
170
        if self is CoOccurrenceType.chemical:
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
171
            return "ChemicalNeighbor"
172
        elif self is CoOccurrenceType.gene:
173
            return "ChemicalGeneSymbolNeighbor"
174
        elif self is CoOccurrenceType.disease:
175
            return "ChemicalDiseaseNeighbor"
176
        raise AssertionError(f"{self} not found!!")
177
178
    @property
179
    def id_name(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
180
        if self is CoOccurrenceType.chemical:
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
181
            return "CID"
182
        elif self is CoOccurrenceType.gene:
183
            return "GeneSymbol"
184
        elif self is CoOccurrenceType.disease:
185
            return "MeSH"
186
        raise AssertionError(f"{self} not found!!")
187
188
189
class ClinicalTrialsGovUtils:
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
190
    @classmethod
191
    def phase_map(cls) -> Mapping[str, float]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
192
        return {
193
            "Phase 4": 4,
194
            "Phase 3": 3,
195
            "Phase 2": 2,
196
            "Phase 1": 1,
197
            "Early Phase 1": 1.5,
198
            "Phase 2/Phase 3": 2.5,
199
            "N/A": 0,
200
        }
201
202
    @classmethod
203
    def known_phases(cls) -> Set[float]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
204
        return set(cls.phase_map().values())
205
206
    @classmethod
207
    def resolve_statuses(cls, st: str) -> Set[str]:
0 ignored issues
show
Coding Style Naming introduced by
Argument name "st" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
introduced by
Missing function or method docstring
Loading history...
208
        found = set()
209
        for s in st.lower().split(","):
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
210
            s = s.strip()
0 ignored issues
show
Coding Style Naming introduced by
Variable name "s" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
211
            if s == "@all":
212
                match = cls.known_statuses()
213
            elif s in cls.known_statuses():
214
                match = {s}
215
            else:
216
                raise ValueError(s)
217
            for m in match:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "m" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
218
                found.add(m)
219
        return found
220
221
    @classmethod
222
    def known_statuses(cls) -> Set[str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
223
        return set(cls.status_map().values())
224
225
    @classmethod
226
    def status_map(cls) -> Mapping[str, str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
227
        return {
228
            "Unknown status": "unknown",
229
            "Completed": "completed",
230
            "Terminated": "stopped",
231
            "Suspended": "stopped",
232
            "Withdrawn": "stopped",
233
            "Not yet recruiting": "ongoing",
234
            "Recruiting": "ongoing",
235
            "Enrolling by invitation": "ongoing",
236
            "Active, not recruiting": "ongoing",
237
            "Available": "completed",
238
            "No longer available": "completed",
239
            "Temporarily not available": "completed",
240
            "Approved for marketing": "completed",
241
        }
242
243
244
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (9/7)
Loading history...
introduced by
Missing class docstring
Loading history...
245
class ClinicalTrial:
246
    ctid: Codes.ClinicaltrialId
247
    title: str
248
    conditions: FrozenSet[str]
249
    disease_ids: FrozenSet[Codes.ClinicaltrialId]
250
    phase: str
251
    status: str
252
    interventions: FrozenSet[str]
253
    cids: FrozenSet[Codes.PubchemCompoundId]
254
    source: str
255
256
    @property
257
    def mapped_phase(self) -> float:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
258
        return ClinicalTrialsGovUtils.phase_map().get(self.phase, 0)
259
260
    @property
261
    def mapped_status(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
262
        return ClinicalTrialsGovUtils.status_map().get(self.status, "unknown")
263
264
265
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
266
class GhsCode:
267
    code: Codes.GhsCode
268
    statement: str
269
    clazz: str
270
    categories: FrozenSet[str]
271
    signal_word: str
272
    type: str
273
274
    @classmethod
275
    def find(cls, code: str) -> GhsCode:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
276
        h = hazards[code]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "h" doesn't conform to snake_case naming style ('([^\\W\\dA-Z][^\\WA-Z]2,|_[^\\WA-Z]*|__[^\\WA-Z\\d_][^\\WA-Z]+__)$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
277
        cats = h["category"]  # TODO
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
278
        return GhsCode(
279
            code=Codes.GhsCode(code),
280
            statement=h["statement"],
281
            clazz=h["class"],
282
            categories=cats,
283
            signal_word=h["signal_word"],
284
            type=h["type"],
285
        )
286
287
    @property
288
    def level(self) -> int:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
289
        return int(self.code[1])
290
291
292
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
293
class AcuteEffectEntry:
294
    gid: int
295
    effects: FrozenSet[Codes.ChemIdPlusEffect]
296
    organism: Codes.ChemIdPlusOrganism
297
    test_type: str
298
    route: str
299
    dose: str
300
301
    @property
302
    def mg_per_kg(self) -> float:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
303
        match = re.compile(r".+?\(\d+mg/kg\)").fullmatch(self.dose)
304
        return float(match.group(1))
305
306
307
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
308
class AssociatedDisorder:
309
    gid: str
310
    disease_id: Codes.MeshCode
311
    disease_name: str
312
    evidence_type: str
313
    n_refs: int
314
315
316
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
317
class AtcCode:
318
    code: str
319
    name: str
320
321
    @property
322
    def level(self) -> int:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
323
        return len(self.parts)
324
325
    @property
326
    def parts(self) -> Sequence[str]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
327
        pat = re.compile(r"([A-Z])([0-9]{2})?([A-Z])?([A-Z])?([A-Z])?")
328
        match = pat.fullmatch(self.code)
329
        return [g for g in match.groups() if g is not None]
330
331
332
class DrugbankTargetType(enum.Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
333
    target = enum.auto()
334
    carrier = enum.auto()
335
    transporter = enum.auto()
336
    enzyme = enum.auto()
337
338
339
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (10/7)
Loading history...
introduced by
Missing class docstring
Loading history...
340
class DrugbankInteraction:
341
    record_id: str
342
    gene_symbol: Codes.GeneId
343
    action: str
344
    protein_id: str
345
    target_type: DrugbankTargetType
346
    target_name: str
347
    general_function: str
348
    specific_function: str
349
    pmids: FrozenSet[Codes.PubmedId]
350
    dois: FrozenSet[Codes.Doi]
351
352
353
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
354
class DrugbankDdi:
355
    drug_drugbank_id: Codes.DrugbankCompoundId
356
    drug_pubchem_id: Codes.PubchemCompoundId
357
    drug_drugbank_name: str
358
    description: str
359
360
361
class AssayType(enum.Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
362
    confirmatory = enum.auto()
363
    literature = enum.auto()
364
    other = enum.auto()
365
366
367
class Activity(enum.Enum):
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
368
    active = enum.auto()
369
    inactive = enum.auto()
370
    inconclusive = enum.auto()
371
    unspecified = enum.auto()
372
373
374
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (13/7)
Loading history...
introduced by
Missing class docstring
Loading history...
375
class Bioactivity:
376
    assay_id: int
377
    assay_type: AssayType
378
    assay_ref: str
379
    assay_name: str
380
    assay_made_date: date
381
    gene_id: Optional[Codes.GeneId]
382
    tax_id: Optional[int]
383
    pmid: Optional[Codes.PubmedId]
384
    activity: Optional[Activity]
385
    activity_name: Optional[str]
386
    activity_value: float
387
    target_name: Optional[str]
388
    compound_name: str
389
390
391
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (9/7)
Loading history...
introduced by
Missing class docstring
Loading history...
392
class PdbEntry:
393
    pdbid: Codes.PdbId
394
    title: str
395
    exp_method: str
396
    resolution: float
397
    lig_names: FrozenSet[str]
398
    cids: FrozenSet[Codes.PubchemCompoundId]
399
    uniprot_ids: FrozenSet[Codes.UniprotId]
400
    pmids: FrozenSet[Codes.PubmedId]
401
    dois: FrozenSet[Codes.Doi]
402
403
404
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
best-practice introduced by
Too many instance attributes (11/7)
Loading history...
405
class PubmedEntry:
406
    pmid: Codes.PubmedId
407
    article_type: str
408
    pmidsrcs: FrozenSet[str]
409
    mesh_headings: FrozenSet[Codes.MeshHeading]
410
    mesh_subheadings: FrozenSet[Codes.MeshSubheading]
411
    mesh_codes: FrozenSet[Codes.MeshCode]
412
    cids: FrozenSet[Codes.PubchemCompoundId]
413
    article_title: str
414
    article_abstract: str
415
    journal_name: str
416
    pub_date: date
417
418
419
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
420
class Publication:
421
    pmid: Codes.PubmedId
422
    pub_date: date
423
    is_review: bool
424
    title: str
425
    journal: str
426
    relevance_score: int
427
428
429
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
best-practice introduced by
Too many instance attributes (8/7)
Loading history...
introduced by
Missing class docstring
Loading history...
430
class CoOccurrence:
431
    neighbor_id: str
432
    neighbor_name: str
433
    kind: CoOccurrenceType
434
    # https://pubchemdocs.ncbi.nlm.nih.gov/knowledge-panels
435
    article_count: int
436
    query_article_count: int
437
    neighbor_article_count: int
438
    score: int
439
    publications: FrozenSet[Publication]
440
441
    def strip_pubs(self) -> CoOccurrence:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
442
        return CoOccurrence(
443
            self.neighbor_id,
444
            self.neighbor_name,
445
            self.kind,
446
            self.article_count,
447
            self.query_article_count,
448
            self.neighbor_article_count,
449
            self.score,
450
            frozenset({}),
451
        )
452
453
454
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
455
class DrugGeneInteraction:
456
    """"""
457
458
    gene_name: Optional[str]
459
    gene_claim_id: Optional[str]
460
    source: str
461
    interactions: FrozenSet[str]
462
    pmids: FrozenSet[Codes.PubmedId]
463
    dois: FrozenSet[Codes.Doi]
464
465
466
@dataclass(frozen=True, repr=True, eq=True)
0 ignored issues
show
introduced by
Missing class docstring
Loading history...
467
class ChemicalGeneInteraction:
468
    gene_name: Optional[Codes.GeneId]
469
    interactions: FrozenSet[str]
470
    tax_id: Optional[int]
471
    tax_name: Optional[str]
472
    pmids: FrozenSet[Codes.PubmedId]
473
474
475
__all__ = [
476
    "ClinicalTrial",
477
    "AssociatedDisorder",
478
    "AtcCode",
479
    "AssayType",
480
    "DrugbankInteraction",
481
    "DrugbankDdi",
482
    "Bioactivity",
483
    "Activity",
484
    "DrugGeneInteraction",
485
    "ChemicalGeneInteraction",
486
    "GhsCode",
487
    "PubmedEntry",
488
    "Code",
489
    "Codes",
490
    "CoOccurrenceType",
491
    "CoOccurrence",
492
    "Publication",
493
    "ComputedProperty",
494
    "ClinicalTrialsGovUtils",
495
    "AcuteEffectEntry",
496
    "DrugbankTargetType",
497
]
498