tol-sdk 1.7.5b4__py3-none-any.whl → 1.8.0__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/core/data_object_converter.py +6 -0
- tol/excel/excel_datasource.py +5 -1
- tol/flows/converters/incoming_sample_to_ena_sample_converter.py +46 -21
- tol/validators/__init__.py +1 -0
- tol/validators/converter_and_validate.py +4 -4
- tol/validators/ena_checklist.py +16 -16
- tol/validators/regex.py +2 -3
- tol/validators/sts_fields.py +20 -14
- tol/validators/tolid.py +1 -1
- tol/validators/types.py +87 -0
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/METADATA +3 -1
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/RECORD +16 -15
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/WHEEL +0 -0
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/entry_points.txt +0 -0
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/licenses/LICENSE +0 -0
- {tol_sdk-1.7.5b4.dist-info → tol_sdk-1.8.0.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# SPDX-License-Identifier: MIT
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass
|
|
6
7
|
from typing import Iterable
|
|
7
8
|
|
|
8
9
|
from more_itertools import flatten
|
|
@@ -95,9 +96,14 @@ class DefaultDataObjectToDataObjectConverter(DataObjectToDataObjectOrUpdateConve
|
|
|
95
96
|
|
|
96
97
|
class SanitisingConverter(DataObjectToDataObjectOrUpdateConverter):
|
|
97
98
|
|
|
99
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
100
|
+
class Config:
|
|
101
|
+
pass
|
|
102
|
+
|
|
98
103
|
def __init__(
|
|
99
104
|
self,
|
|
100
105
|
data_object_factory: DataObjectFactory,
|
|
106
|
+
config: Config,
|
|
101
107
|
**kwargs
|
|
102
108
|
):
|
|
103
109
|
super().__init__(data_object_factory)
|
tol/excel/excel_datasource.py
CHANGED
|
@@ -65,7 +65,7 @@ class ExcelDataSource(
|
|
|
65
65
|
) -> Iterable[DataObject]:
|
|
66
66
|
|
|
67
67
|
return (
|
|
68
|
-
self.__marshal_row(row_index +
|
|
68
|
+
self.__marshal_row(row_index + 2, row) # Add 1 for header, 1 for 1-based ID
|
|
69
69
|
for row_index, row
|
|
70
70
|
in self.__df.iterrows()
|
|
71
71
|
)
|
|
@@ -115,6 +115,10 @@ class ExcelDataSource(
|
|
|
115
115
|
__v: Any,
|
|
116
116
|
) -> Any:
|
|
117
117
|
|
|
118
|
+
# Convert pandas Timestamp to Python datetime
|
|
119
|
+
if isinstance(__v, pd.Timestamp):
|
|
120
|
+
__v = datetime.fromtimestamp(__v.timestamp())
|
|
121
|
+
|
|
118
122
|
if __k not in self.__mappings:
|
|
119
123
|
return __v
|
|
120
124
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import re
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
6
7
|
from typing import Iterable
|
|
7
8
|
|
|
8
9
|
from tol.core import DataObject, DataObjectToDataObjectOrUpdateConverter
|
|
@@ -30,8 +31,10 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
30
31
|
s = data_object
|
|
31
32
|
attributes = {
|
|
32
33
|
'ENA-CHECKLIST': self.__config.ena_checklist_id,
|
|
33
|
-
'organism part': self.
|
|
34
|
-
|
|
34
|
+
'organism part': self.__join_list([
|
|
35
|
+
self.__replace_underscores(v)
|
|
36
|
+
for v in s.attributes.get('ORGANISM_PART', [])
|
|
37
|
+
]),
|
|
35
38
|
'lifestage': (
|
|
36
39
|
'spore-bearing structure'
|
|
37
40
|
if s.attributes.get('LIFESTAGE') == 'SPORE_BEARING_STRUCTURE'
|
|
@@ -40,35 +43,38 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
40
43
|
),
|
|
41
44
|
'project name':
|
|
42
45
|
self.__config.project_name,
|
|
43
|
-
'
|
|
44
|
-
self.__replace_underscores(
|
|
45
|
-
|
|
46
|
+
'collected_by': self.__join_list([
|
|
47
|
+
self.__replace_underscores(v)
|
|
48
|
+
for v in s.attributes.get('COLLECTED_BY', [])
|
|
49
|
+
]),
|
|
46
50
|
'collection date':
|
|
47
|
-
self.
|
|
48
|
-
s.attributes.get('DATE_OF_COLLECTION'))
|
|
51
|
+
self.__format_date(
|
|
52
|
+
s.attributes.get('DATE_OF_COLLECTION')),
|
|
49
53
|
'geographic location (country and/or sea)':
|
|
50
54
|
self.__collection_country(s).replace('_', ' '),
|
|
51
55
|
'geographic location (latitude)':
|
|
52
56
|
self.__replace_underscores(
|
|
53
|
-
s.attributes.get('DECIMAL_LATITUDE')).lower(),
|
|
57
|
+
str(s.attributes.get('DECIMAL_LATITUDE'))).lower(),
|
|
54
58
|
'geographic location (latitude) units':
|
|
55
59
|
'DD',
|
|
56
60
|
'geographic location (longitude)':
|
|
57
61
|
self.__replace_underscores(
|
|
58
|
-
s.attributes.get('DECIMAL_LONGITUDE')).lower(),
|
|
62
|
+
str(s.attributes.get('DECIMAL_LONGITUDE'))).lower(),
|
|
59
63
|
'geographic location (longitude) units':
|
|
60
64
|
'DD',
|
|
61
65
|
'geographic location (region and locality)':
|
|
62
66
|
self.__collection_region(s).replace('_', ' '),
|
|
63
|
-
'identified_by':
|
|
64
|
-
self.__replace_underscores(
|
|
65
|
-
|
|
67
|
+
'identified_by': self.__join_list([
|
|
68
|
+
self.__replace_underscores(v)
|
|
69
|
+
for v in s.attributes.get('IDENTIFIED_BY', [])
|
|
70
|
+
]),
|
|
66
71
|
'habitat':
|
|
67
72
|
self.__replace_underscores(
|
|
68
73
|
s.attributes.get('HABITAT')),
|
|
69
|
-
'identifier_affiliation':
|
|
70
|
-
self.__replace_underscores(
|
|
71
|
-
|
|
74
|
+
'identifier_affiliation': self.__join_list([
|
|
75
|
+
self.__replace_underscores(v)
|
|
76
|
+
for v in s.attributes.get('IDENTIFIER_AFFILIATION', [])
|
|
77
|
+
]),
|
|
72
78
|
'sex':
|
|
73
79
|
self.__replace_underscores(
|
|
74
80
|
s.attributes.get('SEX')),
|
|
@@ -77,9 +83,10 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
77
83
|
s.attributes.get('RELATIONSHIP')),
|
|
78
84
|
'SYMBIONT':
|
|
79
85
|
'Y' if s.attributes.get('SYMBIONT') == 'SYMBIONT' else 'N',
|
|
80
|
-
'collecting institution':
|
|
81
|
-
self.__replace_underscores(
|
|
82
|
-
|
|
86
|
+
'collecting institution': self.__join_list([
|
|
87
|
+
self.__replace_underscores(v)
|
|
88
|
+
for v in s.attributes.get('COLLECTOR_AFFILIATION', [])
|
|
89
|
+
]),
|
|
83
90
|
}
|
|
84
91
|
if self.__sanitise(s.attributes.get('DEPTH')) != '':
|
|
85
92
|
attributes['geographic location (depth)'] = s.attributes.get('DEPTH')
|
|
@@ -88,9 +95,11 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
88
95
|
attributes['geographic location (elevation)'] = s.attributes.get('ELEVATION')
|
|
89
96
|
attributes['geographic location (elevation) units'] = 'm'
|
|
90
97
|
if self.__sanitise(s.attributes.get('ORIGINAL_COLLECTION_DATE')) != '':
|
|
91
|
-
attributes['original collection date'] =
|
|
98
|
+
attributes['original collection date'] = \
|
|
99
|
+
self.__format_date(s.attributes.get('ORIGINAL_COLLECTION_DATE'))
|
|
92
100
|
if self.__sanitise(s.attributes.get('ORIGINAL_GEOGRAPHIC_LOCATION')) != '':
|
|
93
|
-
attributes['original geographic location'] =
|
|
101
|
+
attributes['original geographic location'] = \
|
|
102
|
+
self.__replace_underscores(s.attributes.get('ORIGINAL_GEOGRAPHIC_LOCATION'))
|
|
94
103
|
if s.attributes.get('GAL') is not None:
|
|
95
104
|
attributes['GAL'] = s.attributes.get('GAL')
|
|
96
105
|
if s.attributes.get('VOUCHER_ID') is not None:
|
|
@@ -103,7 +112,7 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
103
112
|
attributes['culture_or_strain_id'] = s.attributes.get('CULTURE_OR_STRAIN_ID')
|
|
104
113
|
|
|
105
114
|
ret = self._data_object_factory(
|
|
106
|
-
|
|
115
|
+
data_object.type,
|
|
107
116
|
s.id,
|
|
108
117
|
attributes=attributes,
|
|
109
118
|
)
|
|
@@ -128,3 +137,19 @@ class IncomingSampleToEnaSampleConverter(DataObjectToDataObjectOrUpdateConverter
|
|
|
128
137
|
if value is None:
|
|
129
138
|
return default_value
|
|
130
139
|
return value
|
|
140
|
+
|
|
141
|
+
def __join_list(self, value_list):
|
|
142
|
+
if value_list is None:
|
|
143
|
+
return ''
|
|
144
|
+
if not isinstance(value_list, list):
|
|
145
|
+
return str(value_list)
|
|
146
|
+
return ' | '.join(str(v) for v in value_list)
|
|
147
|
+
|
|
148
|
+
def __format_date(self, value):
|
|
149
|
+
"""Format date to YYYY-mm-dd format"""
|
|
150
|
+
if value is None:
|
|
151
|
+
return ''
|
|
152
|
+
if isinstance(value, datetime):
|
|
153
|
+
return value.strftime('%Y-%m-%d')
|
|
154
|
+
|
|
155
|
+
return str(value)
|
tol/validators/__init__.py
CHANGED
|
@@ -15,6 +15,7 @@ from .regex_by_value import RegexByValueValidator # noqa
|
|
|
15
15
|
from .specimens_have_same_taxon import SpecimensHaveSameTaxonValidator # noqa
|
|
16
16
|
from .sts_fields import StsFieldsValidator # noqa
|
|
17
17
|
from .tolid import TolidValidator # noqa
|
|
18
|
+
from .types import TypesValidator # noqa
|
|
18
19
|
from .unique_values import UniqueValuesValidator # noqa
|
|
19
20
|
from .unique_whole_organisms import UniqueWholeOrganismsValidator # noqa
|
|
20
21
|
from .interfaces import Condition # noqa
|
|
@@ -19,12 +19,12 @@ class ConverterAndValidateValidator(Validator):
|
|
|
19
19
|
"converters": [{
|
|
20
20
|
"module": "<path.to.module>",
|
|
21
21
|
"class_name": "<path.to.ConverterClass>",
|
|
22
|
-
"
|
|
22
|
+
"config_details": { ... }
|
|
23
23
|
}],
|
|
24
24
|
"validators": [{
|
|
25
25
|
"module": "<path.to.module>",
|
|
26
26
|
"class_name": "<path.to.ValidatorClass>",
|
|
27
|
-
"
|
|
27
|
+
"config_details": { ... }
|
|
28
28
|
}]
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -54,7 +54,7 @@ class ConverterAndValidateValidator(Validator):
|
|
|
54
54
|
converter_class = getattr(__module, conv.get('class_name'))
|
|
55
55
|
|
|
56
56
|
converter_conf = converter_class.Config(
|
|
57
|
-
**conv.get('
|
|
57
|
+
**conv.get('config_details')
|
|
58
58
|
)
|
|
59
59
|
self.__converters.append(converter_class(
|
|
60
60
|
data_object_factory=data_object_factory,
|
|
@@ -65,7 +65,7 @@ class ConverterAndValidateValidator(Validator):
|
|
|
65
65
|
validator_class = getattr(__module, val.get('class_name'))
|
|
66
66
|
|
|
67
67
|
validator_conf = validator_class.Config(
|
|
68
|
-
**val.get('
|
|
68
|
+
**val.get('config_details')
|
|
69
69
|
)
|
|
70
70
|
self.__validators.append(validator_class(
|
|
71
71
|
data_object_factory=data_object_factory,
|
tol/validators/ena_checklist.py
CHANGED
|
@@ -27,31 +27,31 @@ class EnaChecklistValidator(Validator):
|
|
|
27
27
|
super().__init__()
|
|
28
28
|
self.__config = config
|
|
29
29
|
self._datasource = datasource
|
|
30
|
+
self.__ena_checklist = datasource.get_one(
|
|
31
|
+
'checklist',
|
|
32
|
+
self.__config.ena_checklist_id
|
|
33
|
+
).checklist
|
|
30
34
|
|
|
31
35
|
def _validate_data_object(self, obj: DataObject) -> None:
|
|
32
|
-
|
|
33
|
-
ena_checklist = ena_datasource.get_one('checklist', self.__config.ena_checklist_id)
|
|
34
|
-
|
|
35
|
-
validations = ena_checklist.attributes['checklist']
|
|
36
|
-
for key in validations:
|
|
36
|
+
for key, validation in self.__ena_checklist.items():
|
|
37
37
|
field_name = key
|
|
38
|
-
if 'field' in
|
|
39
|
-
field_name =
|
|
40
|
-
if 'mandatory' in
|
|
38
|
+
if 'field' in validation:
|
|
39
|
+
field_name = validation['field']
|
|
40
|
+
if 'mandatory' in validation and key not in obj.attributes:
|
|
41
41
|
self.add_error(object_id=obj.id, detail='Must be given', field=[field_name])
|
|
42
42
|
continue
|
|
43
|
-
if 'mandatory' in
|
|
43
|
+
if 'mandatory' in validation and obj.attributes[key] is None:
|
|
44
44
|
self.add_error(object_id=obj.id, detail='Must be given', field=[field_name])
|
|
45
45
|
continue
|
|
46
|
-
if 'mandatory' in
|
|
46
|
+
if 'mandatory' in validation and obj.attributes.get(key) == '':
|
|
47
47
|
self.add_error(
|
|
48
48
|
object_id=obj.id,
|
|
49
49
|
detail='Must not be empty', field=[field_name]
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
if 'restricted text' in
|
|
53
|
-
for condition in
|
|
54
|
-
if
|
|
52
|
+
if 'restricted text' in validation and key in obj.attributes:
|
|
53
|
+
for condition in validation:
|
|
54
|
+
if isinstance(condition, str) and '(' in condition:
|
|
55
55
|
regex = condition
|
|
56
56
|
compiled_re = re.compile(regex)
|
|
57
57
|
if not compiled_re.search(obj.attributes.get(key)):
|
|
@@ -61,9 +61,9 @@ class EnaChecklistValidator(Validator):
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
# Check against allowed values
|
|
64
|
-
if 'text choice' in
|
|
65
|
-
for condition in
|
|
66
|
-
if
|
|
64
|
+
if 'text choice' in validation and key in obj.attributes:
|
|
65
|
+
for condition in validation:
|
|
66
|
+
if isinstance(condition, list):
|
|
67
67
|
allowed_values = condition
|
|
68
68
|
if obj.attributes.get(key).lower() not in \
|
|
69
69
|
[x.lower() for x in allowed_values]:
|
tol/validators/regex.py
CHANGED
|
@@ -26,9 +26,9 @@ class Regex:
|
|
|
26
26
|
|
|
27
27
|
def is_allowed(self, __v: Any) -> bool:
|
|
28
28
|
# Check regex
|
|
29
|
-
return bool(re.search(
|
|
29
|
+
return __v is None or __v == '' or bool(re.search(
|
|
30
30
|
self.regex,
|
|
31
|
-
str(__v)
|
|
31
|
+
str(__v)
|
|
32
32
|
))
|
|
33
33
|
|
|
34
34
|
|
|
@@ -90,7 +90,6 @@ class RegexValidator(Validator):
|
|
|
90
90
|
obj: DataObject,
|
|
91
91
|
c: Regex,
|
|
92
92
|
) -> None:
|
|
93
|
-
|
|
94
93
|
if c.is_error:
|
|
95
94
|
self.add_error(
|
|
96
95
|
object_id=obj.id,
|
tol/validators/sts_fields.py
CHANGED
|
@@ -53,36 +53,42 @@ class StsFieldsValidator(Validator):
|
|
|
53
53
|
for field in self.__fields.values():
|
|
54
54
|
# Get the value from the data object
|
|
55
55
|
field_value = obj.get_field_by_name(field.get('data_input_key'))
|
|
56
|
-
if field.get('
|
|
56
|
+
if field.get('mandatory_validation') and (field_value is None or field_value == ''):
|
|
57
57
|
self.add_error(
|
|
58
58
|
object_id=obj.id,
|
|
59
59
|
detail=f'Field {field.get("data_input_key")} is required '
|
|
60
60
|
f'for project {self.__config.project_code}',
|
|
61
61
|
field=field.get('data_input_key'),
|
|
62
62
|
)
|
|
63
|
-
elif field.get('allowed_values')
|
|
63
|
+
elif field.get('allowed_values'):
|
|
64
|
+
allowed_values = [
|
|
65
|
+
value.get('value') for value in field.get('allowed_values', [])
|
|
66
|
+
]
|
|
67
|
+
if field_value not in allowed_values:
|
|
68
|
+
self.add_error(
|
|
69
|
+
object_id=obj.id,
|
|
70
|
+
detail=f'Field {field.get("data_input_key")} value '
|
|
71
|
+
f'"{field_value}" not found in allowed values '
|
|
72
|
+
f'{allowed_values} for project '
|
|
73
|
+
f'{self.__config.project_code}',
|
|
74
|
+
field=field.get('data_input_key'),
|
|
75
|
+
)
|
|
76
|
+
elif field.get('min') and field.get('type') == 'String' \
|
|
77
|
+
and field_value is not None and len(field_value) < field.get('min'):
|
|
64
78
|
self.add_error(
|
|
65
79
|
object_id=obj.id,
|
|
66
80
|
detail=f'Field {field.get("data_input_key")} value '
|
|
67
|
-
f'"{field_value}"
|
|
68
|
-
f'{field.get("allowed_values")} for project '
|
|
69
|
-
f'{self.__config.project_code}',
|
|
70
|
-
field=field.get('data_input_key'),
|
|
71
|
-
)
|
|
72
|
-
elif field.get('min') and field_value < field.get('min'):
|
|
73
|
-
self.add_error(
|
|
74
|
-
object_id=obj.id,
|
|
75
|
-
detail=f'Field {field.get("data_input_key")} value '
|
|
76
|
-
f'"{field_value}" is less than minimum value '
|
|
81
|
+
f'"{field_value}" is shorter than minimum length '
|
|
77
82
|
f'"{field.get("min")}" for project '
|
|
78
83
|
f'{self.__config.project_code}',
|
|
79
84
|
field=field.get('data_input_key'),
|
|
80
85
|
)
|
|
81
|
-
elif field.get('max') and
|
|
86
|
+
elif field.get('max') and field.get('type') == 'String' \
|
|
87
|
+
and field_value is not None and len(field_value) > field.get('max'):
|
|
82
88
|
self.add_error(
|
|
83
89
|
object_id=obj.id,
|
|
84
90
|
detail=f'Field {field.get("data_input_key")} value '
|
|
85
|
-
f'"{field_value}" is
|
|
91
|
+
f'"{field_value}" is longer than maximum length '
|
|
86
92
|
f'"{field.get("max")}" for project '
|
|
87
93
|
f'{self.__config.project_code}',
|
|
88
94
|
field=field.get('data_input_key'),
|
tol/validators/tolid.py
CHANGED
|
@@ -103,7 +103,7 @@ class TolidValidator(Validator):
|
|
|
103
103
|
|
|
104
104
|
if str(obj.get_field_by_name(self.__config.species_id_field)) not in taxons:
|
|
105
105
|
self.add_error(
|
|
106
|
-
object_id=obj.id,
|
|
106
|
+
object_id=obj.id + 1,
|
|
107
107
|
detail=f'Specimen ID {specimen_id} does not match Taxon ID '
|
|
108
108
|
f'{obj.get_field_by_name(self.__config.species_id_field)}'
|
|
109
109
|
'in TolID source',
|
tol/validators/types.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2025 Genome Research Ltd.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime, time
|
|
7
|
+
|
|
8
|
+
from tol.core import DataObject
|
|
9
|
+
from tol.core.validate import Validator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TypesValidator(Validator):
|
|
13
|
+
"""
|
|
14
|
+
Validates an incoming stream of `DataObject` instances,
|
|
15
|
+
ensuring that they only have attributes of the given
|
|
16
|
+
allowed keys.
|
|
17
|
+
"""
|
|
18
|
+
@dataclass(slots=True, frozen=True, kw_only=True)
|
|
19
|
+
class Config:
|
|
20
|
+
allowed_types: dict[str, str]
|
|
21
|
+
is_error: bool = True
|
|
22
|
+
detail: str = 'Value is of incorrect type'
|
|
23
|
+
|
|
24
|
+
__slots__ = ['__config']
|
|
25
|
+
__config: Config
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
config: Config,
|
|
30
|
+
**kwargs
|
|
31
|
+
) -> None:
|
|
32
|
+
|
|
33
|
+
super().__init__()
|
|
34
|
+
self.__config = config
|
|
35
|
+
|
|
36
|
+
def _validate_data_object(
|
|
37
|
+
self,
|
|
38
|
+
obj: DataObject
|
|
39
|
+
) -> None:
|
|
40
|
+
|
|
41
|
+
type_map = {
|
|
42
|
+
'str': str,
|
|
43
|
+
'int': int,
|
|
44
|
+
'float': float,
|
|
45
|
+
'bool': bool,
|
|
46
|
+
'list': list,
|
|
47
|
+
'dict': dict,
|
|
48
|
+
'datetime': datetime,
|
|
49
|
+
'time': time
|
|
50
|
+
}
|
|
51
|
+
for key, expected_type in self.__config.allowed_types.items():
|
|
52
|
+
if key in obj.attributes:
|
|
53
|
+
actual_value = obj.get_field_by_name(key)
|
|
54
|
+
if actual_value is None:
|
|
55
|
+
continue
|
|
56
|
+
type_class = type_map.get(expected_type)
|
|
57
|
+
if type_class and not isinstance(actual_value, type_class):
|
|
58
|
+
self.__add_result(
|
|
59
|
+
obj,
|
|
60
|
+
key,
|
|
61
|
+
)
|
|
62
|
+
if type_class and isinstance(actual_value, type_class):
|
|
63
|
+
# Special case for bool since isinstance(True, int) is True
|
|
64
|
+
if expected_type == 'int' and isinstance(actual_value, bool):
|
|
65
|
+
self.__add_result(
|
|
66
|
+
obj,
|
|
67
|
+
key,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def __add_result(
|
|
71
|
+
self,
|
|
72
|
+
obj: DataObject,
|
|
73
|
+
key: str,
|
|
74
|
+
) -> None:
|
|
75
|
+
|
|
76
|
+
if self.__config.is_error:
|
|
77
|
+
self.add_error(
|
|
78
|
+
object_id=obj.id,
|
|
79
|
+
detail=self.__config.detail,
|
|
80
|
+
field=key,
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
self.add_warning(
|
|
84
|
+
object_id=obj.id,
|
|
85
|
+
detail=self.__config.detail,
|
|
86
|
+
field=key,
|
|
87
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tol-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: SDK for interaction with ToL, Sanger and external services
|
|
5
5
|
Author-email: ToL Platforms Team <tol-platforms@sanger.ac.uk>
|
|
6
6
|
License: MIT
|
|
@@ -50,6 +50,8 @@ Requires-Dist: openpyxl>=3.0.10; extra == "sheets"
|
|
|
50
50
|
Requires-Dist: XlsxWriter==3.1.9; extra == "sheets"
|
|
51
51
|
Requires-Dist: xlrd==2.0.1; extra == "sheets"
|
|
52
52
|
Requires-Dist: gspread>=5.12.0; extra == "sheets"
|
|
53
|
+
Provides-Extra: s3
|
|
54
|
+
Requires-Dist: minio==7.2.15; extra == "s3"
|
|
53
55
|
Provides-Extra: all
|
|
54
56
|
Requires-Dist: tol-sdk[api-base]; extra == "all"
|
|
55
57
|
Requires-Dist: tol-sdk[benchling]; extra == "all"
|
|
@@ -89,7 +89,7 @@ tol/core/attribute_metadata.py,sha256=wYD3NXDdStrpkUZoyTUiEpp7c14f7MLIcyooT1G4GE
|
|
|
89
89
|
tol/core/core_converter.py,sha256=Gn4J507BtqDjnOWV2MFRYGz8YElJAKQItmnCrD72s7k,4504
|
|
90
90
|
tol/core/data_loader.py,sha256=k-ET1nIohIz6PcADbEn9Y7k9TupoiBYxKDkcAl_9pGY,14710
|
|
91
91
|
tol/core/data_object.py,sha256=GxG04JMcICaiHU1rufkhoD8jb9YQLhE0QWlFU2ZkQsM,4241
|
|
92
|
-
tol/core/data_object_converter.py,sha256=
|
|
92
|
+
tol/core/data_object_converter.py,sha256=GESpLvwrAEwmCfBwy3GxcSCuHz0xt7ECCBPE2stxBdI,3927
|
|
93
93
|
tol/core/data_source_attribute_metadata.py,sha256=NHvJ_Gmw7-Oej1MoFCohvq4f6emDJ2HF483UmW2Qd_c,4407
|
|
94
94
|
tol/core/data_source_dict.py,sha256=d-hSmoWTwG6IOc0cQTLap1EBslsxYIWGUd3ScSoeH_Q,1705
|
|
95
95
|
tol/core/datasource.py,sha256=e9GaeDPfO_Gs7cgQhmNxCiSDlRNf64reegzFebcMNkA,6303
|
|
@@ -142,7 +142,7 @@ tol/ena/filter.py,sha256=UzOx5ivXvA0TY2QuNzFmS-zDPVNnaAx07DMVkAwVsAE,3370
|
|
|
142
142
|
tol/ena/parser.py,sha256=Z4YmUnpfLKng4QwmZkLEj1hUfwYb_bqr-DWgF1Gw-EY,3253
|
|
143
143
|
tol/excel/__init__.py,sha256=M0xL9w9Au8kYOLWzFGuijJ7WoZENOMkZ1XV1ephhlDY,229
|
|
144
144
|
tol/excel/excel.py,sha256=rcA-wfXY9R14OfNKS-NX2sn__9gmQ_G8LoUgWseF1Gk,2124
|
|
145
|
-
tol/excel/excel_datasource.py,sha256=
|
|
145
|
+
tol/excel/excel_datasource.py,sha256=9dRvpfIaolYbXrXUb7EHOWkROIBlAsYYlsNQuGel_gU,3284
|
|
146
146
|
tol/excel/s3_factory.py,sha256=4lGyKrSvarPXWndyvm7K-tel0FoM0My8wnz-Mzwt0yQ,1245
|
|
147
147
|
tol/flows/__init__.py,sha256=M7iSvnBJs6fJ8M38cW0bYQa9WW0TN8FHAMjIHPDNAJ4,166
|
|
148
148
|
tol/flows/logger.py,sha256=rWXbaknGcPEZRFvC1CiB1qkhFRZsQk435w7VyJ3cpyw,170
|
|
@@ -180,7 +180,7 @@ tol/flows/converters/gap_assembly_to_elastic_assembly_converter.py,sha256=XK-es-
|
|
|
180
180
|
tol/flows/converters/genome_notes_genome_note_to_elastic_genome_note_converter.py,sha256=AaUWbVTaWU-NXnUQPaPwI41TE7a-nC4zlg-jrWpPT2s,1166
|
|
181
181
|
tol/flows/converters/goat_taxon_to_elastic_species_converter.py,sha256=1NGs9427OdXGsBaMB467nOF7aTlJsUKYCuoSoABw9L4,1074
|
|
182
182
|
tol/flows/converters/grit_issue_to_elastic_curation_converter.py,sha256=XpRpoRn589MxTqEk6zPWGn6tamJiqY9Ctxk8v0q-dvA,3953
|
|
183
|
-
tol/flows/converters/incoming_sample_to_ena_sample_converter.py,sha256=
|
|
183
|
+
tol/flows/converters/incoming_sample_to_ena_sample_converter.py,sha256=SAVYWENG3GS7B1rM6rYwxfLQH75nZl7mEzphH5CBxRw,6353
|
|
184
184
|
tol/flows/converters/incoming_sample_to_incoming_sample_with_lists_converter.py,sha256=5Fp1_ojsYqvRcKTgXJbyWqetPisi_vtWFcWr6RtGZoA,1504
|
|
185
185
|
tol/flows/converters/informatics_tolid_to_elastic_tolid_converter.py,sha256=VrvtsDTPlc5Xa3K4rcAMHwV4n71zOH7q5EfALLLQ1tI,587
|
|
186
186
|
tol/flows/converters/labwhere_location_to_elastic_sample_update_converter.py,sha256=NJNmG9sCc2WXc-2J5XfCKXhb2sDH82nZUBekd16PHcw,656
|
|
@@ -321,28 +321,29 @@ tol/treeval/treeval_datasource.py,sha256=GzY6JwH67b5QdV-UVdCFJfgGAIuZ96J2nl53YxZ
|
|
|
321
321
|
tol/utils/__init__.py,sha256=764-Na1OaNGUDWpMIu51ZtXG7n_nB5MccUFK6LmkWRI,138
|
|
322
322
|
tol/utils/csv.py,sha256=mihww25fSn72c4h-RFeqD_pFIG6KHZP4v1_C0rx81ws,421
|
|
323
323
|
tol/utils/s3.py,sha256=aoYCwJ-qcMqFrpxmViFqPa0O1jgp0phtztO3-0CSNjw,491
|
|
324
|
-
tol/validators/__init__.py,sha256=
|
|
324
|
+
tol/validators/__init__.py,sha256=QI5ykFzsTLsIQXcL4vF_aaVGdSr2l0X0Qkssbnxumss,1176
|
|
325
325
|
tol/validators/allowed_keys.py,sha256=RJcHBiguL84B8hjSRaXLNES21yZqaKFwJNp2Tz9zvh0,1506
|
|
326
326
|
tol/validators/allowed_values.py,sha256=-Yy3Sqo1WYacGKlot_dn3M2o7Oj5MXOioJrJmrWCCxs,1536
|
|
327
327
|
tol/validators/allowed_values_from_datasource.py,sha256=ICFO6FcYXDN7M2Cv1OwpyN38CdhmY7oU-njzIatA3-w,3185
|
|
328
328
|
tol/validators/assert_on_condition.py,sha256=eBGgSVfIQ6e45SheM-ZDg7daXJjyZxRVS5L8AWvbXag,2027
|
|
329
|
-
tol/validators/converter_and_validate.py,sha256=
|
|
330
|
-
tol/validators/ena_checklist.py,sha256=
|
|
329
|
+
tol/validators/converter_and_validate.py,sha256=O1uYdrU4YDZ8eZjb7Koots4-8fMVOkJFXESg-LVw2o8,2992
|
|
330
|
+
tol/validators/ena_checklist.py,sha256=M10VAFGpaxnm7rWO4jmFhTWkYRlCmU0Ox2IUEDFGKbo,2812
|
|
331
331
|
tol/validators/ena_submittable.py,sha256=CujF9t4mA4N3Wm_5rA5MRp401aW19kbioOZpfWVXg6I,1965
|
|
332
332
|
tol/validators/min_one_valid_value.py,sha256=gZUHtfRA-Lvpw0d1FJoAA31cRJpLbbxAJCC9DCt5lCY,1442
|
|
333
333
|
tol/validators/mutually_exclusive.py,sha256=6blZK-2IY4Eq79fHKKrm-pxsQ6B5DNH5ldtxOFVCPhU,4492
|
|
334
|
-
tol/validators/regex.py,sha256=
|
|
334
|
+
tol/validators/regex.py,sha256=dLAi_vQt9_DsT6wQZmbYC7X5-Wp15l0leUE6XkPaItg,2602
|
|
335
335
|
tol/validators/regex_by_value.py,sha256=XM5EnT4vgD17rfpR3bUE9I56IemSw26BI9MZtMakd4E,2582
|
|
336
336
|
tol/validators/specimens_have_same_taxon.py,sha256=m2LLRIZMdhPj1fzyioDJOraI6UHXgy1l963xhezgk7E,2177
|
|
337
|
-
tol/validators/sts_fields.py,sha256=
|
|
338
|
-
tol/validators/tolid.py,sha256=
|
|
337
|
+
tol/validators/sts_fields.py,sha256=IUWeqqNpSLZJVfhGewLHuClZMnxoaIOzg9bGnprxXzo,3805
|
|
338
|
+
tol/validators/tolid.py,sha256=yODebLYbKtlem3IpVcv8XImvq90r-AK68asH9JEawqo,3897
|
|
339
|
+
tol/validators/types.py,sha256=hZ8Iz47kKvmqv-UuOz5oj6D8RjjPDfpMEKw0m5t5BaI,2436
|
|
339
340
|
tol/validators/unique_values.py,sha256=o5IrfUNLEmlEp8kpInTtFnTq-FqiHSC9TItKdf-LI1o,3114
|
|
340
341
|
tol/validators/unique_whole_organisms.py,sha256=RdqA1GzIf3LTdrmNGGdxv0aW2udDY2P9EaqZb40hhik,5735
|
|
341
342
|
tol/validators/interfaces/__init__.py,sha256=jtOxnwnwqV_29xjmmMcS_kvlt-pQiWwQYJn2YRP07_w,172
|
|
342
343
|
tol/validators/interfaces/condition_evaluator.py,sha256=nj8Cb8hi47OBy6OVNfeLhF-Pjwtr8MiOSymYL6hfVes,3766
|
|
343
|
-
tol_sdk-1.
|
|
344
|
-
tol_sdk-1.
|
|
345
|
-
tol_sdk-1.
|
|
346
|
-
tol_sdk-1.
|
|
347
|
-
tol_sdk-1.
|
|
348
|
-
tol_sdk-1.
|
|
344
|
+
tol_sdk-1.8.0.dist-info/licenses/LICENSE,sha256=RF9Jacy-9BpUAQQ20INhTgtaNBkmdTolYCHtrrkM2-8,1077
|
|
345
|
+
tol_sdk-1.8.0.dist-info/METADATA,sha256=Sz6ELtecqM1ndqoLPi2pa7JN6urXSDCv4mFS_RK5Gz4,3142
|
|
346
|
+
tol_sdk-1.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
347
|
+
tol_sdk-1.8.0.dist-info/entry_points.txt,sha256=jH3HfTwxjzog7E3lq8CKpUWGIRY9FSXbyL6CpUmv6D0,36
|
|
348
|
+
tol_sdk-1.8.0.dist-info/top_level.txt,sha256=PwKMQLphyZNvagBoriVbl8uwHXQl8IC1niawVG0iXMM,10
|
|
349
|
+
tol_sdk-1.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|