mapkgsutils 0.0.3__tar.gz → 0.0.4__tar.gz
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.
- mapkgsutils-0.0.4/PKG-INFO +114 -0
- mapkgsutils-0.0.4/README.md +65 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/pyproject.toml +2 -2
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/config/schema.py +5 -6
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/consolidate.py +43 -29
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/context.py +9 -15
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/parsers/base.py +13 -83
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/version.py +1 -1
- mapkgsutils-0.0.3/PKG-INFO +0 -385
- mapkgsutils-0.0.3/README.md +0 -336
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/LICENSE +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/__init__.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/config/__init__.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/diff.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/download.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/exports.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/logging.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/parsers/__init__.py +0 -0
- {mapkgsutils-0.0.3 → mapkgsutils-0.0.4}/src/mapkgsutils/py.typed +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mapkgsutils
|
|
3
|
+
Version: 0.0.4
|
|
4
|
+
Summary: Utils shared by several mapping set generation tools for biomedical databases
|
|
5
|
+
Keywords: snekpack,cookiecutter
|
|
6
|
+
Author: Javier Millan Acosta
|
|
7
|
+
Author-email: Javier Millan Acosta <javier.millanacosta@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 1 - Planning
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Framework :: Pytest
|
|
15
|
+
Classifier: Framework :: Sphinx
|
|
16
|
+
Classifier: Natural Language :: English
|
|
17
|
+
Classifier: Programming Language :: Python
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Dist: more-itertools
|
|
26
|
+
Requires-Dist: tqdm
|
|
27
|
+
Requires-Dist: click
|
|
28
|
+
Requires-Dist: more-click
|
|
29
|
+
Requires-Dist: typing-extensions
|
|
30
|
+
Requires-Dist: pydantic>=2.0
|
|
31
|
+
Requires-Dist: pyyaml
|
|
32
|
+
Requires-Dist: sssom==0.4.21
|
|
33
|
+
Requires-Dist: sssom-schema==1.1.0a5
|
|
34
|
+
Requires-Dist: curies>=0.7.0
|
|
35
|
+
Requires-Dist: polars>=0.20.0
|
|
36
|
+
Requires-Dist: polars-runtime-32
|
|
37
|
+
Requires-Dist: pandas
|
|
38
|
+
Requires-Dist: numpy
|
|
39
|
+
Requires-Dist: httpx>=0.24.0
|
|
40
|
+
Maintainer: Javier Millan Acosta
|
|
41
|
+
Maintainer-email: Javier Millan Acosta <javier.millanacosta@gmail.com>
|
|
42
|
+
Requires-Python: >=3.10
|
|
43
|
+
Project-URL: Bug Tracker, https://github.com//mapkgsutils/issues
|
|
44
|
+
Project-URL: Homepage, https://github.com//mapkgsutils
|
|
45
|
+
Project-URL: Repository, https://github.com//mapkgsutils.git
|
|
46
|
+
Project-URL: Documentation, https://mapkgsutils.readthedocs.io
|
|
47
|
+
Project-URL: Funding, https://github.com/sponsors/jmillanacosta
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
<!--
|
|
51
|
+
<p align="center">
|
|
52
|
+
<img src="https://github.com//mapkgsutils/raw/main/docs/source/logo.png" height="150">
|
|
53
|
+
</p>
|
|
54
|
+
-->
|
|
55
|
+
|
|
56
|
+
<h1 align="center">
|
|
57
|
+
MaPkgsUtilS
|
|
58
|
+
</h1>
|
|
59
|
+
|
|
60
|
+
<p align="center">
|
|
61
|
+
<a href="https://github.com//mapkgsutils/actions/workflows/tests.yml">
|
|
62
|
+
<img alt="Tests" src="https://github.com//mapkgsutils/actions/workflows/tests.yml/badge.svg" /></a>
|
|
63
|
+
<a href="https://pypi.org/project/mapkgsutils">
|
|
64
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/mapkgsutils" /></a>
|
|
65
|
+
<a href="https://pypi.org/project/mapkgsutils">
|
|
66
|
+
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/mapkgsutils" /></a>
|
|
67
|
+
<a href="https://github.com//mapkgsutils/blob/main/LICENSE">
|
|
68
|
+
<img alt="PyPI - License" src="https://img.shields.io/pypi/l/mapkgsutils" /></a>
|
|
69
|
+
<a href='https://mapkgsutils.readthedocs.io/en/latest/?badge=latest'>
|
|
70
|
+
<img src='https://readthedocs.org/projects/mapkgsutils/badge/?version=latest' alt='Documentation Status' /></a>
|
|
71
|
+
<a href="https://codecov.io/gh//mapkgsutils/branch/main">
|
|
72
|
+
<img src="https://codecov.io/gh//mapkgsutils/branch/main/graph/badge.svg" alt="Codecov status" /></a>
|
|
73
|
+
<a href="https://github.com/cthoyt/cookiecutter-python-package">
|
|
74
|
+
<img alt="Cookiecutter template from @cthoyt" src="https://img.shields.io/badge/Cookiecutter-snekpack-blue" /></a>
|
|
75
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
76
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
|
|
77
|
+
<a href="https://github.com//mapkgsutils/blob/main/.github/CODE_OF_CONDUCT.md">
|
|
78
|
+
<img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"/></a>
|
|
79
|
+
<!-- uncomment if you archive on zenodo
|
|
80
|
+
<a href="https://doi.org/10.5281/zenodo.XXXXXX">
|
|
81
|
+
<img src="https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXX.svg" alt="DOI"></a>
|
|
82
|
+
-->
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
Utils shared by several mapping set generation tools for biomedical databases.
|
|
86
|
+
|
|
87
|
+
## 🚀 Installation
|
|
88
|
+
|
|
89
|
+
<!-- Uncomment this section after your first ``just finish``
|
|
90
|
+
The most recent release can be installed from
|
|
91
|
+
[PyPI](https://pypi.org/project/mapkgsutils/) with uv:
|
|
92
|
+
|
|
93
|
+
```console
|
|
94
|
+
$ uv pip install mapkgsutils
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
or with pip:
|
|
98
|
+
|
|
99
|
+
```console
|
|
100
|
+
$ python3 -m pip install mapkgsutils
|
|
101
|
+
```
|
|
102
|
+
-->
|
|
103
|
+
|
|
104
|
+
The most recent code and data can be installed directly from GitHub with uv:
|
|
105
|
+
|
|
106
|
+
```console
|
|
107
|
+
$ uv pip install git+https://github.com//mapkgsutils.git
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
or with pip:
|
|
111
|
+
|
|
112
|
+
```console
|
|
113
|
+
$ python3 -m pip install git+https://github.com//mapkgsutils.git
|
|
114
|
+
```
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img src="https://github.com//mapkgsutils/raw/main/docs/source/logo.png" height="150">
|
|
4
|
+
</p>
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<h1 align="center">
|
|
8
|
+
MaPkgsUtilS
|
|
9
|
+
</h1>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://github.com//mapkgsutils/actions/workflows/tests.yml">
|
|
13
|
+
<img alt="Tests" src="https://github.com//mapkgsutils/actions/workflows/tests.yml/badge.svg" /></a>
|
|
14
|
+
<a href="https://pypi.org/project/mapkgsutils">
|
|
15
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/mapkgsutils" /></a>
|
|
16
|
+
<a href="https://pypi.org/project/mapkgsutils">
|
|
17
|
+
<img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/mapkgsutils" /></a>
|
|
18
|
+
<a href="https://github.com//mapkgsutils/blob/main/LICENSE">
|
|
19
|
+
<img alt="PyPI - License" src="https://img.shields.io/pypi/l/mapkgsutils" /></a>
|
|
20
|
+
<a href='https://mapkgsutils.readthedocs.io/en/latest/?badge=latest'>
|
|
21
|
+
<img src='https://readthedocs.org/projects/mapkgsutils/badge/?version=latest' alt='Documentation Status' /></a>
|
|
22
|
+
<a href="https://codecov.io/gh//mapkgsutils/branch/main">
|
|
23
|
+
<img src="https://codecov.io/gh//mapkgsutils/branch/main/graph/badge.svg" alt="Codecov status" /></a>
|
|
24
|
+
<a href="https://github.com/cthoyt/cookiecutter-python-package">
|
|
25
|
+
<img alt="Cookiecutter template from @cthoyt" src="https://img.shields.io/badge/Cookiecutter-snekpack-blue" /></a>
|
|
26
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
27
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff" style="max-width:100%;"></a>
|
|
28
|
+
<a href="https://github.com//mapkgsutils/blob/main/.github/CODE_OF_CONDUCT.md">
|
|
29
|
+
<img src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg" alt="Contributor Covenant"/></a>
|
|
30
|
+
<!-- uncomment if you archive on zenodo
|
|
31
|
+
<a href="https://doi.org/10.5281/zenodo.XXXXXX">
|
|
32
|
+
<img src="https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXX.svg" alt="DOI"></a>
|
|
33
|
+
-->
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
Utils shared by several mapping set generation tools for biomedical databases.
|
|
37
|
+
|
|
38
|
+
## 🚀 Installation
|
|
39
|
+
|
|
40
|
+
<!-- Uncomment this section after your first ``just finish``
|
|
41
|
+
The most recent release can be installed from
|
|
42
|
+
[PyPI](https://pypi.org/project/mapkgsutils/) with uv:
|
|
43
|
+
|
|
44
|
+
```console
|
|
45
|
+
$ uv pip install mapkgsutils
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
or with pip:
|
|
49
|
+
|
|
50
|
+
```console
|
|
51
|
+
$ python3 -m pip install mapkgsutils
|
|
52
|
+
```
|
|
53
|
+
-->
|
|
54
|
+
|
|
55
|
+
The most recent code and data can be installed directly from GitHub with uv:
|
|
56
|
+
|
|
57
|
+
```console
|
|
58
|
+
$ uv pip install git+https://github.com//mapkgsutils.git
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
or with pip:
|
|
62
|
+
|
|
63
|
+
```console
|
|
64
|
+
$ python3 -m pip install git+https://github.com//mapkgsutils.git
|
|
65
|
+
```
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mapkgsutils"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "Utils shared by several mapping set generation tools for biomedical databases"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -236,7 +236,7 @@ known-first-party = [
|
|
|
236
236
|
docstring-code-format = true
|
|
237
237
|
|
|
238
238
|
[tool.bumpversion]
|
|
239
|
-
current_version = "0.0.
|
|
239
|
+
current_version = "0.0.4"
|
|
240
240
|
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<release>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P<build>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
|
|
241
241
|
serialize = [
|
|
242
242
|
"{major}.{minor}.{patch}-{release}+{build}",
|
|
@@ -65,11 +65,11 @@ class DistributionEraSchema(BaseModel):
|
|
|
65
65
|
class DatasourceConfigSchema(BaseModel):
|
|
66
66
|
"""Top-level schema for a ``config/<datasource>.yaml`` file.
|
|
67
67
|
|
|
68
|
-
``name``, ``prefix``, and ``curie_base_url`` are
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
in shape
|
|
72
|
-
:func:`validate_config_dict`),
|
|
68
|
+
``name``, ``prefix``, and ``curie_base_url`` are the only fields
|
|
69
|
+
:class:`~mapkgsutils.parsers.base.DatasourceConfig` declares without a
|
|
70
|
+
default, so they're required here too. Every other recognized key is
|
|
71
|
+
optional and permissive in shape. Unrecognized top-level keys just get a
|
|
72
|
+
warning (see :func:`validate_config_dict`), not a hard error.
|
|
73
73
|
"""
|
|
74
74
|
|
|
75
75
|
model_config = ConfigDict(extra="allow")
|
|
@@ -99,7 +99,6 @@ class DatasourceConfigSchema(BaseModel):
|
|
|
99
99
|
distribution_eras: list[DistributionEraSchema] = Field(default_factory=list)
|
|
100
100
|
xref_sources: list[XrefSourceSchema] = Field(default_factory=list)
|
|
101
101
|
species: dict[str, Any] = Field(default_factory=dict)
|
|
102
|
-
genome_build: dict[str, Any] = Field(default_factory=dict)
|
|
103
102
|
subset: dict[str, Any] = Field(default_factory=dict)
|
|
104
103
|
mappingset: dict[str, Any] = Field(default_factory=dict)
|
|
105
104
|
mapping: dict[str, Any] = Field(default_factory=dict)
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
"""Generic mapping-date consolidation: cache I/O and the release/date walk skeleton.
|
|
2
2
|
|
|
3
|
-
A versioned datasource'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
download/parse one, which mapping-set class to build) is supplied by the
|
|
11
|
-
caller as plain callables/values -- see :mod:`pysec2pri.consolidate` for a
|
|
12
|
-
concrete binding.
|
|
3
|
+
A versioned datasource doesn't always record when a mapping first appeared.
|
|
4
|
+
Recovering that date means walking every historical release once and
|
|
5
|
+
recording the first/last release each mapping (keyed by its
|
|
6
|
+
version-independent pair hash) was seen in.
|
|
7
|
+
|
|
8
|
+
This module provides the cache I/O, the release-walk and single-pass-date
|
|
9
|
+
loop shapes, and rebuilding the cache back into a real SSSOM mapping set.
|
|
13
10
|
"""
|
|
14
11
|
|
|
15
12
|
from __future__ import annotations
|
|
@@ -44,9 +41,9 @@ CACHE_COLUMNS = (
|
|
|
44
41
|
"last_seen_version",
|
|
45
42
|
"last_seen_date",
|
|
46
43
|
# JSON-encoded snapshot of the mapping's own fields (subject_id,
|
|
47
|
-
# object_id, predicate_id, ...) as last seen
|
|
48
|
-
#
|
|
49
|
-
#
|
|
44
|
+
# object_id, predicate_id, ...) as last seen. Used to rebuild the cache
|
|
45
|
+
# into a real SSSOM mapping set later (see build_consolidated_mapping_set).
|
|
46
|
+
# Empty for legacy/hand-built rows.
|
|
50
47
|
"fields_json",
|
|
51
48
|
)
|
|
52
49
|
|
|
@@ -129,12 +126,12 @@ def _mapping_fields_json(m: Any) -> str:
|
|
|
129
126
|
"""JSON-encode a mapping's own fields (excluding mapping_date/record_id).
|
|
130
127
|
|
|
131
128
|
Snapshots a mapping's current shape (subject_id, object_id,
|
|
132
|
-
predicate_id, ...) so
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
predicate_id, ...) so :func:`build_consolidated_mapping_set` can rebuild
|
|
130
|
+
it into a real SSSOM mapping set later. ``default=str`` handles
|
|
131
|
+
enum-valued fields like ``mapping_cardinality``, which round-trip
|
|
132
|
+
cleanly back through ``Mapping(**fields)``. Returns ``"{}"`` for
|
|
133
|
+
non-dataclass stand-ins (e.g. test doubles); callers skip those rows
|
|
134
|
+
rather than materializing them.
|
|
138
135
|
"""
|
|
139
136
|
from dataclasses import fields as dataclass_fields
|
|
140
137
|
from dataclasses import is_dataclass
|
|
@@ -203,15 +200,18 @@ def build_consolidated_mapping_set(
|
|
|
203
200
|
mapping_set_class: type[Any],
|
|
204
201
|
record_namespace: str,
|
|
205
202
|
mapping_set_metadata: Mapping[str, Any],
|
|
203
|
+
cardinality_on: str = "id",
|
|
206
204
|
) -> Any:
|
|
207
205
|
"""Materialize the consolidated index as a real SSSOM mapping set.
|
|
208
206
|
|
|
209
|
-
Each
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
207
|
+
Each row's ``mapping_date`` and ``subject_source_version``/
|
|
208
|
+
``object_source_version`` are overridden from the record's
|
|
209
|
+
``first_seen_date``/``first_seen_version``: the date of first
|
|
210
|
+
appearance and the release it first appeared in, rather than whatever
|
|
211
|
+
the last-seen snapshot happened to carry. The two stay in their own
|
|
212
|
+
fields. A release version (e.g. ChEBI's "183") is not a date and never
|
|
213
|
+
goes in ``mapping_date``, and a real date never goes in the version
|
|
214
|
+
fields.
|
|
215
215
|
|
|
216
216
|
Args:
|
|
217
217
|
records: ``record_id -> fields`` dict as read by :func:`read_cache`.
|
|
@@ -221,6 +221,8 @@ def build_consolidated_mapping_set(
|
|
|
221
221
|
``record_id``.
|
|
222
222
|
mapping_set_metadata: ``mapping_set_id``/``curie_map``/``license``/...
|
|
223
223
|
metadata for the resulting mapping set.
|
|
224
|
+
cardinality_on: ``"id"`` or ``"label"``, forwarded to
|
|
225
|
+
:meth:`~mapkgsutils.parsers.base.BaseMappingSet._compute_cardinalities`.
|
|
224
226
|
|
|
225
227
|
Returns:
|
|
226
228
|
A ``mapping_set_class`` instance with cardinalities computed.
|
|
@@ -243,7 +245,15 @@ def build_consolidated_mapping_set(
|
|
|
243
245
|
# nothing to materialize into a real Mapping; date bookkeeping
|
|
244
246
|
# for them still lives in the TSV cache, just not in the SSSOM.
|
|
245
247
|
continue
|
|
248
|
+
# mapping_date and subject/object_source_version are semantically
|
|
249
|
+
# distinct SSSOM fields: a release version (e.g. ChEBI's "183") is
|
|
250
|
+
# never a date, and must never end up in mapping_date or vice versa.
|
|
251
|
+
# Override both from first_seen_*, not whatever the snapshot's own
|
|
252
|
+
# (last-seen) values were.
|
|
246
253
|
row_fields["mapping_date"] = fields.get("first_seen_date") or None
|
|
254
|
+
first_seen_version = fields.get("first_seen_version") or None
|
|
255
|
+
row_fields["subject_source_version"] = first_seen_version
|
|
256
|
+
row_fields["object_source_version"] = first_seen_version
|
|
247
257
|
row_fields["record_id"] = f"{record_namespace}{version_label}/consolidate/{pair_key}"
|
|
248
258
|
mappings.append(SSSOMMapping(**row_fields))
|
|
249
259
|
|
|
@@ -257,7 +267,7 @@ def build_consolidated_mapping_set(
|
|
|
257
267
|
mapping_set_description=mapping_set_metadata.get("mapping_set_description"),
|
|
258
268
|
license=mapping_set_metadata.get("license"),
|
|
259
269
|
)
|
|
260
|
-
mapping_set.
|
|
270
|
+
mapping_set._compute_cardinalities(on=cardinality_on)
|
|
261
271
|
return mapping_set
|
|
262
272
|
|
|
263
273
|
|
|
@@ -268,6 +278,7 @@ def write_consolidated_sssom(
|
|
|
268
278
|
mapping_set_class: type[Any],
|
|
269
279
|
record_namespace: str,
|
|
270
280
|
mapping_set_metadata: Mapping[str, Any],
|
|
281
|
+
cardinality_on: str = "id",
|
|
271
282
|
) -> Path:
|
|
272
283
|
"""Build and save the companion SSSOM mapping set next to the cache file.
|
|
273
284
|
|
|
@@ -278,6 +289,8 @@ def write_consolidated_sssom(
|
|
|
278
289
|
record_namespace: Base IRI namespace for rebuilt ``record_id`` values.
|
|
279
290
|
mapping_set_metadata: ``mapping_set_id``/``curie_map``/``license``/...
|
|
280
291
|
metadata for the resulting mapping set.
|
|
292
|
+
cardinality_on: ``"id"`` or ``"label"``, see
|
|
293
|
+
:func:`build_consolidated_mapping_set`.
|
|
281
294
|
|
|
282
295
|
Returns:
|
|
283
296
|
Path to the written SSSOM TSV (see :func:`sssom_output_path`).
|
|
@@ -290,6 +303,7 @@ def write_consolidated_sssom(
|
|
|
290
303
|
mapping_set_class=mapping_set_class,
|
|
291
304
|
record_namespace=record_namespace,
|
|
292
305
|
mapping_set_metadata=mapping_set_metadata,
|
|
306
|
+
cardinality_on=cardinality_on,
|
|
293
307
|
)
|
|
294
308
|
output_path = sssom_output_path(cache_path)
|
|
295
309
|
mapping_set.save("sssom", output_path)
|
|
@@ -341,9 +355,9 @@ def consolidate_by_release(
|
|
|
341
355
|
try:
|
|
342
356
|
mapping_set = run_one_version(v)
|
|
343
357
|
release_date = resolve_release_date(v)
|
|
344
|
-
# Empty
|
|
345
|
-
#
|
|
346
|
-
#
|
|
358
|
+
# Empty when no real release date resolves. v is a version, not
|
|
359
|
+
# a date, and isn't always date-shaped (e.g. ChEBI's plain
|
|
360
|
+
# release numbers), so don't store it as one.
|
|
347
361
|
date_str = release_date.date().isoformat() if release_date else ""
|
|
348
362
|
|
|
349
363
|
for m in mapping_set.mappings or []:
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
"""Disambiguate ambiguous
|
|
1
|
+
"""Disambiguate ambiguous mappings with context: label, id, and xref evidence.
|
|
2
2
|
|
|
3
3
|
:class:`ContextSpec` describes a per-row piece of evidence that can
|
|
4
4
|
be used to decide which of those two entities a given ambiguous cell actually
|
|
5
5
|
means:
|
|
6
6
|
|
|
7
|
-
* ``label``:
|
|
7
|
+
* ``label``: a label string.
|
|
8
8
|
* ``id`` : a related/foreign identifier string.
|
|
9
9
|
* ``xref`` : a cross-reference token (e.g. an Ensembl ID) resolved through
|
|
10
10
|
an independent :class:`XrefMapping` crosswalk table.
|
|
11
|
-
|
|
12
|
-
All three are resolved the same way: confirm *secondary usage* (the evidence
|
|
13
|
-
points to the mapping's target), confirm *primary usage* (the evidence points
|
|
14
|
-
to the token's own current identity), or leave the cell unresolved. Every
|
|
15
|
-
attempt can be recorded as a :class:`DecisionRecord` for an auditable TSV log.
|
|
16
11
|
"""
|
|
17
12
|
|
|
18
13
|
from __future__ import annotations
|
|
@@ -35,8 +30,8 @@ class XrefRecord:
|
|
|
35
30
|
|
|
36
31
|
Args:
|
|
37
32
|
subject_id: The cross-reference token, e.g. ``"ENSG00000197471"``.
|
|
38
|
-
object_id: The target
|
|
39
|
-
object_label: The target
|
|
33
|
+
object_id: The target id, e.g. ``"HGNC:11249"``.
|
|
34
|
+
object_label: The target label, e.g. ``"SPN"``.
|
|
40
35
|
predicate_id: The equivalence predicate, e.g. ``"skos:exactMatch"``.
|
|
41
36
|
``None`` when the source table carries no predicate column.
|
|
42
37
|
"""
|
|
@@ -93,8 +88,8 @@ def load_xref_mapping(
|
|
|
93
88
|
Args:
|
|
94
89
|
path: Path to the crosswalk file.
|
|
95
90
|
subject_col: Column with the cross-reference token.
|
|
96
|
-
object_col: Column with the target
|
|
97
|
-
object_label_col: Column with the target
|
|
91
|
+
object_col: Column with the target id.
|
|
92
|
+
object_label_col: Column with the target label (optional).
|
|
98
93
|
predicate_col: Column with the equivalence predicate (optional).
|
|
99
94
|
sep: Field delimiter.
|
|
100
95
|
|
|
@@ -257,13 +252,12 @@ def resolve_ambiguous_with_xref(
|
|
|
257
252
|
"""Attempt to resolve an ambiguous token using a cross-reference token.
|
|
258
253
|
|
|
259
254
|
Args:
|
|
260
|
-
token: The ambiguous label or ID
|
|
261
|
-
entry and as a secondary one pointing elsewhere).
|
|
255
|
+
token: The ambiguous label or ID.
|
|
262
256
|
xref_token: The row's cross-reference token, e.g. an Ensembl ID.
|
|
263
|
-
lkp: ``{
|
|
257
|
+
lkp: ``{token: resolved_token}`` lookup.
|
|
264
258
|
xref_index: ``{xref_token: [XrefRecord, ...]}`` (see
|
|
265
259
|
:meth:`XrefMapping.by_subject`).
|
|
266
|
-
token_to_id: ``{
|
|
260
|
+
token_to_id: ``{token: id}``. ``None`` when *token*
|
|
267
261
|
is already a CURIE (ID mode).
|
|
268
262
|
accepted_predicates: Equivalence predicates to accept. ``None``
|
|
269
263
|
accepts any predicate.
|
|
@@ -48,7 +48,7 @@ class DistributionEra:
|
|
|
48
48
|
format: str | None = None
|
|
49
49
|
from_version: str | None = None
|
|
50
50
|
to_version: str | None = None
|
|
51
|
-
wayback: bool = False # declarative only for now
|
|
51
|
+
wayback: bool = False # declarative only for now, no resolver yet
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
@dataclass
|
|
@@ -124,8 +124,6 @@ class DatasourceConfig:
|
|
|
124
124
|
xref_sources: list[XrefSource] = field(default_factory=list)
|
|
125
125
|
# Species this datasource publishes
|
|
126
126
|
species: dict[str, Any] = field(default_factory=dict)
|
|
127
|
-
# Genome assembly/build metadata.
|
|
128
|
-
genome_build: dict[str, Any] = field(default_factory=dict)
|
|
129
127
|
# Compound/entry subset this datasource publishes (e.g. ChEBI's
|
|
130
128
|
# 3star/complete). Generic, config-driven counterpart to `species`.
|
|
131
129
|
subset: dict[str, Any] = field(default_factory=dict)
|
|
@@ -299,7 +297,6 @@ def get_datasource_config(datasource_name: str, *, config_package: str) -> Datas
|
|
|
299
297
|
distribution_eras=eras,
|
|
300
298
|
xref_sources=xref_sources,
|
|
301
299
|
species=raw.get("species") or {},
|
|
302
|
-
genome_build=raw.get("genome_build") or {},
|
|
303
300
|
subset=raw.get("subset") or {},
|
|
304
301
|
mappingset_metadata=raw.get("mappingset", {}),
|
|
305
302
|
mapping_metadata=raw.get("mapping", {}),
|
|
@@ -507,16 +504,16 @@ def pair_hash(pri: str, sec: str) -> str:
|
|
|
507
504
|
"""Version-independent 16-hex-char digest for a (pri, sec) pair.
|
|
508
505
|
|
|
509
506
|
The same pair always hashes identically, regardless of release/version
|
|
510
|
-
or product (species/subset).
|
|
511
|
-
|
|
512
|
-
when it first/last appeared
|
|
513
|
-
field
|
|
507
|
+
or product (species/subset). A cross-release consolidation layer uses
|
|
508
|
+
this as the join key to match a mapping across releases and discover
|
|
509
|
+
when it first/last appeared. It is not what ends up in the
|
|
510
|
+
``record_id`` field; see :func:`mint_record_id` for that.
|
|
514
511
|
"""
|
|
515
512
|
return hashlib.sha256(f"{pri}|{sec}".encode()).hexdigest()[:16]
|
|
516
513
|
|
|
517
514
|
|
|
518
515
|
def mint_record_id(pri: str, sec: str, *, namespace: str) -> str:
|
|
519
|
-
"""Mint a row's ``record_id
|
|
516
|
+
"""Mint a row's ``record_id``, the row's OWL Axiom IRI in SSSOM's RDF/OWL output.
|
|
520
517
|
|
|
521
518
|
Scoped to *namespace* (typically a release- and product-specific prefix,
|
|
522
519
|
see :meth:`BaseParser._record_namespace`), so the same (pri, sec) pair
|
|
@@ -528,7 +525,7 @@ def mint_record_id(pri: str, sec: str, *, namespace: str) -> str:
|
|
|
528
525
|
confidence, ...) under the same IRI.
|
|
529
526
|
|
|
530
527
|
The trailing 16 hex characters are always :func:`pair_hash`'s
|
|
531
|
-
version-independent digest
|
|
528
|
+
version-independent digest. Use that function directly, not this one,
|
|
532
529
|
for cross-release matching/lookups.
|
|
533
530
|
"""
|
|
534
531
|
return f"{namespace}{pair_hash(pri, sec)}"
|
|
@@ -1278,12 +1275,6 @@ class BaseParser(ABC):
|
|
|
1278
1275
|
"""
|
|
1279
1276
|
self.version = version
|
|
1280
1277
|
self.show_progress = show_progress
|
|
1281
|
-
# Genome assembly/build for the SSSOM ``*_source_version`` fields.
|
|
1282
|
-
# Default ``None`` -> resolved from config per species, or left to
|
|
1283
|
-
# fall back to ``self.version``. Parsers that read a build straight
|
|
1284
|
-
# from the data (e.g. an Ensembl-style ``mapping_session``) may set
|
|
1285
|
-
# this, or override per mapping row.
|
|
1286
|
-
self.genome_build: str | None = None
|
|
1287
1278
|
# Release date of the source data, used for the SSSOM ``mapping_date``.
|
|
1288
1279
|
# Set by the download layer to the upstream release date; falls back
|
|
1289
1280
|
# to the version when that is an ISO date (e.g. quarterly
|
|
@@ -1370,7 +1361,7 @@ class BaseParser(ABC):
|
|
|
1370
1361
|
|
|
1371
1362
|
``None`` for most parsers (one release == one product). Override
|
|
1372
1363
|
when a parser option selects a genuinely different dataset rather
|
|
1373
|
-
than just a different output mode
|
|
1364
|
+
than just a different output mode, e.g. a species selector for a
|
|
1374
1365
|
multi-species datasource, where the same release number produces a
|
|
1375
1366
|
disjoint set of mappings per species. Folded into ``mapping_set_id``
|
|
1376
1367
|
and :meth:`_record_namespace` so two runs that differ only in this
|
|
@@ -1384,8 +1375,8 @@ class BaseParser(ABC):
|
|
|
1384
1375
|
Mirrors ``mapping_set_id``'s ``{base}/{version}/{slug}`` ordering
|
|
1385
1376
|
(see :meth:`create_mapping_set`) so a mapping's ``record_id`` is
|
|
1386
1377
|
scoped to the same release/product as the mapping *set* it's
|
|
1387
|
-
asserted in
|
|
1388
|
-
``mapping_metadata()["record_id"]`` directly
|
|
1378
|
+
asserted in. Use this, instead of reading
|
|
1379
|
+
``mapping_metadata()["record_id"]`` directly, when building
|
|
1389
1380
|
per-row ``record_id`` values.
|
|
1390
1381
|
"""
|
|
1391
1382
|
base = str(self.get_mapping_metadata().get("record_id") or "")
|
|
@@ -1717,74 +1708,13 @@ class BaseParser(ABC):
|
|
|
1717
1708
|
return str(self.version)
|
|
1718
1709
|
return date.today().isoformat()
|
|
1719
1710
|
|
|
1720
|
-
def _species_build(self) -> str | None:
|
|
1721
|
-
"""Return the configured genome build for this run's species, if any.
|
|
1722
|
-
|
|
1723
|
-
Reads ``species.available[<species>].build`` from config.
|
|
1724
|
-
``self.species`` may be a canonical taxon ID (single-species runs)
|
|
1725
|
-
or a datasource token (all-species runs), so both the ``available``
|
|
1726
|
-
key and each entry's ``token`` are matched.
|
|
1727
|
-
|
|
1728
|
-
Returns:
|
|
1729
|
-
The per-species build string, or ``None`` when not configured.
|
|
1730
|
-
"""
|
|
1731
|
-
cfg = self._config
|
|
1732
|
-
species = getattr(self, "species", None)
|
|
1733
|
-
if not cfg or species is None:
|
|
1734
|
-
return None
|
|
1735
|
-
available = (cfg.species or {}).get("available") or {}
|
|
1736
|
-
entry = available.get(species) or available.get(str(species))
|
|
1737
|
-
if entry is None:
|
|
1738
|
-
for value in available.values():
|
|
1739
|
-
if isinstance(value, dict) and str(value.get("token")) == str(species):
|
|
1740
|
-
entry = value
|
|
1741
|
-
break
|
|
1742
|
-
if isinstance(entry, dict) and entry.get("build"):
|
|
1743
|
-
return str(entry["build"])
|
|
1744
|
-
return None
|
|
1745
|
-
|
|
1746
|
-
def _genome_build(self) -> str | None:
|
|
1747
|
-
"""Resolve the genome assembly/build for the source-version fields.
|
|
1748
|
-
|
|
1749
|
-
The mapping set's release is already explicit in
|
|
1750
|
-
``mapping_set_version`` / ``mapping_set_id``, so
|
|
1751
|
-
``subject_source_version`` / ``object_source_version`` carry the
|
|
1752
|
-
genome build (e.g. ``"GRCh38"``) rather than repeating the release.
|
|
1753
|
-
Resolution order:
|
|
1754
|
-
|
|
1755
|
-
1. an explicit :attr:`genome_build` override (e.g. a build a parser
|
|
1756
|
-
discovered straight from the data);
|
|
1757
|
-
2. the per-species build from ``species.available[<species>].build``;
|
|
1758
|
-
3. the datasource-wide ``genome_build.default`` -- but *only* for
|
|
1759
|
-
single-build datasources (those with no ``species.available`` map,
|
|
1760
|
-
e.g. a human-only datasource). A multi-species datasource never
|
|
1761
|
-
falls through to ``default``, so it can't stamp one build (say
|
|
1762
|
-
GRCh38) onto a non-human species; an uncurated species is left to
|
|
1763
|
-
fall back to the release instead.
|
|
1764
|
-
|
|
1765
|
-
Returns:
|
|
1766
|
-
The resolved build, or ``None`` when none is configured (e.g.
|
|
1767
|
-
ChEBI), so callers fall back to :attr:`version`.
|
|
1768
|
-
"""
|
|
1769
|
-
if self.genome_build:
|
|
1770
|
-
return self.genome_build
|
|
1771
|
-
species_build = self._species_build()
|
|
1772
|
-
if species_build:
|
|
1773
|
-
return species_build
|
|
1774
|
-
cfg = self._config
|
|
1775
|
-
if not cfg or (cfg.species or {}).get("available"):
|
|
1776
|
-
return None
|
|
1777
|
-
default = (cfg.genome_build or {}).get("default")
|
|
1778
|
-
return str(default) if default else None
|
|
1779
|
-
|
|
1780
1711
|
def _source_version(self) -> str | None:
|
|
1781
1712
|
"""Return the value for the set-level SSSOM ``*_source_version`` fields.
|
|
1782
1713
|
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
datasources without a genome build, e.g. ChEBI).
|
|
1714
|
+
Always the analyzed release, :attr:`version`. The same field means
|
|
1715
|
+
the same thing across every datasource's mapping set.
|
|
1786
1716
|
"""
|
|
1787
|
-
return self.
|
|
1717
|
+
return self.version
|
|
1788
1718
|
|
|
1789
1719
|
def create_mapping_set(
|
|
1790
1720
|
self,
|