tol-sdk 1.8.4__py3-none-any.whl → 1.8.6__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.
- tol/api_base/system.py +18 -0
- tol/api_client/api_datasource.py +33 -12
- tol/api_client/client.py +9 -6
- tol/api_client/factory.py +2 -13
- tol/api_client/view.py +1 -1
- tol/core/requested_fields.py +20 -0
- tol/dummy/__init__.py +6 -0
- tol/dummy/client.py +88 -0
- tol/dummy/converter.py +48 -0
- tol/dummy/dummy_datasource.py +105 -0
- tol/dummy/factory.py +95 -0
- tol/dummy/parser.py +70 -0
- tol/flows/converters/__init__.py +4 -0
- tol/flows/converters/combine_fields_converter.py +45 -0
- tol/flows/converters/default_field_value_if_missing_converter.py +43 -0
- tol/flows/converters/prefix_field_converter.py +49 -0
- tol/flows/converters/time_string_to_time.py +35 -0
- tol/sources/dummy.py +17 -0
- tol/sql/auth/blueprint.py +12 -5
- tol/sql/sql_datasource.py +1 -20
- tol/sql/standard/factory.py +2 -0
- tol/validators/ena_checklist.py +29 -9
- tol/validators/specimens_have_same_taxon.py +1 -1
- tol/validators/types.py +13 -6
- tol/validators/value_check.py +5 -4
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/METADATA +1 -1
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/RECORD +31 -20
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/WHEEL +0 -0
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/entry_points.txt +0 -0
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/licenses/LICENSE +0 -0
- {tol_sdk-1.8.4.dist-info → tol_sdk-1.8.6.dist-info}/top_level.txt +0 -0
tol/dummy/parser.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Genome Research Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import typing
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any, Iterable
|
|
10
|
+
|
|
11
|
+
from dateutil.parser import parse as dateutil_parse
|
|
12
|
+
|
|
13
|
+
from ..core import DataObject
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from ..core import DataSource
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
DummyResource = dict[str, Any]
|
|
20
|
+
DummyDoc = dict[str, list[DummyResource]]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Parser(ABC):
|
|
24
|
+
|
|
25
|
+
def parse_iterable(
|
|
26
|
+
self,
|
|
27
|
+
transfers: Iterable[DummyResource]
|
|
28
|
+
) -> Iterable[DataObject]:
|
|
29
|
+
"""
|
|
30
|
+
Parses an `Iterable` of Dummy transfer resources
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
self.parse(t) for t in transfers
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def parse(self, transfer: DummyResource) -> DataObject:
|
|
39
|
+
"""
|
|
40
|
+
Parses an individual Dummy transfer resource to a
|
|
41
|
+
`DataObject` instance
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class DefaultParser(Parser):
|
|
46
|
+
|
|
47
|
+
def __init__(self, data_source_dict: dict[str, DataSource]) -> None:
|
|
48
|
+
self.__dict = data_source_dict
|
|
49
|
+
|
|
50
|
+
def parse(self, transfer: DummyResource) -> DataObject:
|
|
51
|
+
ds = self.__dict[transfer.get('type')]
|
|
52
|
+
return ds.data_object_factory(
|
|
53
|
+
transfer.get('type'),
|
|
54
|
+
id_=transfer.get('id'),
|
|
55
|
+
attributes={
|
|
56
|
+
k: (
|
|
57
|
+
dateutil_parse(v)
|
|
58
|
+
if k in ['date'] and v is not None
|
|
59
|
+
else v
|
|
60
|
+
)
|
|
61
|
+
for k, v in transfer.items()
|
|
62
|
+
if k not in ['id', 'type', 'category']
|
|
63
|
+
},
|
|
64
|
+
to_one={
|
|
65
|
+
'category': ds.data_object_factory(
|
|
66
|
+
'category',
|
|
67
|
+
transfer.get('category')
|
|
68
|
+
) if 'category' in transfer else None
|
|
69
|
+
}
|
|
70
|
+
)
|
tol/flows/converters/__init__.py
CHANGED
|
@@ -56,4 +56,8 @@ from .sts_sample_to_casm_benchling_converter import StsSampleToCasmBenchlingConv
|
|
|
56
56
|
from .treeofsex_species_to_treeofsexwh_species_converter import TreeofsexSpeciesToTreeofsexwhSpeciesConverter # noqa F401
|
|
57
57
|
from .treeofsex_upload_to_treeofsex_attribute_converter import TreeofsexUploadToTreeofsexAttributeConverter # noqa F401
|
|
58
58
|
from .skip_null_fields_converter import SkipNullFieldsConverter # noqa F401
|
|
59
|
+
from .default_field_value_if_missing_converter import DefaultFieldValueIfMissingConverter # noqa F401
|
|
60
|
+
from .prefix_field_converter import PrefixFieldConverter # noqa F401
|
|
61
|
+
from .combine_fields_converter import CombineFieldsConverter # noqa F401
|
|
59
62
|
from .auto_detect_manifest_type_converter import AutoDetectManifestTypeConverter # noqa F401
|
|
63
|
+
from .time_string_to_time import TimeStringToTimeConverter # noqa F401
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Genome Research Ltd.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from tol.core import DataObject, DataObjectToDataObjectOrUpdateConverter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CombineFieldsConverter(DataObjectToDataObjectOrUpdateConverter):
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
13
|
+
class Config:
|
|
14
|
+
field1: str
|
|
15
|
+
field2: str
|
|
16
|
+
dest_field: str
|
|
17
|
+
lowercase_field1: bool
|
|
18
|
+
|
|
19
|
+
__slots__ = ('__config',)
|
|
20
|
+
__config: Config
|
|
21
|
+
|
|
22
|
+
def __init__(self, data_object_factory, config: Config) -> None:
|
|
23
|
+
super().__init__(data_object_factory)
|
|
24
|
+
self.__config = config
|
|
25
|
+
|
|
26
|
+
def convert(self, data_object: DataObject) -> Iterable[DataObject]:
|
|
27
|
+
"""
|
|
28
|
+
Concatenates the values of two fields and stores the result in a new field.
|
|
29
|
+
The first field's value may be lowercased if specified in the configuration.
|
|
30
|
+
The destination field name is given by the configuration.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
val1 = data_object.get_field_by_name(self.__config.field1)
|
|
34
|
+
val2 = data_object.get_field_by_name(self.__config.field2)
|
|
35
|
+
attributes = dict(data_object.attributes)
|
|
36
|
+
|
|
37
|
+
if val1 is not None and val2 is not None:
|
|
38
|
+
part1 = str(val1).lower() if self.__config.lowercase_field1 else str(val1)
|
|
39
|
+
attributes[self.__config.dest_field] = f'{part1}{val2}'
|
|
40
|
+
|
|
41
|
+
yield self._data_object_factory(
|
|
42
|
+
data_object.type,
|
|
43
|
+
data_object.id,
|
|
44
|
+
attributes=attributes,
|
|
45
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Genome Research Ltd.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from tol.core import DataObject, DataObjectToDataObjectOrUpdateConverter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DefaultFieldValueIfMissingConverter(DataObjectToDataObjectOrUpdateConverter):
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
13
|
+
class Config:
|
|
14
|
+
field_name: str
|
|
15
|
+
default_value: str
|
|
16
|
+
|
|
17
|
+
__slots__ = ['__config']
|
|
18
|
+
__config: Config
|
|
19
|
+
|
|
20
|
+
def __init__(self, data_object_factory, config: Config) -> None:
|
|
21
|
+
super().__init__(data_object_factory)
|
|
22
|
+
self.__config = config
|
|
23
|
+
|
|
24
|
+
def convert(self, data_object: DataObject) -> Iterable[DataObject]:
|
|
25
|
+
"""
|
|
26
|
+
Adds a default value for a configured field if missing, empty, or None
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
attributes_obj = data_object.attributes
|
|
30
|
+
if hasattr(attributes_obj, 'get_field_by_name'):
|
|
31
|
+
current_value = attributes_obj.get_field_by_name(self.__config.field_name)
|
|
32
|
+
else:
|
|
33
|
+
current_value = attributes_obj.get(self.__config.field_name)
|
|
34
|
+
attributes = dict(attributes_obj)
|
|
35
|
+
if not current_value:
|
|
36
|
+
attributes[self.__config.field_name] = self.__config.default_value
|
|
37
|
+
|
|
38
|
+
ret = self._data_object_factory(
|
|
39
|
+
data_object.type,
|
|
40
|
+
data_object.id,
|
|
41
|
+
attributes=attributes
|
|
42
|
+
)
|
|
43
|
+
yield ret
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Genome Research Ltd.
|
|
2
|
+
# SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Iterable
|
|
6
|
+
|
|
7
|
+
from tol.core import DataObject, DataObjectToDataObjectOrUpdateConverter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PrefixFieldConverter(DataObjectToDataObjectOrUpdateConverter):
|
|
11
|
+
|
|
12
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
13
|
+
class Config:
|
|
14
|
+
field_name: str
|
|
15
|
+
prefix: str
|
|
16
|
+
|
|
17
|
+
__slots__ = ['__config']
|
|
18
|
+
__config: Config
|
|
19
|
+
|
|
20
|
+
def __init__(self, data_object_factory, config: Config) -> None:
|
|
21
|
+
super().__init__(data_object_factory)
|
|
22
|
+
self.__config = config
|
|
23
|
+
self._data_object_factory = data_object_factory
|
|
24
|
+
|
|
25
|
+
def convert(self, data_object: DataObject) -> Iterable[DataObject]:
|
|
26
|
+
"""
|
|
27
|
+
Ensures the configured field value
|
|
28
|
+
starts with the configured prefix. If the field is None, it is
|
|
29
|
+
left as-is.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
value = data_object.get_field_by_name(
|
|
33
|
+
self.__config.field_name
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if value is not None:
|
|
37
|
+
value_str = str(value)
|
|
38
|
+
if not value_str.startswith(self.__config.prefix):
|
|
39
|
+
value = f'{self.__config.prefix}{value_str}'
|
|
40
|
+
|
|
41
|
+
ret = self._data_object_factory(
|
|
42
|
+
data_object.type,
|
|
43
|
+
data_object.id,
|
|
44
|
+
attributes={
|
|
45
|
+
**data_object.attributes,
|
|
46
|
+
self.__config.field_name: value
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
yield ret
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Genome Research Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from datetime import time
|
|
7
|
+
|
|
8
|
+
from tol.core import DataObject
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Converter:
|
|
12
|
+
def convert(self, obj):
|
|
13
|
+
raise NotImplementedError()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TimeStringToTimeConverter(Converter):
|
|
17
|
+
"""
|
|
18
|
+
Converts string fields representing time in HH:MM (24-hour) format to Python time objects.
|
|
19
|
+
If the string is not in HH:MM, tries to append ':00' and parse as HH:MM:SS.
|
|
20
|
+
"""
|
|
21
|
+
def __init__(self, field: str):
|
|
22
|
+
self.field = field
|
|
23
|
+
|
|
24
|
+
def convert(self, obj: DataObject) -> DataObject:
|
|
25
|
+
value = obj.attributes.get(self.field)
|
|
26
|
+
if isinstance(value, str):
|
|
27
|
+
match = re.match(r'^(\d{1,2}):(\d{2})(?::(\d{2}))?$', value)
|
|
28
|
+
if match:
|
|
29
|
+
h, m = int(match.group(1)), int(match.group(2))
|
|
30
|
+
s = int(match.group(3)) if match.group(3) else 0
|
|
31
|
+
try:
|
|
32
|
+
obj.attributes[self.field] = time(h, m, s)
|
|
33
|
+
except ValueError:
|
|
34
|
+
pass
|
|
35
|
+
return obj
|
tol/sources/dummy.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2024 Genome Research Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
from ..core import (
|
|
6
|
+
core_data_object
|
|
7
|
+
)
|
|
8
|
+
from ..dummy import (
|
|
9
|
+
DummyDataSource,
|
|
10
|
+
create_dummy_datasource
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dummy(**kwargs) -> DummyDataSource:
|
|
15
|
+
dummy = create_dummy_datasource()
|
|
16
|
+
core_data_object(dummy)
|
|
17
|
+
return dummy
|
tol/sql/auth/blueprint.py
CHANGED
|
@@ -8,7 +8,6 @@ from datetime import datetime, timedelta
|
|
|
8
8
|
from typing import Any, Callable, Optional
|
|
9
9
|
from urllib.parse import urlencode
|
|
10
10
|
|
|
11
|
-
import requests
|
|
12
11
|
from requests.auth import HTTPBasicAuth
|
|
13
12
|
|
|
14
13
|
from .models import ModelClass, ModelTuple, create_models
|
|
@@ -228,9 +227,14 @@ class DbAuthManager(AuthManager):
|
|
|
228
227
|
Raises:
|
|
229
228
|
requests.HTTPError: If the user info request fails
|
|
230
229
|
"""
|
|
230
|
+
|
|
231
|
+
client = HttpClient()
|
|
232
|
+
|
|
233
|
+
session = client.get_session()
|
|
234
|
+
|
|
231
235
|
headers = {'Authorization': f'Bearer {token}'}
|
|
232
236
|
|
|
233
|
-
r =
|
|
237
|
+
r = session.get(self.__config.user_info_url, headers=headers)
|
|
234
238
|
r.raise_for_status()
|
|
235
239
|
|
|
236
240
|
json_return = r.json()
|
|
@@ -401,13 +405,16 @@ class DbAuthManager(AuthManager):
|
|
|
401
405
|
Raises:
|
|
402
406
|
requests.HTTPError: If the token request fails
|
|
403
407
|
"""
|
|
404
|
-
|
|
408
|
+
client = HttpClient()
|
|
409
|
+
|
|
410
|
+
session = client.get_session()
|
|
411
|
+
r = session.post(
|
|
405
412
|
self.__config.token_url,
|
|
406
|
-
auth=self.__basic_auth(),
|
|
407
413
|
data=self.__token_post_data(code),
|
|
414
|
+
auth=self.__basic_auth()
|
|
408
415
|
)
|
|
409
|
-
r.raise_for_status()
|
|
410
416
|
|
|
417
|
+
r.raise_for_status()
|
|
411
418
|
return r.json()
|
|
412
419
|
|
|
413
420
|
def __basic_auth(self) -> HTTPBasicAuth:
|
tol/sql/sql_datasource.py
CHANGED
|
@@ -42,6 +42,7 @@ from ..core.operator import (
|
|
|
42
42
|
Upserter,
|
|
43
43
|
)
|
|
44
44
|
from ..core.relationship import RelationshipConfig
|
|
45
|
+
from ..core.requested_fields import requested_fields_to_tree
|
|
45
46
|
|
|
46
47
|
if typing.TYPE_CHECKING:
|
|
47
48
|
from ..core.session import OperableSession
|
|
@@ -56,26 +57,6 @@ FilterFactory = Callable[[DataSourceFilter], DatabaseFilter]
|
|
|
56
57
|
SorterFactory = Callable[[Optional[str]], DatabaseSorter]
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
def requested_fields_to_tree(func):
|
|
60
|
-
"""
|
|
61
|
-
Allows `requested_fields` keyword arguments to be supplied to methods if a
|
|
62
|
-
`requested_tree` object has not been given.
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
|
-
def wrapper(self, tablename, *args, **kwargs):
|
|
66
|
-
if 'requested_fields' in kwargs:
|
|
67
|
-
if 'requested_tree' in kwargs:
|
|
68
|
-
msg = 'Both requested_fields and requested_tree arguments given'
|
|
69
|
-
raise TypeError(msg)
|
|
70
|
-
flds = kwargs.pop('requested_fields')
|
|
71
|
-
kwargs['requested_tree'] = ReqFieldsTree(
|
|
72
|
-
tablename, self, requested_fields=flds
|
|
73
|
-
)
|
|
74
|
-
return func(self, tablename, *args, **kwargs)
|
|
75
|
-
|
|
76
|
-
return wrapper
|
|
77
|
-
|
|
78
|
-
|
|
79
60
|
class SqlDataSource(
|
|
80
61
|
Counter,
|
|
81
62
|
Cursor,
|
tol/sql/standard/factory.py
CHANGED
|
@@ -485,6 +485,8 @@ def create_standard_models(
|
|
|
485
485
|
foreign_keys=[data_source_config_id]
|
|
486
486
|
)
|
|
487
487
|
|
|
488
|
+
source_order: Mapped[list[str]] = mapped_column(JSONB, nullable=True)
|
|
489
|
+
|
|
488
490
|
class DataSourceConfigSummary(base_model_class):
|
|
489
491
|
__tablename__ = 'data_source_config_summary'
|
|
490
492
|
|
tol/validators/ena_checklist.py
CHANGED
|
@@ -12,6 +12,11 @@ from tol.sources.ena import ena
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class EnaChecklistValidator(Validator):
|
|
15
|
+
ENA_TO_INTERNAL_FIELD_MAP = {
|
|
16
|
+
'geographic location (longitude)': 'DECIMAL_LONGITUDE',
|
|
17
|
+
'geographic location (latitude)': 'DECIMAL_LATITUDE',
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
"""
|
|
16
21
|
validates the ENA_CHECKLIST for each samples
|
|
17
22
|
"""
|
|
@@ -37,16 +42,18 @@ class EnaChecklistValidator(Validator):
|
|
|
37
42
|
field_name = key
|
|
38
43
|
if 'field' in validation:
|
|
39
44
|
field_name = validation['field']
|
|
45
|
+
internal_field = self.ENA_TO_INTERNAL_FIELD_MAP.get(field_name, field_name)
|
|
46
|
+
|
|
40
47
|
if 'mandatory' in validation and key not in obj.attributes:
|
|
41
|
-
self.add_error(object_id=obj.id, detail='Must be given', field=[
|
|
48
|
+
self.add_error(object_id=obj.id, detail='Must be given', field=[internal_field])
|
|
42
49
|
continue
|
|
43
50
|
if 'mandatory' in validation and obj.attributes[key] is None:
|
|
44
|
-
self.add_error(object_id=obj.id, detail='Must be given', field=[
|
|
51
|
+
self.add_error(object_id=obj.id, detail='Must be given', field=[internal_field])
|
|
45
52
|
continue
|
|
46
53
|
if 'mandatory' in validation and obj.attributes.get(key) == '':
|
|
47
54
|
self.add_error(
|
|
48
55
|
object_id=obj.id,
|
|
49
|
-
detail='Must not be empty', field=[
|
|
56
|
+
detail='Must not be empty', field=[internal_field]
|
|
50
57
|
)
|
|
51
58
|
|
|
52
59
|
if 'restricted text' in validation and key in obj.attributes:
|
|
@@ -55,12 +62,25 @@ class EnaChecklistValidator(Validator):
|
|
|
55
62
|
regex = condition
|
|
56
63
|
compiled_re = re.compile(regex)
|
|
57
64
|
if not compiled_re.search(obj.attributes.get(key)):
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
if internal_field in ('DECIMAL_LONGITUDE', 'DECIMAL_LATITUDE'):
|
|
66
|
+
self.add_error(
|
|
67
|
+
object_id=obj.id,
|
|
68
|
+
detail=(
|
|
69
|
+
f'Value for {internal_field} must be a valid decimal in the'
|
|
70
|
+
f' format required by ENA (e.g. 12.3456). '
|
|
71
|
+
f'See ENA checklist for accepted format.',
|
|
72
|
+
),
|
|
73
|
+
field=[internal_field]
|
|
74
|
+
)
|
|
75
|
+
else:
|
|
76
|
+
self.add_error(
|
|
77
|
+
object_id=obj.id,
|
|
78
|
+
detail=(
|
|
79
|
+
f'Value for {internal_field} must match the required pattern.'
|
|
80
|
+
),
|
|
81
|
+
field=[internal_field]
|
|
82
|
+
)
|
|
62
83
|
|
|
63
|
-
# Check against allowed values
|
|
64
84
|
if 'text choice' in validation and key in obj.attributes:
|
|
65
85
|
for condition in validation:
|
|
66
86
|
if isinstance(condition, list):
|
|
@@ -69,5 +89,5 @@ class EnaChecklistValidator(Validator):
|
|
|
69
89
|
[x.lower() for x in allowed_values]:
|
|
70
90
|
self.add_error(
|
|
71
91
|
object_id=obj.id,
|
|
72
|
-
detail='Must be in allowed values', field=[
|
|
92
|
+
detail='Must be in allowed values', field=[internal_field]
|
|
73
93
|
)
|
|
@@ -53,7 +53,7 @@ class SpecimensHaveSameTaxonValidator(Validator):
|
|
|
53
53
|
if specimen_id in self.__seen and taxon_id != self.__seen[specimen_id]:
|
|
54
54
|
self.add_error(
|
|
55
55
|
object_id=obj.id,
|
|
56
|
-
detail='
|
|
56
|
+
detail='All samples from the same specimen_ID must have the same TAXON_ID.',
|
|
57
57
|
field=self.__config.specimen_id_field,
|
|
58
58
|
)
|
|
59
59
|
if specimen_id not in self.__seen:
|
tol/validators/types.py
CHANGED
|
@@ -55,12 +55,19 @@ class TypesValidator(Validator):
|
|
|
55
55
|
continue
|
|
56
56
|
type_class = type_map.get(expected_type)
|
|
57
57
|
if type_class and not isinstance(actual_value, type_class):
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
if expected_type == 'time':
|
|
59
|
+
self.__add_result(
|
|
60
|
+
obj,
|
|
61
|
+
key,
|
|
62
|
+
detail='Only HH:MM format is accepted.',
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
self.__add_result(
|
|
66
|
+
obj,
|
|
67
|
+
key,
|
|
68
|
+
detail=f'Field {key} value "{actual_value}" is not of type '
|
|
69
|
+
f'"{expected_type}"',
|
|
70
|
+
)
|
|
64
71
|
if type_class and isinstance(actual_value, type_class):
|
|
65
72
|
# Special case for bool since isinstance(True, int) is True
|
|
66
73
|
if expected_type == 'int' and isinstance(actual_value, bool):
|
tol/validators/value_check.py
CHANGED
|
@@ -26,11 +26,12 @@ class ValueCheckValidator(Validator):
|
|
|
26
26
|
self.__config = config
|
|
27
27
|
|
|
28
28
|
def _validate_data_object(self, obj: DataObject) -> None:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if obj.attributes.get(self.__config.field) == self.__config.value:
|
|
29
|
+
if obj.attributes.get(self.__config.field) != self.__config.value:
|
|
32
30
|
self.add_error(
|
|
33
31
|
object_id=obj.id,
|
|
34
|
-
detail=
|
|
32
|
+
detail=(
|
|
33
|
+
f"Expected '{self.__config.value}' for field "
|
|
34
|
+
f"'{self.__config.field}', but got '{obj.attributes.get(self.__config.field)}'"
|
|
35
|
+
),
|
|
35
36
|
field=self.__config.field,
|
|
36
37
|
)
|
|
@@ -10,7 +10,7 @@ tol/api_base/blueprint.py,sha256=AHCvBUwvXbXtKuRv_MmQNtMrXnVEtdNVZMh-_CMsYY0,193
|
|
|
10
10
|
tol/api_base/controller.py,sha256=5Fp2FobjXXPlongu1j7adGJKlSVufW0_jpk_wZIMRa4,26818
|
|
11
11
|
tol/api_base/data_upload.py,sha256=z8Kp4BWQ6q06vT9ZwS8cgedWuGxBwZLkT6xpHk1K008,2939
|
|
12
12
|
tol/api_base/pipeline_steps.py,sha256=Y3rcqQh1w4F0tCxMLGI3H8W3pbl54OWLrUYbBR44Y4A,5850
|
|
13
|
-
tol/api_base/system.py,sha256=
|
|
13
|
+
tol/api_base/system.py,sha256=NLvG1eQnIgOa5H_NYufVn87NZtpazpmITPFJqm5KMZI,1822
|
|
14
14
|
tol/api_base/auth/__init__.py,sha256=LNbVtQDlzKWzLoCmxAPSIAcM6mlVMnn0Aj9HFUudseo,460
|
|
15
15
|
tol/api_base/auth/asserts.py,sha256=3kHP2yW6HZxevIgeO8Rl3iKovi8WHZcguVbPSFUjawQ,2765
|
|
16
16
|
tol/api_base/auth/blueprint.py,sha256=-Bv5FNnyr73NpdRlqjkq3h4J5-29BrM01bxfBPPHUV8,2209
|
|
@@ -31,15 +31,15 @@ tol/api_base/misc/list_get_parameters.py,sha256=6DtUgfKPTok3utKPOxceJdwV_ZnJ5nis
|
|
|
31
31
|
tol/api_base/misc/relation_url.py,sha256=qfo-okp8Gv9-PEDghMfGZ2pHdYbHRhohvA9v3Govtlo,1127
|
|
32
32
|
tol/api_base/misc/stats_parameters.py,sha256=IVpHqUeGQyjuih59jwqT-fIQMCBeESi2T9b4r9i4J28,1721
|
|
33
33
|
tol/api_client/__init__.py,sha256=58SAywuMrIUCBAY9us_d_RLTMnaUTYWWts0LRQC5wLo,187
|
|
34
|
-
tol/api_client/api_datasource.py,sha256=
|
|
35
|
-
tol/api_client/client.py,sha256=
|
|
34
|
+
tol/api_client/api_datasource.py,sha256=AYvaOqsrTuBu8CTRSmm9Fo-VHReJAQjpZUSZc63dLXQ,15161
|
|
35
|
+
tol/api_client/client.py,sha256=H4X5_Jj-JmnaeAN85ghCWGtQUhYprOZqJXNmCJq_Vz0,14216
|
|
36
36
|
tol/api_client/converter.py,sha256=g32fjqga4mC923n95HmQImPuawMfeb9rQcl3ZxUWP2s,4463
|
|
37
37
|
tol/api_client/exception.py,sha256=MkvJaIyRVCzQ2rKOYnCOcT747mpOeQwGJJl3Kkb1BsQ,3999
|
|
38
|
-
tol/api_client/factory.py,sha256=
|
|
38
|
+
tol/api_client/factory.py,sha256=PHRMpCJwBcgaEU_9x_9hNM__RI0udTN7Q7xzAxfVcNk,3492
|
|
39
39
|
tol/api_client/filter.py,sha256=D49RIai5Yj4CiQvIkgaEIXdw_oSU7CD5cn9SdXWRYXU,1474
|
|
40
40
|
tol/api_client/parser.py,sha256=88Q2RlPwn9JJsSIukC2_yxfaM2AaXjsGuDOESqY7se4,8351
|
|
41
41
|
tol/api_client/validate.py,sha256=9wFZotTJ4fI21BdO5AuMZok0rDQ4s8zM_WojLdVvA0A,3071
|
|
42
|
-
tol/api_client/view.py,sha256=
|
|
42
|
+
tol/api_client/view.py,sha256=LGojaC0vCPkjemV8A83b3T_uDVQ002nQYgAZN7JapIU,8763
|
|
43
43
|
tol/barcodes/__init__.py,sha256=k9KoBO0xJyxg1kanLrN0E3HuVSbbpfSInGEsWX-8UsY,214
|
|
44
44
|
tol/barcodes/main.py,sha256=QxueF2AdrclIaQuu7W4lb4eF5tU_l-p3UMsDFIYgCfo,6213
|
|
45
45
|
tol/benchling/__init__.py,sha256=9VIL6PBxvZRaBrN1j2Di75MdEY261kEHs_D-lUutIlg,229
|
|
@@ -100,7 +100,7 @@ tol/core/datasource_utils.py,sha256=18mwvFmtJL73_mxtFb56rKXZCGCtZFoEb8sWFKn3Yf0,
|
|
|
100
100
|
tol/core/factory.py,sha256=pLLu8l-yK8QaLTt52izMhKZ2VlFHqRQlUHwMaLL6DI4,9156
|
|
101
101
|
tol/core/http_client.py,sha256=QyZarplEHVYIrqEfrySeHbawfbnBU4nN62TLt41x4tY,2242
|
|
102
102
|
tol/core/relationship.py,sha256=etdyCjLbfi2tgkaqzE6cntpNtTzgT_jOPGeNKmPu5yc,4624
|
|
103
|
-
tol/core/requested_fields.py,sha256
|
|
103
|
+
tol/core/requested_fields.py,sha256=-UXBnalzK1Fd-6zaYbZxlvlQOqIndOW0Zjvr4Z5UgaQ,7397
|
|
104
104
|
tol/core/session.py,sha256=6AohamIEfB8oV3Z414ishKqmlTgVfUaGYWzvxLgZgM4,3803
|
|
105
105
|
tol/core/validate.py,sha256=kFRPhgYyeZisGbSXR7S1pmD8jvz_aZ7RX40bT-gD_iA,4083
|
|
106
106
|
tol/core/operator/__init__.py,sha256=ircyLdj45IlaL5k0cDzWHjEr-_66BAx7YRbuEvWF30k,1826
|
|
@@ -124,6 +124,12 @@ tol/core/operator/statter.py,sha256=qCyJ6AOeyvDy5t0tQEJr6-p-5Rw6vVwlQ-PXTmHXn8c,
|
|
|
124
124
|
tol/core/operator/summariser.py,sha256=l7kmcvI_s9TkLGQf9XModrMziqvxKH6jdyo-mROUUxU,5823
|
|
125
125
|
tol/core/operator/updater.py,sha256=uLx02Mw77c6sHCw76pdJIArlGbk6N7HDv4FUf0C542w,1055
|
|
126
126
|
tol/core/operator/upserter.py,sha256=0sAx13I-_YiXnzGC7t8N23BZFiil9FxT9S0WJ8Z6EKk,2603
|
|
127
|
+
tol/dummy/__init__.py,sha256=A7mBVh-j6rmSZ_Chk3FodUCSb42vreeP4pG0GKz0ZCM,193
|
|
128
|
+
tol/dummy/client.py,sha256=tU5Oj-XWuvo2G6JYUyHvC6Oc5TZ--tbWsMf8-DA0pss,2516
|
|
129
|
+
tol/dummy/converter.py,sha256=WU4y3VyYDhJLERqBqROKmEK0glwWlGPeF9XoDYtQY50,1068
|
|
130
|
+
tol/dummy/dummy_datasource.py,sha256=wgbO2qycdApAARL3y_9WsiUftyMCxoVuQktNzBAjAp8,2862
|
|
131
|
+
tol/dummy/factory.py,sha256=Jf5Oo5D_FlQMthIMLEF2kROxpJIoXiB7qG8DsCJdGn8,2105
|
|
132
|
+
tol/dummy/parser.py,sha256=xQRTLRdMdwolkQ1KXkS5RxTDiKwrLWwLd3lpXIAGCew,1793
|
|
127
133
|
tol/elastic/__init__.py,sha256=5zOZ83jkup26AHrTEhLw4Pve548uN3gnzC6fe_mtm-Q,166
|
|
128
134
|
tol/elastic/elastic_datasource.py,sha256=XPMr5sn64qPMe9H6Rf6xd-6TgMagVJzaZ2J1wZfRvwM,42041
|
|
129
135
|
tol/elastic/runtime_fields.py,sha256=08N8JnbF1U2znSK9mpuCoyQjxiCShuxsggljptfCVoE,4479
|
|
@@ -149,7 +155,7 @@ tol/flows/__init__.py,sha256=M7iSvnBJs6fJ8M38cW0bYQa9WW0TN8FHAMjIHPDNAJ4,166
|
|
|
149
155
|
tol/flows/logger.py,sha256=rWXbaknGcPEZRFvC1CiB1qkhFRZsQk435w7VyJ3cpyw,170
|
|
150
156
|
tol/flows/secrets.py,sha256=1mlbsxaahzYRfVAx3XdztHOmUCtDMSJDzHysdbaCtj0,352
|
|
151
157
|
tol/flows/sequencing_submissions.py,sha256=ukz_y5be-BCBN2y3JPQ2EK6b3jwOCh-187j-jnw3EUY,11027
|
|
152
|
-
tol/flows/converters/__init__.py,sha256=
|
|
158
|
+
tol/flows/converters/__init__.py,sha256=m2q04EXBdG5SZ3ieMsm_ork9eUuj-g8z6ulMOlVHs4U,6641
|
|
153
159
|
tol/flows/converters/auto_detect_manifest_type_converter.py,sha256=uHakTmVHMbm2kFQOWaAv8ynD9ueh7p-kq6wmfEGnmEw,1361
|
|
154
160
|
tol/flows/converters/benchling_entity_to_benchling_worklist_item_converter_factory.py,sha256=PN27fcvN4JLBnLrtPPAot1cWjAwPQHVcIDoMfPDeKzU,1210
|
|
155
161
|
tol/flows/converters/benchling_extraction_to_elastic_extraction_converter.py,sha256=S8pbmIeKlcXrLPRJHYBUGP0-Q7jTOV2QQk2TeA2naWo,1966
|
|
@@ -165,6 +171,8 @@ tol/flows/converters/bioscan_qc_specimen_to_elastic_sample_update_converter.py,s
|
|
|
165
171
|
tol/flows/converters/bioscan_qc_uksi_entry_to_elastic_sample_update_converter.py,sha256=F_81d9XweccUWPppIzuq1rHVEaIctwoFBmWf9g9jWtw,650
|
|
166
172
|
tol/flows/converters/bold_bin_to_elastic_sample_update_converter.py,sha256=viQNm-3PGCTf8zUHax3H1PpZSfW2P9HCocVL8h9O74o,579
|
|
167
173
|
tol/flows/converters/bold_sample_to_elastic_sample_update_converter.py,sha256=eehTzW37x4IINKtMdXDrgSk-Iptx1k8_IR1WDNX1JtA,585
|
|
174
|
+
tol/flows/converters/combine_fields_converter.py,sha256=S_Bm4dix18kgmfPF6TEUTp05Wv53hJPR2MjtGjn8BrI,1528
|
|
175
|
+
tol/flows/converters/default_field_value_if_missing_converter.py,sha256=81xKskti_cpAEIs7A1arG9nlaIvrl1TkQj6URvFI3o0,1397
|
|
168
176
|
tol/flows/converters/elastic_object_to_portaldb_object_converter.py,sha256=YVUTYMvsLwDfq33WQbyIDl2d3D5jEcMxhqnGhx3HtQ4,1600
|
|
169
177
|
tol/flows/converters/elastic_sample_to_benchling_tissue_converter.py,sha256=zsybAg8ZyRI_ibTpkvSy7Zn2lNzUYmhc9SNMQQFj1HE,1103
|
|
170
178
|
tol/flows/converters/elastic_sample_to_benchling_tissue_update_converter.py,sha256=2Y_Kn8Z5_Sksgah9p4Q3Gy4Em3KOAG-rnzmy4_jtod0,3726
|
|
@@ -190,6 +198,7 @@ tol/flows/converters/labwhere_location_to_sts_tray_converter.py,sha256=dSBP5Hfdv
|
|
|
190
198
|
tol/flows/converters/mlwh_extraction_to_elastic_extraction_converter.py,sha256=047eU_3Zb3ZlJ8utrk3o_VW7jifv5VO0WxgmFg-QkaE,1195
|
|
191
199
|
tol/flows/converters/mlwh_run_data_to_elastic_run_data_converter.py,sha256=f0I_N8DH_9Kom4Fpl_O7hKTUPPiURWeyhxWN3XFLmLk,1313
|
|
192
200
|
tol/flows/converters/mlwh_sequencing_request_to_elastic_sequencing_request_converter.py,sha256=zXYkq37qsvHEY7O1zh2vOoMBdt8hE7T1SuFIOR2DQrM,1209
|
|
201
|
+
tol/flows/converters/prefix_field_converter.py,sha256=MHYvreOygcEOZdqF067MrDmWQWiLcp_c0w5-juK4oY4,1435
|
|
193
202
|
tol/flows/converters/skip_null_fields_converter.py,sha256=IlLrX31XWpQ58indW-hk9eApMunu2HGFhMConYDmxtU,1226
|
|
194
203
|
tol/flows/converters/sts_banked_sample_to_elastic_sample_converter.py,sha256=HK_3Q60E6CKCT8YSWbZXu0Wqs2mmBLYhcD0OEwXM4Qs,688
|
|
195
204
|
tol/flows/converters/sts_manifest_to_elastic_manifest_converter.py,sha256=Ko3ulOFXXYaZB1YUiwu_HPlP5oPZeI65hViAZ05cioQ,1806
|
|
@@ -198,6 +207,7 @@ tol/flows/converters/sts_sample_project_to_elastic_sample_converter.py,sha256=YE
|
|
|
198
207
|
tol/flows/converters/sts_sample_to_casm_benchling_converter.py,sha256=Zo577u2v5_Fela2uQVtZsGZmHq4bLecTCC4Ewvq61Xo,39414
|
|
199
208
|
tol/flows/converters/sts_sampleset_to_elastic_sampleset_converter.py,sha256=PUP0Qjy9wTmqp5GHNEd9fukqtWdoMvDfs4rJisfLzcc,3197
|
|
200
209
|
tol/flows/converters/sts_species_to_elastic_species_converter.py,sha256=ELZ_ML8vPlLkfXrx0B_wxUWiPyxkI8UXgSPQZCKknXU,1164
|
|
210
|
+
tol/flows/converters/time_string_to_time.py,sha256=myo9K9pRMT6GtT5vXf3c0UDK5n8bMVbvW7ewhSPkecU,1062
|
|
201
211
|
tol/flows/converters/tolid_specimen_to_elastic_tolid_converter.py,sha256=4Ird6ATYjsjSFZ1AGsxpuWdQ6QtQTvKJ-rF-_TCC_rg,1072
|
|
202
212
|
tol/flows/converters/tolqc_data_to_elastic_run_data_converter.py,sha256=f9PYnsswikskvXpnSlrfYN7wXfyn8iXQpg996ZsQHbw,3799
|
|
203
213
|
tol/flows/converters/tolqc_sample_to_elastic_sequencing_request_converter.py,sha256=MzPcO75Z_3-6nsWC8X0kmmmxJTurV_HOZhseC-tWdFo,1397
|
|
@@ -267,6 +277,7 @@ tol/sources/bioscan_qc.py,sha256=XpU7gufFOcjtUEgW65nNGATrpt6utWhON-kCogDhEaM,171
|
|
|
267
277
|
tol/sources/bold.py,sha256=vbJGRaO-xJj7-mNcaUEPC_YTNTlb1_442YP3sGvCbu4,674
|
|
268
278
|
tol/sources/copo.py,sha256=L03fFT3x1xWe3IpbijoVnKDGlncVQPmKhuGcAjdAuYg,485
|
|
269
279
|
tol/sources/defaults.py,sha256=wnxLSqZzxgQYwSJCshzfjJ0gzw7Au7Ag75aC3C9IzeA,1463
|
|
280
|
+
tol/sources/dummy.py,sha256=p2eZWpsKd2bv_uZW0sBGCgpr0Sye_35g1VEeWOlNZlc,328
|
|
270
281
|
tol/sources/elastic.py,sha256=LNl0MKT8jZta2aCB9Jjl4lJkC9b8Qd4TjgmyqLgQxWw,1058
|
|
271
282
|
tol/sources/ena.py,sha256=BnCq_tGAOQ3Ij0afcArjFaMjYG1MqGRJUrGZhnpvxYY,615
|
|
272
283
|
tol/sources/gap.py,sha256=h3dwi-Y19kT6vRsU-GHtZBgD-piaHR4r4wqU7qBIGww,1544
|
|
@@ -302,16 +313,16 @@ tol/sql/relationship.py,sha256=EClMgVx_If5nZV9MV99TQk7Wr7uACWetwQdWAliM5XI,2891
|
|
|
302
313
|
tol/sql/session.py,sha256=VmqTegr4L2X2zvaOJCpwSrkVRx8fc1RVL0drkL2MXu8,806
|
|
303
314
|
tol/sql/sort.py,sha256=ENrjHGgj4fZtXKmkdlkv1HRi1X14SVlcl-tp8Pu7G0k,2553
|
|
304
315
|
tol/sql/sql_converter.py,sha256=taD5FRwadvw2bBaUGrCIiUs0-ATAbBnRYI1M7xe3yEc,4618
|
|
305
|
-
tol/sql/sql_datasource.py,sha256=
|
|
316
|
+
tol/sql/sql_datasource.py,sha256=RnPkfg4TSHaxfJG77eBrNgBAKexJaypc-fvfFzssdac,15598
|
|
306
317
|
tol/sql/action/__init__.py,sha256=T1zAsCza_lvsNtXF1ecSLt9OFGup8tGnIs68YylBmXI,142
|
|
307
318
|
tol/sql/action/factory.py,sha256=HkareJp_57ud0_Bdd9Kwz3_Rnq2l211sGJgftohFAHg,3589
|
|
308
319
|
tol/sql/auth/__init__.py,sha256=e3JuwugXmXobklqZ1Mt1w03qPgb1WdUaJVM7oblzHyk,202
|
|
309
|
-
tol/sql/auth/blueprint.py,sha256=
|
|
320
|
+
tol/sql/auth/blueprint.py,sha256=1B4RqYKxOfoBhrNY6qrsPO1kHSfaUfKyoAzjPSCZGs4,26131
|
|
310
321
|
tol/sql/auth/models.py,sha256=U4CsKMMyzGMg6hj4tp_iRenr3_Q--64WJmHWvxQ2--Q,12297
|
|
311
322
|
tol/sql/pipeline_step/__init__.py,sha256=O7u4RrLfuoB0mwLcPxFoUrdTBZGB_4bE1vWCn5ho-qw,147
|
|
312
323
|
tol/sql/pipeline_step/factory.py,sha256=FaO61WLST4GQdAWuCIGqAvpVBvzkfBOgZWgHEZy2OXo,5483
|
|
313
324
|
tol/sql/standard/__init__.py,sha256=2NbLXFk0rneGZosZ2ESIRcT0WMK0KncmPWaLPqvX-i4,142
|
|
314
|
-
tol/sql/standard/factory.py,sha256=
|
|
325
|
+
tol/sql/standard/factory.py,sha256=zCCbPBpPwoTNKacqSjWXFY8ROGzHx8IaL7TsrGUeelM,20089
|
|
315
326
|
tol/status/__init__.py,sha256=sBo-j1wCmberl89uryVCBEJk8ohbfsYhaNpIp_brR9Y,146
|
|
316
327
|
tol/status/status_datasource.py,sha256=UYU2vB561XRWY8y2dY96qHiWXy15xaHxsbGCVCIUnqs,1916
|
|
317
328
|
tol/sts/__init__.py,sha256=1nb_lBWDwxJo3hutxSid2rqMIpfZ4GHxDS6cfj-FKv4,187
|
|
@@ -331,25 +342,25 @@ tol/validators/assert_on_condition.py,sha256=eBGgSVfIQ6e45SheM-ZDg7daXJjyZxRVS5L
|
|
|
331
342
|
tol/validators/branching.py,sha256=7YFjHNjrrTmy4hZ3E7JKDT6MEsBMhrc3P3p3ykv4wKI,5720
|
|
332
343
|
tol/validators/converter_and_validate.py,sha256=O1uYdrU4YDZ8eZjb7Koots4-8fMVOkJFXESg-LVw2o8,2992
|
|
333
344
|
tol/validators/date_sorting.py,sha256=NzYsBfhgeG4NYlYjVUYgcGGwEHns5hESqeaPvXUxjUI,1918
|
|
334
|
-
tol/validators/ena_checklist.py,sha256=
|
|
345
|
+
tol/validators/ena_checklist.py,sha256=2IfgzdgpumTPPwzoycIpfFFnkTSRlEjpZ8RQYQpBoXY,3771
|
|
335
346
|
tol/validators/ena_submittable.py,sha256=CujF9t4mA4N3Wm_5rA5MRp401aW19kbioOZpfWVXg6I,1965
|
|
336
347
|
tol/validators/min_one_valid_value.py,sha256=gZUHtfRA-Lvpw0d1FJoAA31cRJpLbbxAJCC9DCt5lCY,1442
|
|
337
348
|
tol/validators/mutually_exclusive.py,sha256=6blZK-2IY4Eq79fHKKrm-pxsQ6B5DNH5ldtxOFVCPhU,4492
|
|
338
349
|
tol/validators/regex.py,sha256=dLAi_vQt9_DsT6wQZmbYC7X5-Wp15l0leUE6XkPaItg,2602
|
|
339
350
|
tol/validators/regex_by_value.py,sha256=XM5EnT4vgD17rfpR3bUE9I56IemSw26BI9MZtMakd4E,2582
|
|
340
|
-
tol/validators/specimens_have_same_taxon.py,sha256=
|
|
351
|
+
tol/validators/specimens_have_same_taxon.py,sha256=BaJcZ38ZprPcuGTIorSxxC9uGN0_lj6HS6B54EObcuY,2183
|
|
341
352
|
tol/validators/sts_fields.py,sha256=aYbzy15btEg4-ocDT1qrspe7-atoWRrOJ_KmuPU6J14,8936
|
|
342
353
|
tol/validators/tolid.py,sha256=yODebLYbKtlem3IpVcv8XImvq90r-AK68asH9JEawqo,3897
|
|
343
|
-
tol/validators/types.py,sha256=
|
|
354
|
+
tol/validators/types.py,sha256=jMVpckRp8RS93f7usf58YH_K-5rKWgZIYs7bO9dHhQc,2914
|
|
344
355
|
tol/validators/unique_value_check.py,sha256=sFvDooYkKeORvULGEOTsgIcxlbe0AXDWxY3Gbr3j0KI,1282
|
|
345
356
|
tol/validators/unique_values.py,sha256=o5IrfUNLEmlEp8kpInTtFnTq-FqiHSC9TItKdf-LI1o,3114
|
|
346
357
|
tol/validators/unique_whole_organisms.py,sha256=RdqA1GzIf3LTdrmNGGdxv0aW2udDY2P9EaqZb40hhik,5735
|
|
347
|
-
tol/validators/value_check.py,sha256=
|
|
358
|
+
tol/validators/value_check.py,sha256=DdNx_B1gns01zgBg5N6Bwia46Aukw6MAteM-M37Kv1k,1122
|
|
348
359
|
tol/validators/interfaces/__init__.py,sha256=jtOxnwnwqV_29xjmmMcS_kvlt-pQiWwQYJn2YRP07_w,172
|
|
349
360
|
tol/validators/interfaces/condition_evaluator.py,sha256=nj8Cb8hi47OBy6OVNfeLhF-Pjwtr8MiOSymYL6hfVes,3766
|
|
350
|
-
tol_sdk-1.8.
|
|
351
|
-
tol_sdk-1.8.
|
|
352
|
-
tol_sdk-1.8.
|
|
353
|
-
tol_sdk-1.8.
|
|
354
|
-
tol_sdk-1.8.
|
|
355
|
-
tol_sdk-1.8.
|
|
361
|
+
tol_sdk-1.8.6.dist-info/licenses/LICENSE,sha256=RF9Jacy-9BpUAQQ20INhTgtaNBkmdTolYCHtrrkM2-8,1077
|
|
362
|
+
tol_sdk-1.8.6.dist-info/METADATA,sha256=drjwvfHHt_dIUvdywaSjXVuTa0wNCbdMHsLPuONx92o,3142
|
|
363
|
+
tol_sdk-1.8.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
364
|
+
tol_sdk-1.8.6.dist-info/entry_points.txt,sha256=jH3HfTwxjzog7E3lq8CKpUWGIRY9FSXbyL6CpUmv6D0,36
|
|
365
|
+
tol_sdk-1.8.6.dist-info/top_level.txt,sha256=PwKMQLphyZNvagBoriVbl8uwHXQl8IC1niawVG0iXMM,10
|
|
366
|
+
tol_sdk-1.8.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|