tol-sdk 1.8.8__py3-none-any.whl → 1.8.10__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.
@@ -30,10 +30,13 @@ class DataSourceUtils:
30
30
  @classmethod
31
31
  def get_datasource_by_datasource_instance(
32
32
  cls,
33
- datasource_instance: DataObject
33
+ datasource_instance: DataObject,
34
+ **kwargs
34
35
  ) -> DataSource:
35
36
  datasource_config = datasource_instance.data_source_config
36
- kwargs = dict(datasource_instance.kwargs) if datasource_instance.kwargs else {}
37
+ new_kwargs = dict(datasource_instance.kwargs) if datasource_instance.kwargs else {}
38
+ if kwargs:
39
+ new_kwargs.update(kwargs)
37
40
  if datasource_config:
38
41
  relationship_config = cls.get_relationship_config_from_data_source_config(
39
42
  datasource_config
@@ -44,14 +47,14 @@ class DataSourceUtils:
44
47
  runtime_fields = cls.get_runtime_fields_from_data_source_config(
45
48
  datasource_config
46
49
  )
47
- kwargs.update({
50
+ new_kwargs.update({
48
51
  'relationship_cfg': relationship_config,
49
52
  'attribute_metadata': amd,
50
53
  'runtime_fields': runtime_fields
51
54
  })
52
55
  return DataSourceUtils.get_datasource_by_name(
53
56
  datasource_instance.builtin_name,
54
- **kwargs
57
+ **new_kwargs
55
58
  )
56
59
 
57
60
  @classmethod
@@ -83,7 +86,6 @@ class DataSourceUtils:
83
86
  cls,
84
87
  datasource_config: DataObject
85
88
  ) -> dict:
86
- from ..elastic.runtime_fields import RuntimeFields # Break circular import cycle
87
89
  runtime_fields = {}
88
90
  f = DataSourceFilter()
89
91
  f.and_ = {
@@ -95,6 +97,7 @@ class DataSourceUtils:
95
97
  if dsa.object_type not in runtime_fields:
96
98
  runtime_fields[dsa.object_type] = {}
97
99
  if 'function' in dsa.runtime_definition:
100
+ from ..elastic.runtime_fields import RuntimeFields # Break circular import cycle
98
101
  method = getattr(RuntimeFields, dsa.runtime_definition['function'])
99
102
  runtime_fields[dsa.object_type][dsa.name] = \
100
103
  method(**dsa.runtime_definition.get('function_kwargs', {}))
tol/ena/ena_datasource.py CHANGED
@@ -11,6 +11,7 @@ from cachetools.func import ttl_cache
11
11
 
12
12
  import requests
13
13
  from requests.auth import HTTPBasicAuth
14
+ from requests.exceptions import HTTPError
14
15
 
15
16
 
16
17
  from .client import EnaApiClient
@@ -118,9 +119,13 @@ class EnaDataSource(
118
119
  if object_type == 'submittable_taxon':
119
120
  ena_response = []
120
121
  for object_id in object_ids:
121
- response = client.get_detail(object_type, [object_id])
122
- if response and isinstance(response, list):
123
- ena_response.extend(response)
122
+ try:
123
+ response = client.get_detail(object_type, [object_id])
124
+ if response and isinstance(response, list):
125
+ ena_response.extend(response)
126
+ except HTTPError as http_error:
127
+ if http_error.response.status_code != 400:
128
+ raise
124
129
  else:
125
130
  ena_response = client.get_detail(object_type, object_ids)
126
131
  # For a checklist we need to convert into a list of dicts
tol/sources/portaldb.py CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  import os
6
6
 
7
+ from .defaults import Defaults
7
8
  from ..api_client import (
8
9
  ApiDataSource,
9
10
  create_api_datasource
@@ -15,9 +16,10 @@ from ..core import (
15
16
 
16
17
  def portaldb(retries: int = 5, **kwargs) -> ApiDataSource:
17
18
  portaldb = create_api_datasource(
18
- api_url=os.getenv('PORTAL_URL') + os.getenv('PORTAL_API_PATH') + '/local',
19
+ api_url=os.getenv('PORTAL_URL', Defaults.PORTAL_URL)
20
+ + os.getenv('PORTAL_API_PATH', Defaults.PORTAL_API_PATH),
19
21
  token=os.getenv('PORTAL_API_KEY'),
20
- data_prefix='',
22
+ data_prefix='/local',
21
23
  retries=retries
22
24
  )
23
25
  core_data_object(portaldb)
tol/sources/sql.py ADDED
@@ -0,0 +1,25 @@
1
+ # SPDX-FileCopyrightText: 2023 Genome Research Ltd.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from ..core import (
6
+ core_data_object
7
+ )
8
+ from ..sql import (
9
+ SqlDataSource,
10
+ create_sql_datasource
11
+ )
12
+
13
+
14
+ # Create a SQL datasource. This is also expecting the models to be passed in
15
+ # so would most likely be used within an app
16
+ def sql(models, db_uri, behind_api, database_factory, **kwargs) -> SqlDataSource:
17
+ sql_ds = create_sql_datasource(
18
+ models=models,
19
+ db_uri=db_uri,
20
+ behind_api=True,
21
+ database_factory=database_factory,
22
+ **kwargs
23
+ )
24
+ core_data_object(sql_ds)
25
+ return sql_ds
tol/sql/sql_datasource.py CHANGED
@@ -86,6 +86,7 @@ class SqlDataSource(
86
86
  sorter_factory: SorterFactory,
87
87
  user_id_getter: Optional[UserIdGetter] = None,
88
88
  attribute_metadata: AttributeMetadata = DefaultAttributeMetadata,
89
+ **kwargs
89
90
  ) -> None:
90
91
  self.__db = db
91
92
  self.__type_tablename_map = type_tablename_map
@@ -19,8 +19,9 @@ from .types import TypesValidator # noqa
19
19
  from .unique_values import UniqueValuesValidator # noqa
20
20
  from .unique_whole_organisms import UniqueWholeOrganismsValidator # noqa
21
21
  from .interfaces import Condition # noqa
22
- from .min_one_valid_value import MinOneValidValueValidator # noqa
22
+ from .min_one_valid_value import MinOneValidValueValidator # noqa
23
23
  from .value_check import ValueCheckValidator # noqa
24
24
  from .branching import BranchingValidator # noqa
25
25
  from .unique_value_check import UniqueValueCheckValidator # noqa
26
- from .date_sorting import DateSortingValidator # noqa
26
+ from .date_sorting import DateSortingValidator # noqa
27
+ from .taxon_matches_goat import TaxonMatchesGoatValidator # noqa
@@ -30,6 +30,7 @@ class AllowedKeysValidator(Validator):
30
30
  **kwargs
31
31
  ) -> None:
32
32
 
33
+ del kwargs
33
34
  super().__init__()
34
35
  self.__config = config
35
36
 
@@ -0,0 +1,86 @@
1
+ # SPDX-FileCopyrightText: 2026 Genome Research Ltd.
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from tol.core import DataObject, Validator
8
+ from tol.sources.goat import GoatDataSource, goat
9
+
10
+
11
+ class TaxonMatchesGoatValidator(Validator):
12
+ """
13
+ Validates a stream of `DataObject` instances, checking whether its Taxonomy information
14
+ matches that in GoaT
15
+ """
16
+ @dataclass(slots=True, frozen=True, kw_only=True)
17
+ class Config:
18
+ species_field: str | None = None
19
+ genus_field: str | None = None
20
+ family_field: str | None = None
21
+ superfamily_field: str | None = None
22
+ phylum_field: str | None = None
23
+ kingdom_field: str | None = None
24
+ superkingdom_field: str | None = None
25
+ domain_field: str | None = None
26
+
27
+ __slots__ = ['__config', '__goat_datasource', '_cached_taxa']
28
+ __config: Config
29
+ __goat_datasource: GoatDataSource
30
+ _cached_taxa: dict[str, DataObject]
31
+
32
+ def __init__(self, config: Config, **kwargs) -> None:
33
+ super().__init__()
34
+ self.__config = config
35
+ self.__goat_datasource = goat()
36
+ self._cached_taxa = {}
37
+
38
+ def _validate_data_object(self, obj: DataObject) -> None:
39
+ taxon_id = obj.get_field_by_name('TAXON_ID')
40
+
41
+ # Check whether we already have the information for this id in the cache.
42
+ # If we don't, fetch it from GoaT and add it to the cache
43
+ taxon: DataObject | None
44
+ if taxon_id in self._cached_taxa:
45
+ taxon = self._cached_taxa[taxon_id]
46
+ else:
47
+ taxon = self.__goat_datasource.get_one('taxon', taxon_id)
48
+
49
+ # Add this taxon to cached taxa.
50
+ # Error if GoaT has no taxon with this id
51
+ if taxon is not None:
52
+ self._cached_taxa[taxon_id] = taxon
53
+ else:
54
+ self.add_error(
55
+ object_id=obj.id,
56
+ detail=f'Invalid Taxon ID: {taxon_id}',
57
+ field='taxon_id'
58
+ )
59
+
60
+ # We can't validate a taxon that doesn't exist, so after this error move on
61
+ # to the next DataObject
62
+ return
63
+
64
+ # Check that each associated taxonomy rank for this taxon matches those in GoaT
65
+ taxonomic_ranks = ('species', 'genus', 'family', 'superfamily',
66
+ 'phylum', 'kingdom', 'superkingdom', 'domain')
67
+ for rank in taxonomic_ranks:
68
+ # From the rank to check, get the name of its field in the data object we're validating
69
+ # from the validator config. If this field name is `None`, then this taxonomic rank
70
+ # isn't being checked (likely because the data object does not have this field)
71
+ field_name: str | None = getattr(self.__config, f'{rank}_field')
72
+ if field_name is None:
73
+ continue
74
+
75
+ # Fetch the values of these taxonomic ranks
76
+ value_in_data_object = obj.get_field_by_name(field_name)
77
+ value_in_goat = taxon.get_field_by_name(f'{rank}.scientific_name')
78
+
79
+ # Ensure the value in the data object matches the one in GoaT
80
+ if value_in_data_object != value_in_goat:
81
+ self.add_warning(
82
+ object_id=obj.id,
83
+ detail=(f'Value for {field_name} ({value_in_data_object}) '
84
+ f'does not match the value in GoaT ({value_in_goat})'),
85
+ field=field_name,
86
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tol-sdk
3
- Version: 1.8.8
3
+ Version: 1.8.10
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
@@ -96,7 +96,7 @@ tol/core/data_source_dict.py,sha256=d-hSmoWTwG6IOc0cQTLap1EBslsxYIWGUd3ScSoeH_Q,
96
96
  tol/core/datasource.py,sha256=e9GaeDPfO_Gs7cgQhmNxCiSDlRNf64reegzFebcMNkA,6303
97
97
  tol/core/datasource_error.py,sha256=TqfqaPANG0gishadhA7myCmTO1Fg9u7hVZOvsY6BdAo,1660
98
98
  tol/core/datasource_filter.py,sha256=RY2S9kTx0XwdrFRSE2n2GohB9__fKGzFVsZrkN5hzQk,726
99
- tol/core/datasource_utils.py,sha256=18mwvFmtJL73_mxtFb56rKXZCGCtZFoEb8sWFKn3Yf0,6232
99
+ tol/core/datasource_utils.py,sha256=6J72RS49IO2quCaFUXBl4DnjZBlXx-aW3Zy9iuzyeQ8,6327
100
100
  tol/core/factory.py,sha256=qbLvp5mLTcHbxOjopqtnK-wbZrlskgXjRIREszZjSyE,9157
101
101
  tol/core/http_client.py,sha256=QyZarplEHVYIrqEfrySeHbawfbnBU4nN62TLt41x4tY,2242
102
102
  tol/core/relationship.py,sha256=etdyCjLbfi2tgkaqzE6cntpNtTzgT_jOPGeNKmPu5yc,4624
@@ -142,7 +142,7 @@ tol/eln/sanitise.py,sha256=fMj-VrQTnw4zn2X0wnjWQAI8gWAa8RYqNuv23LXQssI,406
142
142
  tol/ena/__init__.py,sha256=T3TCqaHpgi2Uk2PjPGu60GaG2V8cTrHJlVLtZfLFhTQ,174
143
143
  tol/ena/client.py,sha256=ldmm7Z9_auQf1zVWjsFLXYgbKvGtSHTsr88YO3zfv2Y,6731
144
144
  tol/ena/converter.py,sha256=nxbo4IFzzOvKNMq3Aeiw5iDqVWvY33nTngLppjHAoGY,1144
145
- tol/ena/ena_datasource.py,sha256=jEvyUaH4pfFxmdtn6O_PwOdjPz6u80uAT3SLlR2f5nM,8968
145
+ tol/ena/ena_datasource.py,sha256=QRj4pXFeAaC0qPdmtPCrCD3DKVsXhu1jYZ97P3AHlnA,9184
146
146
  tol/ena/ena_methods.py,sha256=jgpLssZq-F-vgkO-fYu4jrXenmNkdFpFKAY3VKp5HHE,9209
147
147
  tol/ena/factory.py,sha256=3IJCmGLo59PWbGmNqmKho5WYG330OjL8SoZYstIwHt4,3423
148
148
  tol/ena/filter.py,sha256=UzOx5ivXvA0TY2QuNzFmS-zDPVNnaAx07DMVkAwVsAE,3370
@@ -290,9 +290,10 @@ tol/sources/labwhere.py,sha256=gbfpPNKEZEfvw9DXGshg1w4Kdo_tpjWoxndDza6Oa6E,541
290
290
  tol/sources/mlwh.py,sha256=wPs2yuU1A8v57jaDQbeIThraDqVtzzhm5DAa0qekApM,324
291
291
  tol/sources/portal.py,sha256=1mhX1c4fjbRoHJ86RuBgyXFbDJrVIV55pGwvnuOxVuA,761
292
292
  tol/sources/portal_attributes.py,sha256=j_ZIZ8RHpgbRzOdHSOnzhAM9xA0HcwiAxuVgybVtDrg,1926
293
- tol/sources/portaldb.py,sha256=A-eD_tpJ1cqyDEsnN_fEE67Zkl-WQnRcVlfodZzMcVw,545
293
+ tol/sources/portaldb.py,sha256=fZNIUk5mZ3RuQiknZQlPxOK6Vdz_pzqz1-w_dW0L2yE,626
294
294
  tol/sources/prefect.py,sha256=16PrC_KfvxrsDzAcq7UvEBo4c6_n38NC23vHQeiljck,1046
295
295
  tol/sources/sciops.py,sha256=qbdoNEo-BfWGw16sjANV7dxFRVMDJQlvaSZgofE8604,1129
296
+ tol/sources/sql.py,sha256=mP5hyNNoxi8yKY1Z-6G5EZ1VW24BW_3q5Hp-w5VpPmE,623
296
297
  tol/sources/sts.py,sha256=vd65QAYIM1lltHajqJPOrGg10ZCI7OUB2dbWNAlrAn8,749
297
298
  tol/sources/sts_legacy.py,sha256=jWDM1_Jicxhay9UEOGh4lzeO0TIxuH8sa34TCn-GDWQ,440
298
299
  tol/sources/tolid.py,sha256=ERSB5L_u7c4NtGYTzdGXWjiGiL58QJNC_0ij1MQMMEQ,663
@@ -314,7 +315,7 @@ tol/sql/relationship.py,sha256=EClMgVx_If5nZV9MV99TQk7Wr7uACWetwQdWAliM5XI,2891
314
315
  tol/sql/session.py,sha256=VmqTegr4L2X2zvaOJCpwSrkVRx8fc1RVL0drkL2MXu8,806
315
316
  tol/sql/sort.py,sha256=ENrjHGgj4fZtXKmkdlkv1HRi1X14SVlcl-tp8Pu7G0k,2553
316
317
  tol/sql/sql_converter.py,sha256=taD5FRwadvw2bBaUGrCIiUs0-ATAbBnRYI1M7xe3yEc,4618
317
- tol/sql/sql_datasource.py,sha256=RnPkfg4TSHaxfJG77eBrNgBAKexJaypc-fvfFzssdac,15598
318
+ tol/sql/sql_datasource.py,sha256=2FUwGTIN5Dw2cNAEb9fWGsZ0PVdjhdjn8WajA_9LT_Y,15615
318
319
  tol/sql/action/__init__.py,sha256=T1zAsCza_lvsNtXF1ecSLt9OFGup8tGnIs68YylBmXI,142
319
320
  tol/sql/action/factory.py,sha256=HkareJp_57ud0_Bdd9Kwz3_Rnq2l211sGJgftohFAHg,3589
320
321
  tol/sql/auth/__init__.py,sha256=e3JuwugXmXobklqZ1Mt1w03qPgb1WdUaJVM7oblzHyk,202
@@ -335,8 +336,8 @@ tol/treeval/treeval_datasource.py,sha256=GzY6JwH67b5QdV-UVdCFJfgGAIuZ96J2nl53YxZ
335
336
  tol/utils/__init__.py,sha256=764-Na1OaNGUDWpMIu51ZtXG7n_nB5MccUFK6LmkWRI,138
336
337
  tol/utils/csv.py,sha256=mihww25fSn72c4h-RFeqD_pFIG6KHZP4v1_C0rx81ws,421
337
338
  tol/utils/s3.py,sha256=aoYCwJ-qcMqFrpxmViFqPa0O1jgp0phtztO3-0CSNjw,491
338
- tol/validators/__init__.py,sha256=_ETv6oGQ2bTH_6-foYFy9T5wP5OG3cl96zEjvrIS7zk,1399
339
- tol/validators/allowed_keys.py,sha256=RJcHBiguL84B8hjSRaXLNES21yZqaKFwJNp2Tz9zvh0,1506
339
+ tol/validators/__init__.py,sha256=sF9i4rxi-NsJkqLvvxj2k_I_QPRgkhQZc9ErSqT7quk,1465
340
+ tol/validators/allowed_keys.py,sha256=eLZnr6DVqK6ru6b-T-1hvzSdAxlWZkTNZuohrbO0vS8,1525
340
341
  tol/validators/allowed_values.py,sha256=-Yy3Sqo1WYacGKlot_dn3M2o7Oj5MXOioJrJmrWCCxs,1536
341
342
  tol/validators/allowed_values_from_datasource.py,sha256=9cVwllBbzfCls8UsojazfCInt9_AakA0_H9pBO1wSL4,3173
342
343
  tol/validators/assert_on_condition.py,sha256=eBGgSVfIQ6e45SheM-ZDg7daXJjyZxRVS5L8AWvbXag,2027
@@ -351,6 +352,7 @@ tol/validators/regex.py,sha256=dLAi_vQt9_DsT6wQZmbYC7X5-Wp15l0leUE6XkPaItg,2602
351
352
  tol/validators/regex_by_value.py,sha256=XM5EnT4vgD17rfpR3bUE9I56IemSw26BI9MZtMakd4E,2582
352
353
  tol/validators/specimens_have_same_taxon.py,sha256=BaJcZ38ZprPcuGTIorSxxC9uGN0_lj6HS6B54EObcuY,2183
353
354
  tol/validators/sts_fields.py,sha256=aYbzy15btEg4-ocDT1qrspe7-atoWRrOJ_KmuPU6J14,8936
355
+ tol/validators/taxon_matches_goat.py,sha256=lYZ0qwPver9wXJm2ekv_6KcC49MugVXD9t1IAxPGG2Q,3492
354
356
  tol/validators/tolid.py,sha256=VOb6lNFz11H_0KaWX8_nvsw8xJEa6KrjB0p-5lkcqog,3885
355
357
  tol/validators/types.py,sha256=jMVpckRp8RS93f7usf58YH_K-5rKWgZIYs7bO9dHhQc,2914
356
358
  tol/validators/unique_value_check.py,sha256=sFvDooYkKeORvULGEOTsgIcxlbe0AXDWxY3Gbr3j0KI,1282
@@ -359,9 +361,9 @@ tol/validators/unique_whole_organisms.py,sha256=RdqA1GzIf3LTdrmNGGdxv0aW2udDY2P9
359
361
  tol/validators/value_check.py,sha256=DdNx_B1gns01zgBg5N6Bwia46Aukw6MAteM-M37Kv1k,1122
360
362
  tol/validators/interfaces/__init__.py,sha256=jtOxnwnwqV_29xjmmMcS_kvlt-pQiWwQYJn2YRP07_w,172
361
363
  tol/validators/interfaces/condition_evaluator.py,sha256=nj8Cb8hi47OBy6OVNfeLhF-Pjwtr8MiOSymYL6hfVes,3766
362
- tol_sdk-1.8.8.dist-info/licenses/LICENSE,sha256=RF9Jacy-9BpUAQQ20INhTgtaNBkmdTolYCHtrrkM2-8,1077
363
- tol_sdk-1.8.8.dist-info/METADATA,sha256=iyFf12GxWPRsRVPQgR5mrGzLDhAH-OrJ58-4T6CROLA,3142
364
- tol_sdk-1.8.8.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
365
- tol_sdk-1.8.8.dist-info/entry_points.txt,sha256=jH3HfTwxjzog7E3lq8CKpUWGIRY9FSXbyL6CpUmv6D0,36
366
- tol_sdk-1.8.8.dist-info/top_level.txt,sha256=PwKMQLphyZNvagBoriVbl8uwHXQl8IC1niawVG0iXMM,10
367
- tol_sdk-1.8.8.dist-info/RECORD,,
364
+ tol_sdk-1.8.10.dist-info/licenses/LICENSE,sha256=RF9Jacy-9BpUAQQ20INhTgtaNBkmdTolYCHtrrkM2-8,1077
365
+ tol_sdk-1.8.10.dist-info/METADATA,sha256=8VkqLVdpb45ixAI3mGjagWB0lDmyJYxfrSp6tTr-NFg,3143
366
+ tol_sdk-1.8.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
367
+ tol_sdk-1.8.10.dist-info/entry_points.txt,sha256=jH3HfTwxjzog7E3lq8CKpUWGIRY9FSXbyL6CpUmv6D0,36
368
+ tol_sdk-1.8.10.dist-info/top_level.txt,sha256=PwKMQLphyZNvagBoriVbl8uwHXQl8IC1niawVG0iXMM,10
369
+ tol_sdk-1.8.10.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.10.1)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5