Passed
Push — main ( cdf0f7...3de8e8 )
by Douglas
01:40
created

mandos.search.pubchem.drugbank_ddi_search   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 135
Duplicated Lines 14.81 %

Importance

Changes 0
Metric Value
eloc 115
dl 20
loc 135
rs 9.36
c 0
b 0
f 0
wmc 38

8 Methods

Rating   Name   Duplication   Size   Complexity  
A DrugbankDdiSearch._guess_up_down() 0 6 3
B DrugbankDdiSearch.find() 0 35 6
A DrugbankDdiSearch._guess_type() 0 16 3
A DrugbankDdiSearch.data_source() 0 3 1
B DrugbankDdiSearch._guess_activity() 10 10 6
C DrugbankDdiSearch._guess_pk() 0 16 9
A DrugbankDdiSearch._guess_efficacy() 0 8 4
B DrugbankDdiSearch._guess_adverse() 10 10 6

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
import re
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
from dataclasses import dataclass
3
from typing import Sequence, Optional
4
5
from mandos.search.pubchem import PubchemHit, PubchemSearch
6
7
8
@dataclass(frozen=True, order=True, repr=True)
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
9
class DrugbankDdiHit(PubchemHit):
10
    """"""
11
12
    type: str
13
    effect_target: Optional[str]
14
    change: Optional[str]
15
    description: str
16
17
18
class DrugbankDdiSearch(PubchemSearch[DrugbankDdiHit]):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
19
    """"""
20
21
    @property
22
    def data_source(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
23
        return "DrugBank"
24
25
    def find(self, inchikey: str) -> Sequence[DrugbankDdiHit]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
26
        data = self.api.fetch_data(inchikey)
27
        hits = []
28
        for dd in data.biomolecular_interactions_and_pathways.drugbank_ddis:
0 ignored issues
show
Coding Style Naming introduced by
Variable name "dd" 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...
29
            kind = self._guess_type(dd.description)
30
            up_or_down = self._guess_up_down(dd.description)
31
            spec = None
32
            if kind == "risk":
33
                spec = self._guess_adverse(dd.description)
34
            elif kind == "activity":
35
                spec = self._guess_activity(dd.description)
36
            elif kind == "PK":
37
                spec = self._guess_pk(dd.description)
38
            elif kind == "efficacy":
39
                spec = self._guess_efficacy(dd.description)
40
            hits.append(
41
                DrugbankDdiHit(
42
                    record_id=None,
43
                    origin_inchikey=inchikey,
44
                    matched_inchikey=data.names_and_identifiers.inchikey,
45
                    compound_id=str(data.cid),
46
                    compound_name=data.name,
47
                    predicate="has DDI with",
48
                    object_id=dd.drug_drugbank_id,
49
                    object_name=dd.drug_drugbank_id,
50
                    search_key=self.key,
51
                    search_class=self.search_class,
52
                    data_source=self.data_source,
53
                    type=kind,
54
                    effect_target=spec,
55
                    change=up_or_down,
56
                    description=dd.description,
57
                )
58
            )
59
        return hits
60
61
    def _guess_up_down(self, desc: str) -> str:
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
62
        if "increase" in desc:
0 ignored issues
show
unused-code introduced by
Unnecessary "elif" after "return"
Loading history...
63
            return "increase"
64
        elif "decrease" in desc:
65
            return "decrease"
66
        return "unknown"
67
68
    def _guess_efficacy(self, desc: str) -> Optional[str]:
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
69
        match = re.compile("efficacy of (.+)").search(desc)
70
        if match is None or match.group(1) is None:
71
            return None
72
        split = match.group(1).split(" can")
73
        if len(split) != 2:
74
            return None
75
        return split[0].strip()
76
77 View Code Duplication
    def _guess_activity(self, desc: str) -> Optional[str]:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
78
        match = re.compile("may increase the (.+)").search(desc)
79
        if match is None or match.group(1) is None:
80
            match = re.compile("may decrease the (.+)").search(desc)
81
        if match is None or match.group(1) is None:
82
            return None
83
        split = re.compile("activities").split(match.group(1))
84
        if len(split) != 2:
85
            return None
86
        return split[0].strip()
87
88 View Code Duplication
    def _guess_adverse(self, desc: str) -> Optional[str]:
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
89
        match = re.compile(" risk or severity of (.+)").search(desc)
90
        if match is None or match.group(1) is None:
91
            match = re.compile(" risk of (.+)").search(desc)
92
            if match is None or match.group(1) is None:
93
                return None
94
        split = re.compile(" (?:can)|(?:may) be").split(match.group(1))
95
        if len(split) != 2:
96
            return None
97
        return split[0].strip()
98
99
    def _guess_pk(self, desc: str) -> Optional[str]:
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
100
        match = re.compile("^The (.+)").search(desc)
101
        if match is not None and match.group(1) is not None:
102
            split = re.compile("can be").split(match.group(1))
103
            if len(split) == 2:
104
                return split[0].strip()
105
        # try another way
106
        match = re.compile("may increase the (.+)").search(desc)
107
        if match is None or match.group(1) is None:
108
            match = re.compile("may decrease the (.+)").search(desc)
109
        if match is None or match.group(1) is None:
110
            return None
111
        split = re.compile("which").split(match.group(1))
112
        if len(split) != 2:
113
            return None
114
        return split[0].strip()
115
116
    def _guess_type(self, desc: str) -> str:
0 ignored issues
show
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
117
        for k, v in {
0 ignored issues
show
Coding Style Naming introduced by
Variable name "v" 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...
118
            "serum concentration": "PK",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
119
            "metabolism": "PK",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
120
            "absorption": "PK",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
121
            "excretion": "PK",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
122
            "risk": "risk",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
123
            "severity": "risk",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
124
            "adverse": "risk",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
125
            "activities": "activity",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
126
            "activity": "activity",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
127
            "efficacy": "efficacy",
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
128
        }.items():
129
            if k in desc:
130
                return v
131
        return "unknown"
132
133
134
__all__ = ["DrugbankDdiHit", "DrugbankDdiSearch"]
135