Passed
Push — main ( 83a9fb...fa90c4 )
by Douglas
03:43
created

mandos.model.searches.Search.find_all()   B

Complexity

Conditions 5

Size

Total Lines 38
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 23
nop 2
dl 0
loc 38
rs 8.8613
c 0
b 0
f 0
1
from __future__ import annotations
0 ignored issues
show
introduced by
Missing module docstring
Loading history...
2
3
import abc
4
import dataclasses
5
from typing import Any, Generic, Mapping, Sequence, TypeVar
6
7
from pocketutils.core.exceptions import XTypeError
0 ignored issues
show
introduced by
Unable to import 'pocketutils.core.exceptions'
Loading history...
8
from suretime import Suretime
0 ignored issues
show
introduced by
Unable to import 'suretime'
Loading history...
9
10
from mandos.model.hit_dfs import HitDf
11
from mandos.model.hits import AbstractHit
12
from mandos.model.utils.reflection_utils import ReflectionUtils
13
from mandos.model.utils.resources import MandosResources
14
15
H = TypeVar("H", bound=AbstractHit, covariant=True)
0 ignored issues
show
Coding Style Naming introduced by
Class name "H" doesn't conform to PascalCase naming style ('[^\\W\\da-z][^\\W_]+$' 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...
16
17
18
class SearchError(Exception):
19
    """
20
    Wrapper for any exception raised in ``find`` except for ``CompoundNotFoundError``.
21
    """
22
23
    def __init__(
24
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
25
        *args,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
26
        inchikey: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
27
        search_key: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
28
        search_class: str = None,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
29
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
30
    ):
31
        super().__init__(*args, *kwargs)
32
        self.inchikey = inchikey
33
        self.search_key = search_key
34
        self.search_class = search_class
35
36
37
class Search(Generic[H], metaclass=abc.ABCMeta):
38
    """
39
    Something to search and how to do it.
40
    """
41
42
    def __init__(self, key: str):
43
        self.key = key
44
45
    @classmethod
46
    def primary_data_source(cls) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
47
        z = MandosResources.strings[cls.__name__]["source"]
0 ignored issues
show
Coding Style Naming introduced by
Variable name "z" 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...
48
        # TODO: really?
0 ignored issues
show
Coding Style introduced by
TODO and FIXME comments should generally be avoided.
Loading history...
49
        return z.split(":")[0]
50
51
    @property
52
    def search_class(self) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
53
        return self.__class__.__name__
54
55
    @classmethod
56
    def search_name(cls) -> str:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
57
        return cls.__name__.lower().replace("search", "")
58
59
    def get_params(self) -> Mapping[str, Any]:
60
        """
61
        Returns the *parameters* of this ``Search`` their values.
62
        Parameters are attributes that do not begin with an underscore.
63
        """
64
        return {key: value for key, value in vars(self).items() if not key.startswith("_")}
65
66
    def find(self, inchikey: str) -> Sequence[H]:
0 ignored issues
show
introduced by
Missing function or method docstring
Loading history...
67
        # override this
68
        raise NotImplementedError()
69
70
    @classmethod
71
    def hit_fields(cls) -> Sequence[str]:
72
        """
73
        Gets the fields in the Hit type parameter.
74
        """
75
        # Okay, there's a lot of magic going on here
76
        # We need to access the _parameter_ H on cls -- raw `H` doesn't work
77
        # get_args and __orig_bases__ do this for us
78
        # then dataclasses.fields gives us the dataclass fields
79
        # there's also actual_h.__annotations__, but that doesn't include ClassVar and InitVar
80
        # (not that we're using those)
81
        # If this magic is too magical, we can make this an abstract method
82
        # But that would be a lot of excess code and it might be less modular
83
        x = cls.get_h()
0 ignored issues
show
Coding Style Naming introduced by
Variable name "x" 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...
84
        # noinspection PyDataclass
85
        return [f.name for f in dataclasses.fields(x) if f.name != "search_class"]
86
87
    @classmethod
88
    def get_h(cls):
89
        """
90
        Returns the underlying hit TypeVar, ``H``.
91
        """
92
        # noinspection PyTypeChecker
93
        return ReflectionUtils.get_generic_arg(cls, AbstractHit)
94
95
    def _format_source(self, **kwargs) -> str:
96
        s = MandosResources.strings[self.search_class]["source"]
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...
97
        for k, v in kwargs.items():
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...
98
            s = s.replace(f"{{{k}}}", str(v))
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...
99
        return s
100
101
    def _format_predicate(self, **kwargs) -> str:
102
        s = MandosResources.strings[self.search_class]["predicate"]
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...
103
        for k, v in kwargs.items():
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...
104
            s = s.replace(f"{{{k}}}", str(v))
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...
105
        return s
106
107
    def _create_hit(
0 ignored issues
show
best-practice introduced by
Too many arguments (9/5)
Loading history...
108
        self,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
109
        c_origin: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
110
        c_matched: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
111
        c_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
112
        c_name: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
113
        data_source: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
114
        predicate: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
115
        object_id: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
116
        object_name: str,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
117
        **kwargs,
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation before block (add 4 spaces).
Loading history...
118
    ) -> H:
119
        # ignore statement -- we've removed it for now
120
        entry = dict(
121
            record_id=None,
122
            search_key=self.key,
123
            search_class=self.search_class,
124
            data_source=data_source,
125
            run_date=Suretime.tagged.now_utc_sys().iso_with_zone,
126
            cache_date=None,
127
            weight=1,
128
            compound_id=c_id,
129
            origin_inchikey=c_origin,
130
            matched_inchikey=c_matched,
131
            compound_name=c_name,
132
            predicate=predicate,
133
            object_id=object_id,
134
            object_name=object_name,
135
        )
136
        entry.update(kwargs)
137
        clazz = self.__class__.get_h()
138
        # noinspection PyArgumentList
139
        return clazz(**entry)
140
141
    def __repr__(self) -> str:
142
        return ", ".join([k + "=" + str(v) for k, v in self.get_params().items()])
143
144
    def __str__(self) -> str:
145
        return repr(self)
146
147
    def __eq__(self, other: Search) -> bool:
148
        """
149
        Returns True iff all of the parameters match, thereby excluding attributes with underscores.
150
        Multiversal equality.
151
152
        Raises:
153
            TypeError: If ``other`` is not a :class:`Search`
154
        """
155
        if not isinstance(other, Search):
156
            raise XTypeError(f"{type(other)} not comparable")
157
        return repr(self) == repr(other)
158
159
160
__all__ = ["Search", "HitDf", "SearchError"]
161