Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: pip install uv

- name: Install dependencies
run: uv sync --group dev
run: uv sync --locked --group dev

- name: Run pre-commit (lint + format + tests)
run: uv run pre-commit run --all-files
run: uv run pre-commit run --all-files
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ dependencies = [
"connectorx>=0.4.3",
"fastexcel>=0.16.0",
"httpx>=0.28.0",
"python-dotenv>=1.0.0",
"hydra-core>=1.3.2",
"loguru>=0.7.3",
"ontoma[ner]>=2.4.1",
Expand Down
2 changes: 1 addition & 1 deletion src/mira/provider/chembl/curation.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def extract_chembl_ct_curation(
host=db_host,
port=db_port,
service=db_service,
db_schema="CHEMBL_36",
db_schema="CHEMBL_37",
limit=None,
init_client_lib_dir=oracle_client_path,
)
Expand Down
12 changes: 11 additions & 1 deletion src/mira/provider/chembl/indications.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Extraction of clinical reports from ChEMBL drug/indications dataset."""

import polars as pl
import polars_hash as plh

from mira.dataset import ClinicalReport
from mira.dataset.clinical_report import APPROVAL_SOURCES
Expand Down Expand Up @@ -42,7 +43,7 @@ def extract_clinical_report(
id=(
pl.when(pl.col("ref_type") == "INN")
.then(
pl.concat_str(
plh.concat_str(
pl.col("ref_id"),
pl.lit("/"),
pl.col("efo_term"),
Expand Down Expand Up @@ -92,6 +93,15 @@ def extract_clinical_report(
type=pl.lit(ClinicalReportType.INDICATION.value),
)
.explode("id")
.with_columns(
id=(
# ID is hashed when it does not relate to the representation in the primary source
pl.when(pl.col("source").is_in(["INN", "FDA", "USAN"]))
.then(pl.col("id").chash.sha2_256())
# For DalyMed, EMA, ATC - ID remains the original value (can be queried in the primary sources)
.otherwise(pl.col("id"))
)
)
.unique()
)

Expand Down
8 changes: 5 additions & 3 deletions src/mira/provider/ttd.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import polars as pl
import polars_hash as plh

from mira.dataset.clinical_report import ClinicalReport
from mira.schemas import (
Expand Down Expand Up @@ -69,9 +70,10 @@ def extract_clinical_report(
) -> ClinicalReport:
"""Extract clinical reports from TTD drug/disease dataset."""
reports = indications.select(
id=pl.concat_str(
[pl.col("ttd_id"), pl.lit("/"), pl.col("diseaseFromSource")]
).str.to_lowercase(),
id=plh.concat_str(
pl.col("ttd_id"),
pl.col("diseaseFromSource").str.to_lowercase(),
).chash.sha2_256(),
origin=pl.lit(ClinicalReportOrigin.CURATED_RESOURCE),
url=pl.concat_str(
[pl.lit("https://ttd.idrblab.cn/data/drug/details/"), pl.col("ttd_id")]
Expand Down
16 changes: 12 additions & 4 deletions src/mira/utils/db.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from urllib.parse import quote

import polars as pl
from loguru import logger

Expand All @@ -16,10 +18,14 @@ def construct_db_uri(
db_uri: str,
db_user: str | None = None,
db_password: str | None = None,
):
"""Constructs a database URI from the given parameters."""
) -> str:
"""Construct a database URI, requiring complete credentials when supplied."""
if bool(db_user) != bool(db_password):
raise ValueError("db_user and db_password must be set together")
if db_user and db_password:
return f"{db_type}://{db_user}:{db_password}@{db_uri}"
user = quote(db_user, safe="")
password = quote(db_password, safe="")
return f"{db_type}://{user}:{password}@{db_uri}"
return f"{db_type}://{db_uri}"


Expand All @@ -39,7 +45,8 @@ def _build_select_query(
select_cols: List of columns or a raw
limit: Optional row limit.
dialect: "generic" uses SQL LIMIT. "oracle" uses FETCH FIRST.
where_clause: Optional WHERE clause (without the WHERE keyword).
where_clause: Optional raw SQL WHERE clause (without the WHERE keyword).
It is intentionally interpolated as supplied by the caller.

Returns:
SQL query string.
Expand All @@ -56,6 +63,7 @@ def _build_select_query(
query += f" WHERE {where_clause}"

if limit is not None:
limit = int(limit)
if dialect.lower() == "oracle":
query += f" FETCH FIRST {limit} ROWS ONLY"
else:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import pytest

from mira.utils.db import _build_select_query, construct_db_uri


def test_construct_db_uri_quotes_credentials() -> None:
assert (
construct_db_uri(
"postgresql",
"localhost:5432/db",
db_user="user@example",
db_password="p@ss:/word",
)
== "postgresql://user%40example:p%40ss%3A%2Fword@localhost:5432/db"
)


@pytest.mark.parametrize(
("db_user", "db_password"),
[("user", None), (None, "password"), ("", "password"), ("user", "")],
)
def test_construct_db_uri_rejects_partial_credentials(
db_user: str | None, db_password: str | None
) -> None:
with pytest.raises(ValueError, match="set together"):
construct_db_uri("postgresql", "localhost/db", db_user, db_password)


def test_build_select_query_coerces_limit() -> None:
assert _build_select_query("table", "schema", limit="5") == (
"SELECT DISTINCT * FROM schema.table LIMIT 5"
)


def test_build_select_query_documents_raw_where_clause() -> None:
assert _build_select_query("table", "schema", where_clause="id > 1") == (
"SELECT DISTINCT * FROM schema.table WHERE id > 1"
)
11 changes: 0 additions & 11 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading