pythonQEPest 2.0.0a2__py3-none-any.whl
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.
- pythonQEPest/.env.example +4 -0
- pythonQEPest/__init__.py +11 -0
- pythonQEPest/cli/__init__.py +7 -0
- pythonQEPest/cli/cli.py +101 -0
- pythonQEPest/config/__init__.py +11 -0
- pythonQEPest/config/config_provider.py +9 -0
- pythonQEPest/config/normalise.py +12 -0
- pythonQEPest/config/qepest_config.py +31 -0
- pythonQEPest/config/qepest_default.py +26 -0
- pythonQEPest/core/__init__.py +7 -0
- pythonQEPest/core/qepest.py +98 -0
- pythonQEPest/core/qepest_meta.py +28 -0
- pythonQEPest/data.txt +2 -0
- pythonQEPest/data.txt.out +2 -0
- pythonQEPest/dto/QEPestData.py +21 -0
- pythonQEPest/dto/QEPestFile.py +34 -0
- pythonQEPest/dto/QEPestInput.py +60 -0
- pythonQEPest/dto/QEPestOutput.py +13 -0
- pythonQEPest/dto/__init__.py +27 -0
- pythonQEPest/dto/coefficients/QEPestCoefficient.py +57 -0
- pythonQEPest/dto/coefficients/QEPestCoefficientList.py +29 -0
- pythonQEPest/dto/coefficients/QEPestCoefficientNumerics.py +44 -0
- pythonQEPest/dto/coefficients/__init__.py +7 -0
- pythonQEPest/dto/normalisation/Normaliser.py +16 -0
- pythonQEPest/dto/normalisation/__init__.py +3 -0
- pythonQEPest/dto/pest_type/PestTypeCoefficient.py +30 -0
- pythonQEPest/dto/pest_type/PestTypeConfig.py +9 -0
- pythonQEPest/dto/pest_type/__init__.py +4 -0
- pythonQEPest/gui/__init__.py +7 -0
- pythonQEPest/gui/actions/__init__.py +9 -0
- pythonQEPest/gui/actions/action_clicks.py +42 -0
- pythonQEPest/gui/actions/actions_crud.py +131 -0
- pythonQEPest/gui/actions/actions_other.py +126 -0
- pythonQEPest/gui/elements/ButtonsFrame.py +50 -0
- pythonQEPest/gui/elements/DataTree.py +23 -0
- pythonQEPest/gui/elements/EditWindow.py +63 -0
- pythonQEPest/gui/elements/Menu.py +12 -0
- pythonQEPest/gui/elements/ResultTree.py +22 -0
- pythonQEPest/gui/elements/SaveButton.py +10 -0
- pythonQEPest/gui/elements/__init__.py +17 -0
- pythonQEPest/gui/gui.py +87 -0
- pythonQEPest/gui/gui_meta.py +17 -0
- pythonQEPest/gui/utility/DataManager.py +76 -0
- pythonQEPest/gui/utility/DataManagerMeta.py +8 -0
- pythonQEPest/gui/utility/__init__.py +6 -0
- pythonQEPest/helpers/__init__.py +17 -0
- pythonQEPest/helpers/check_nan.py +8 -0
- pythonQEPest/helpers/compute_df.py +5 -0
- pythonQEPest/helpers/get_num_of_cols.py +2 -0
- pythonQEPest/helpers/get_values_from_line.py +5 -0
- pythonQEPest/helpers/norm.py +33 -0
- pythonQEPest/helpers/round_to_4digs.py +2 -0
- pythonQEPest/logger.py +58 -0
- pythonQEPest/main.py +4 -0
- pythonQEPest/providers/__init__.py +6 -0
- pythonQEPest/providers/default_provider.py +23 -0
- pythonQEPest/providers/json_provider.py +30 -0
- pythonQEPest/providers/txt_provider.py +46 -0
- pythonQEPest/services/QEPestFileService.py +125 -0
- pythonQEPest/services/__init__.py +3 -0
- pythonqepest-2.0.0a2.dist-info/METADATA +217 -0
- pythonqepest-2.0.0a2.dist-info/RECORD +64 -0
- pythonqepest-2.0.0a2.dist-info/WHEEL +4 -0
- pythonqepest-2.0.0a2.dist-info/entry_points.txt +6 -0
pythonQEPest/__init__.py
ADDED
pythonQEPest/cli/cli.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Sequence
|
|
8
|
+
|
|
9
|
+
from pythonQEPest.dto import QEPestFile
|
|
10
|
+
from pythonQEPest.services.QEPestFileService import QEPestFileService
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _resolve_package_version() -> str:
|
|
16
|
+
try:
|
|
17
|
+
return version("pythonQEPest")
|
|
18
|
+
except PackageNotFoundError:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import tomllib # py3.11+
|
|
23
|
+
|
|
24
|
+
pyproject = Path(__file__).resolve().parents[2] / "pyproject.toml"
|
|
25
|
+
data = tomllib.loads(pyproject.read_text(encoding="utf-8"))
|
|
26
|
+
return data["project"]["version"]
|
|
27
|
+
except Exception:
|
|
28
|
+
return "0+unknown"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
32
|
+
parser = argparse.ArgumentParser(
|
|
33
|
+
prog="pythonqepest",
|
|
34
|
+
description="Compute QEPest scores from a tab-separated input file.",
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"-v",
|
|
38
|
+
"--version",
|
|
39
|
+
action="version",
|
|
40
|
+
version=f"%(prog)s {_resolve_package_version()}",
|
|
41
|
+
help="Show program's version number.",
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument(
|
|
44
|
+
"-i",
|
|
45
|
+
"--input",
|
|
46
|
+
default="data.txt",
|
|
47
|
+
help="Path to input tab-separated file with "
|
|
48
|
+
+ "QEPest descriptors (default: data.txt).",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"-o",
|
|
53
|
+
"--output",
|
|
54
|
+
default="data.out.txt",
|
|
55
|
+
help="Path to output tab-separated file with "
|
|
56
|
+
+ "QEPest descriptors (default: data.txt.out).",
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"-f",
|
|
61
|
+
"--format",
|
|
62
|
+
default="txt",
|
|
63
|
+
help="Format to output file with QEPest (json, txt).",
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--smiles",
|
|
68
|
+
action="store_true",
|
|
69
|
+
help="Input file contains SMILES strings "
|
|
70
|
+
"(one per line) instead of descriptors.",
|
|
71
|
+
)
|
|
72
|
+
return parser
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main(argv: Sequence[str] | None = None) -> int:
|
|
76
|
+
from dotenv import load_dotenv
|
|
77
|
+
|
|
78
|
+
from pythonQEPest.core.qepest import QEPest
|
|
79
|
+
from pythonQEPest.logger import init_logger
|
|
80
|
+
|
|
81
|
+
args = build_parser().parse_args(argv)
|
|
82
|
+
|
|
83
|
+
load_dotenv()
|
|
84
|
+
init_logger()
|
|
85
|
+
|
|
86
|
+
logger.debug("CLI initiated")
|
|
87
|
+
logger.info(f"CLI args: {argv}")
|
|
88
|
+
|
|
89
|
+
service = QEPestFileService(
|
|
90
|
+
qepest=QEPest(),
|
|
91
|
+
qepest_file=QEPestFile(
|
|
92
|
+
input_file=args.input,
|
|
93
|
+
output_file=args.output,
|
|
94
|
+
format=args.format,
|
|
95
|
+
smiles=args.smiles,
|
|
96
|
+
),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
service.read_file_and_compute_params()
|
|
100
|
+
|
|
101
|
+
return 0 if not service.error else 1
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from pythonQEPest.config.qepest_default import qepest_default
|
|
2
|
+
from pythonQEPest.config.normalise import normalise_default
|
|
3
|
+
from pythonQEPest.config.qepest_config import QEPestConfig
|
|
4
|
+
from pythonQEPest.config.config_provider import ConfigProvider
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"normalise_default",
|
|
8
|
+
"qepest_default",
|
|
9
|
+
"ConfigProvider",
|
|
10
|
+
"QEPestConfig",
|
|
11
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
normalise_default = {
|
|
2
|
+
"herb": (69.5849922, 94.4228257, 120.4572352, 228.1589796, 89.7012502, 276.9634213),
|
|
3
|
+
"insect": (
|
|
4
|
+
78.2919965,
|
|
5
|
+
71.2829691,
|
|
6
|
+
133.9224801,
|
|
7
|
+
331.170104,
|
|
8
|
+
70.5540709,
|
|
9
|
+
193.0023343,
|
|
10
|
+
),
|
|
11
|
+
"fung": (53.3719946, 52.773116, 73.7976536, 144.9887053, 41.4385926, 102.3024319),
|
|
12
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from pythonQEPest.dto.pest_type.PestTypeConfig import PestTypeConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QEPestConfig(BaseModel):
|
|
9
|
+
name: Optional[str] = "HerbInsectFung"
|
|
10
|
+
pest_types: list[PestTypeConfig]
|
|
11
|
+
|
|
12
|
+
def get_pest_names(self) -> list[str]:
|
|
13
|
+
return [pest.name for pest in self.pest_types]
|
|
14
|
+
|
|
15
|
+
def get_coefficients(
|
|
16
|
+
self, pest_name: str
|
|
17
|
+
) -> list[tuple[float, float, float, float]]:
|
|
18
|
+
for pest in self.pest_types:
|
|
19
|
+
if pest.name == pest_name:
|
|
20
|
+
return pest.coefficients.as_set()
|
|
21
|
+
|
|
22
|
+
raise ValueError(f"Pest type '{pest_name}' not found")
|
|
23
|
+
|
|
24
|
+
def get_normaliser(
|
|
25
|
+
self, pest_name: str
|
|
26
|
+
) -> tuple[float, float, float, float, float, float]:
|
|
27
|
+
for pest in self.pest_types:
|
|
28
|
+
if pest.name == pest_name:
|
|
29
|
+
return pest.normaliser
|
|
30
|
+
|
|
31
|
+
raise ValueError(f"Pest type '{pest_name}' not found")
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
qepest_default = {
|
|
2
|
+
"herb": [
|
|
3
|
+
(70.77, 283.0, 84.97, -1.185), # mwH
|
|
4
|
+
(93.81, 3.077, 1.434, 0.6164), # logpH
|
|
5
|
+
(117.6, 2.409, 1.567, 7.155), # hbaH
|
|
6
|
+
(233.4, 0.4535, -1.48, 4.47), # hbdH
|
|
7
|
+
(84.7, 4.758, -2.423, 5.437), # rbH
|
|
8
|
+
(301.8, 1.101, 0.8869, -22.81), # arRCH
|
|
9
|
+
],
|
|
10
|
+
"insect": [
|
|
11
|
+
(76.38, 298.3, 83.64, 1.912), # mwI
|
|
12
|
+
(74.27, 4.555, -2.193, -2.987), # logpI
|
|
13
|
+
(139.4, 1.363, 1.283, 0.5341), # hbaI
|
|
14
|
+
(670.6, -1.163, 0.7856, 0.7951), # hbdI
|
|
15
|
+
(65.49, 6.219, -2.448, 5.318), # rbI
|
|
16
|
+
(287.5, 0.305, 1.554, -88.64), # arRCI
|
|
17
|
+
],
|
|
18
|
+
"fung": [
|
|
19
|
+
(51.03, 314.2, -56.31, 2.342), # mwF
|
|
20
|
+
(50.73, 3.674, -1.238, 2.067), # logpF
|
|
21
|
+
(73.79, 1.841, 1.326, 0.5158), # hbaF
|
|
22
|
+
(164.7, -0.9762, -2.027, 1.384), # hbdF
|
|
23
|
+
(40.91, 1.822, 2.582, 0.6235), # rbF
|
|
24
|
+
(134.4, 0.8383, 1.347, -31.17), # arRCF
|
|
25
|
+
],
|
|
26
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from pythonQEPest.config import ConfigProvider
|
|
6
|
+
from pythonQEPest.core.qepest_meta import QEPestMeta
|
|
7
|
+
from pythonQEPest.config.qepest_config import QEPestConfig
|
|
8
|
+
from pythonQEPest.dto import QEPestData
|
|
9
|
+
|
|
10
|
+
from pythonQEPest.dto.QEPestInput import QEPestInput
|
|
11
|
+
from pythonQEPest.dto.QEPestOutput import QEPestOutput
|
|
12
|
+
from pythonQEPest.dto.normalisation.Normaliser import Normaliser
|
|
13
|
+
from pythonQEPest.helpers.check_nan import check_nan
|
|
14
|
+
from pythonQEPest.helpers.compute_df import compute_df
|
|
15
|
+
from pythonQEPest.helpers.get_values_from_line import get_values_from_line
|
|
16
|
+
from pythonQEPest.helpers.round_to_4digs import round_to_4digs
|
|
17
|
+
from pythonQEPest.providers import DefaultConfigProvider
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class QEPest(QEPestMeta):
|
|
23
|
+
def __init__(self, provider: Optional[ConfigProvider] = None, *args, **kwargs):
|
|
24
|
+
super().__init__(*args, **kwargs)
|
|
25
|
+
|
|
26
|
+
self.config: QEPestConfig
|
|
27
|
+
self.normalisers: dict[str, Normaliser] = {}
|
|
28
|
+
self.qex: QEPestData = QEPestData({})
|
|
29
|
+
|
|
30
|
+
logger.debug("QEPest initialization")
|
|
31
|
+
|
|
32
|
+
self.initialise_config(provider)
|
|
33
|
+
|
|
34
|
+
logger.debug("QEPest initialization successful")
|
|
35
|
+
|
|
36
|
+
def initialise_config(self, provider: Optional[ConfigProvider] = None):
|
|
37
|
+
logger.debug("Config initialisation")
|
|
38
|
+
if provider is None:
|
|
39
|
+
logger.debug("Config is empty. Loading default one.")
|
|
40
|
+
provider = DefaultConfigProvider()
|
|
41
|
+
|
|
42
|
+
self.config = provider.load()
|
|
43
|
+
logger.debug(f"Config {self.config} loaded")
|
|
44
|
+
|
|
45
|
+
for pest in self.config.pest_types:
|
|
46
|
+
self.normalisers[pest.name] = Normaliser(pest.normaliser)
|
|
47
|
+
|
|
48
|
+
def _log_compute_df(
|
|
49
|
+
self, func, index: int, lst: list[float], data_lst: list
|
|
50
|
+
) -> float:
|
|
51
|
+
df_result = compute_df(lst[index], *data_lst[index])
|
|
52
|
+
return math.log(func(df_result, index))
|
|
53
|
+
|
|
54
|
+
def get_names(self) -> list[str]:
|
|
55
|
+
return self.config.get_pest_names()
|
|
56
|
+
|
|
57
|
+
def compute_params(self, data_input: QEPestInput) -> QEPestOutput:
|
|
58
|
+
self.get_qex_values(
|
|
59
|
+
get_values_from_line(list(data_input.model_dump().values()))
|
|
60
|
+
)
|
|
61
|
+
return QEPestOutput(data=self.qex, name=data_input.name)
|
|
62
|
+
|
|
63
|
+
def get_qex_values(self, d: list[float]) -> QEPestData:
|
|
64
|
+
names = self.get_names()
|
|
65
|
+
|
|
66
|
+
if len(names) == 0:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
"No pest types configured. " "Please provide a valid configuration."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
qe_values = dict.fromkeys(names, 0.0)
|
|
72
|
+
|
|
73
|
+
d_num = len(d)
|
|
74
|
+
for i in range(d_num):
|
|
75
|
+
for name in names:
|
|
76
|
+
coeffs = self.config.get_coefficients(name)
|
|
77
|
+
normaliser = self.normalisers[name]
|
|
78
|
+
qe_values[name] += self._log_compute_df(
|
|
79
|
+
func=normaliser.norm,
|
|
80
|
+
index=i,
|
|
81
|
+
lst=d,
|
|
82
|
+
data_lst=coeffs,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
result = [round_to_4digs(math.exp(qe_values[name] / d_num)) for name in names]
|
|
86
|
+
|
|
87
|
+
result = check_nan(result)
|
|
88
|
+
|
|
89
|
+
self.qex = QEPestData(
|
|
90
|
+
{f"qe_{name}": result[idx] for idx, name in enumerate(names)}
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
return self.qex
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
qepest = QEPest()
|
|
98
|
+
qepest.get_qex_values([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0])
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from abc import abstractmethod, ABC
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
from pythonQEPest.dto import QEPestInput, QEPestOutput
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class QEPestMeta(ABC):
|
|
10
|
+
def __init__(self, *args, **kwargs):
|
|
11
|
+
self.qex: BaseModel | None = None
|
|
12
|
+
|
|
13
|
+
self.col_number: int = 7
|
|
14
|
+
self.dir: str = os.getcwd()
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def compute_params(self, data_input: QEPestInput) -> QEPestOutput:
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def get_qex_values(self, d: list[float]) -> None:
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def _log_compute_df(
|
|
26
|
+
self, func, index: int, lst: list[float], data_lst: list
|
|
27
|
+
) -> float:
|
|
28
|
+
pass
|
pythonQEPest/data.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pydantic import RootModel, model_validator
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class QEPestData(RootModel[dict[str, float]]):
|
|
5
|
+
@model_validator(mode="before")
|
|
6
|
+
@classmethod
|
|
7
|
+
def handle_input(cls, data):
|
|
8
|
+
if data is None:
|
|
9
|
+
return {}
|
|
10
|
+
if isinstance(data, dict):
|
|
11
|
+
return data
|
|
12
|
+
return data
|
|
13
|
+
|
|
14
|
+
def __getattr__(self, name: str) -> float:
|
|
15
|
+
return self.root.get(name, 0.0)
|
|
16
|
+
|
|
17
|
+
def __setattr__(self, name: str, value: float) -> None:
|
|
18
|
+
if name == "root":
|
|
19
|
+
super().__setattr__(name, value)
|
|
20
|
+
else:
|
|
21
|
+
self.root[name] = value
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, model_validator
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class QEPestFormat(Enum):
|
|
8
|
+
TXT = "txt"
|
|
9
|
+
JSON = "json"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QEPestFile(BaseModel):
|
|
13
|
+
input_file: Optional[str] = None
|
|
14
|
+
output_file: Optional[str] = None
|
|
15
|
+
format: Optional[QEPestFormat] = QEPestFormat.TXT
|
|
16
|
+
smiles: Optional[bool] = False
|
|
17
|
+
|
|
18
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
19
|
+
|
|
20
|
+
@model_validator(mode="after")
|
|
21
|
+
def set_defaults(self):
|
|
22
|
+
if self.input_file is None:
|
|
23
|
+
self.input_file = "data.txt"
|
|
24
|
+
|
|
25
|
+
if self.format is None:
|
|
26
|
+
self.format = QEPestFormat.TXT
|
|
27
|
+
|
|
28
|
+
formatted_output = self.input_file.replace(".txt", "").replace(".json", "")
|
|
29
|
+
if self.format == QEPestFormat.JSON:
|
|
30
|
+
self.output_file = f"{formatted_output}.out.json"
|
|
31
|
+
else:
|
|
32
|
+
self.output_file = f"{formatted_output}.out.txt"
|
|
33
|
+
|
|
34
|
+
return self
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class QEPestInput(BaseModel):
|
|
10
|
+
name: str = ""
|
|
11
|
+
|
|
12
|
+
mol_weight: float = 0.0
|
|
13
|
+
log_p: float = 0.0
|
|
14
|
+
|
|
15
|
+
hbond_acceptors: int = 0
|
|
16
|
+
hbond_donors: int = 0
|
|
17
|
+
|
|
18
|
+
rotatable_bonds: int = 0
|
|
19
|
+
aromatic_rings: int = 0
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_array(cls, data: Union[list, tuple, set]):
|
|
23
|
+
if len(data) != 7:
|
|
24
|
+
raise ValueError(f"Expected 7 elements, got {len(data)}")
|
|
25
|
+
return cls(
|
|
26
|
+
name=data[0],
|
|
27
|
+
mol_weight=float(data[1]),
|
|
28
|
+
log_p=float(data[2]),
|
|
29
|
+
hbond_acceptors=int(data[3]),
|
|
30
|
+
hbond_donors=int(data[4]),
|
|
31
|
+
rotatable_bonds=int(data[5]),
|
|
32
|
+
aromatic_rings=int(data[6]),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_smiles(cls, smiles: str, name: str = ""):
|
|
37
|
+
try:
|
|
38
|
+
from rdkit import Chem
|
|
39
|
+
from rdkit.Chem import Descriptors
|
|
40
|
+
except ImportError:
|
|
41
|
+
logger.warning(
|
|
42
|
+
"RDKit is not installed. Install it with: poetry install --with rdkit. "
|
|
43
|
+
"Returning QEPestInput with default values."
|
|
44
|
+
)
|
|
45
|
+
return cls(name=name)
|
|
46
|
+
|
|
47
|
+
mol = Chem.MolFromSmiles(smiles)
|
|
48
|
+
|
|
49
|
+
if mol is None:
|
|
50
|
+
raise ValueError(f"Invalid SMILES: {smiles}")
|
|
51
|
+
|
|
52
|
+
return cls(
|
|
53
|
+
name=name,
|
|
54
|
+
mol_weight=Descriptors.ExactMolWt(mol),
|
|
55
|
+
log_p=Descriptors.MolLogP(mol),
|
|
56
|
+
hbond_acceptors=Descriptors.NumHAcceptors(mol),
|
|
57
|
+
hbond_donors=Descriptors.NumHDonors(mol),
|
|
58
|
+
rotatable_bonds=Descriptors.NumRotatableBonds(mol),
|
|
59
|
+
aromatic_rings=Descriptors.NumAromaticRings(mol),
|
|
60
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from pythonQEPest.dto.QEPestData import QEPestData
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class QEPestOutput(BaseModel):
|
|
8
|
+
data: QEPestData
|
|
9
|
+
name: Optional[str] = ""
|
|
10
|
+
|
|
11
|
+
def to_array(self) -> list:
|
|
12
|
+
values = list(self.data.root.values())
|
|
13
|
+
return [self.name] + values
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from pythonQEPest.dto.QEPestData import QEPestData
|
|
2
|
+
from pythonQEPest.dto.QEPestInput import QEPestInput
|
|
3
|
+
from pythonQEPest.dto.QEPestFile import QEPestFile, QEPestFormat
|
|
4
|
+
from pythonQEPest.dto.QEPestOutput import QEPestOutput
|
|
5
|
+
|
|
6
|
+
from pythonQEPest.dto.coefficients import QEPestCoefficient
|
|
7
|
+
from pythonQEPest.dto.coefficients import QEPestCoefficientList
|
|
8
|
+
from pythonQEPest.dto.coefficients import QEPestCoefficientNumerics
|
|
9
|
+
|
|
10
|
+
from pythonQEPest.dto.normalisation import Normaliser
|
|
11
|
+
|
|
12
|
+
from pythonQEPest.dto.pest_type.PestTypeCoefficient import PestTypeCoefficient
|
|
13
|
+
from pythonQEPest.dto.pest_type.PestTypeConfig import PestTypeConfig
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"QEPestOutput",
|
|
17
|
+
"QEPestData",
|
|
18
|
+
"QEPestInput",
|
|
19
|
+
"QEPestFile",
|
|
20
|
+
"QEPestFormat",
|
|
21
|
+
"PestTypeConfig",
|
|
22
|
+
"PestTypeCoefficient",
|
|
23
|
+
"Normaliser",
|
|
24
|
+
"QEPestCoefficientNumerics",
|
|
25
|
+
"QEPestCoefficientList",
|
|
26
|
+
"QEPestCoefficient",
|
|
27
|
+
]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from pythonQEPest.dto.coefficients import QEPestCoefficientNumerics
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class QEPestCoefficient(BaseModel):
|
|
7
|
+
name: str
|
|
8
|
+
coefficients: QEPestCoefficientNumerics
|
|
9
|
+
|
|
10
|
+
def __init__(self, *args, **kwargs):
|
|
11
|
+
if args:
|
|
12
|
+
if len(args) != 1:
|
|
13
|
+
raise ValueError(f"Expected len(args) == 1, got {len(args)}")
|
|
14
|
+
|
|
15
|
+
# In case of {"test": [[1,2,3,4],[1,2,3,4],[1,2,3,4]}
|
|
16
|
+
if len(args[0]) == 1:
|
|
17
|
+
must_be_dict = args[0]
|
|
18
|
+
if isinstance(must_be_dict, dict):
|
|
19
|
+
key = list(must_be_dict.keys())[0]
|
|
20
|
+
coefficients = QEPestCoefficientNumerics(must_be_dict[key])
|
|
21
|
+
super().__init__(name=key, coefficients=coefficients)
|
|
22
|
+
|
|
23
|
+
# In case of ["test", [[1,2,3,4],[1,2,3,4],[1,2,3,4]]
|
|
24
|
+
elif len(args[0]) == 2:
|
|
25
|
+
arr = args[0]
|
|
26
|
+
if (
|
|
27
|
+
isinstance(arr, tuple)
|
|
28
|
+
or isinstance(arr, set)
|
|
29
|
+
or isinstance(arr, list)
|
|
30
|
+
):
|
|
31
|
+
name = arr[0]
|
|
32
|
+
coefficients = QEPestCoefficientNumerics(arr[1])
|
|
33
|
+
super().__init__(name=name, coefficients=coefficients)
|
|
34
|
+
|
|
35
|
+
elif kwargs:
|
|
36
|
+
super().__init__(**kwargs)
|
|
37
|
+
else:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
"No name and coefficients provided. Please provide either a "
|
|
40
|
+
+ "name and coefficients tuple or keyword arguments."
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
# Example usage
|
|
46
|
+
coefficients = QEPestCoefficient(
|
|
47
|
+
mw=[0.1, 0.2, 0.3, 0.4],
|
|
48
|
+
logp=[0.1, 0.2, 0.3, 0.4],
|
|
49
|
+
hba=[0.1, 0.2, 0.3, 0.4],
|
|
50
|
+
hbd=[0.1, 0.2, 0.3, 0.4],
|
|
51
|
+
rb=[0.1, 0.2, 0.3, 0.4],
|
|
52
|
+
ar=[0.1, 0.2, 0.3, 0.4],
|
|
53
|
+
)
|
|
54
|
+
print(coefficients)
|
|
55
|
+
|
|
56
|
+
coefficients_alternative = QEPestCoefficient([[0.1, 0.2, 0.3, 0.4] * 6])
|
|
57
|
+
print(coefficients_alternative)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Optional, List
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
from pythonQEPest.dto.coefficients import QEPestCoefficient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class QEPestCoefficientList(BaseModel):
|
|
9
|
+
name: Optional[str]
|
|
10
|
+
named_coefficients: List[QEPestCoefficient]
|
|
11
|
+
|
|
12
|
+
# TODO: Done all the cases and make some sort of docs here, pls
|
|
13
|
+
def __init__(self, *args, **kwargs):
|
|
14
|
+
if args:
|
|
15
|
+
named_coeff = args[0]
|
|
16
|
+
if len(named_coeff) == 1:
|
|
17
|
+
pass
|
|
18
|
+
elif len(named_coeff) > 1:
|
|
19
|
+
if isinstance(named_coeff, dict):
|
|
20
|
+
lst = [QEPestCoefficient({i: z}) for i, z in named_coeff.items()]
|
|
21
|
+
super().__init__(name="generic", named_coefficients=lst)
|
|
22
|
+
|
|
23
|
+
elif kwargs:
|
|
24
|
+
super().__init__(**kwargs)
|
|
25
|
+
else:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
"No name and coefficients provided. Please provide either a "
|
|
28
|
+
+ "name and coefficients tuple or keyword arguments."
|
|
29
|
+
)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
CoefficientTuple = tuple[float, float, float, float]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class QEPestCoefficientNumerics(BaseModel):
|
|
7
|
+
mw: CoefficientTuple
|
|
8
|
+
logp: CoefficientTuple
|
|
9
|
+
hba: CoefficientTuple
|
|
10
|
+
hbd: CoefficientTuple
|
|
11
|
+
rb: CoefficientTuple
|
|
12
|
+
ar: CoefficientTuple
|
|
13
|
+
|
|
14
|
+
def __init__(self, *args, **kwargs):
|
|
15
|
+
if args:
|
|
16
|
+
# In case of [[1,2,3,4],[1,2,3,4],[1,2,3,4]]
|
|
17
|
+
# I don't want it to be a pain to init D:
|
|
18
|
+
if len(args) != 1:
|
|
19
|
+
raise ValueError(
|
|
20
|
+
f"Expected a single list of coefficients, got {len(args)}"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
coefficients = args[0]
|
|
24
|
+
if len(coefficients) != 6:
|
|
25
|
+
raise ValueError(
|
|
26
|
+
"Expected list with structure [[1,2,3,4,5,6], ",
|
|
27
|
+
"[1,2,3,4,5,6], [1,...] ...] got ",
|
|
28
|
+
f"{len(coefficients)} and {coefficients}",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
coefficients_mapped = {
|
|
32
|
+
field: coefficients[i]
|
|
33
|
+
for i, field in enumerate(["mw", "logp", "hba", "hbd", "rb", "ar"])
|
|
34
|
+
}
|
|
35
|
+
super().__init__(**coefficients_mapped)
|
|
36
|
+
|
|
37
|
+
elif kwargs:
|
|
38
|
+
super().__init__(**kwargs)
|
|
39
|
+
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"No coefficients provided. Please provide either a ",
|
|
43
|
+
"list of coefficient tuples or keyword arguments.",
|
|
44
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from pythonQEPest.dto.coefficients.QEPestCoefficientNumerics import (
|
|
2
|
+
QEPestCoefficientNumerics,
|
|
3
|
+
)
|
|
4
|
+
from pythonQEPest.dto.coefficients.QEPestCoefficient import QEPestCoefficient
|
|
5
|
+
from pythonQEPest.dto.coefficients.QEPestCoefficientList import QEPestCoefficientList
|
|
6
|
+
|
|
7
|
+
__all__ = ["QEPestCoefficientNumerics", "QEPestCoefficient", "QEPestCoefficientList"]
|