Spaces:
Running
Running
File size: 6,885 Bytes
42fb3af e53bbe5 42fb3af | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | """
Entity name normalization to canonical IDs.
Priority: static alias table β known compound map β slugify fallback.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
from config import CANONICAL_IDS_PATH
# ββ Gene / protein alias table ββββββββββββββββββββββββββββββββββββββββββββββββ
_GENE_ALIASES: dict[str, str] = {
# TDP-43 / TARDBP
"TDP-43": "TARDBP", "TDP43": "TARDBP", "tdp-43": "TARDBP", "tdp43": "TARDBP",
"TAR DNA-binding protein 43": "TARDBP",
"TAR DNA binding protein 43": "TARDBP",
"TAR DNA-binding protein": "TARDBP",
# FUS
"FUS/TLS": "FUS", "TLS": "FUS", "TLS/FUS": "FUS", "fus": "FUS",
"fused in sarcoma": "FUS",
# C9orf72
"C9ORF72": "C9orf72", "c9orf72": "C9orf72", "C9": "C9orf72",
"chromosome 9 open reading frame 72": "C9orf72",
# SOD1
"SOD-1": "SOD1", "sod1": "SOD1",
"superoxide dismutase 1": "SOD1",
"Cu/Zn-superoxide dismutase": "SOD1",
"copper-zinc superoxide dismutase": "SOD1",
# ATXN2
"ataxin-2": "ATXN2", "ataxin 2": "ATXN2", "SCA2": "ATXN2",
# Optineurin
"optineurin": "OPTN",
# Ubiquilin
"ubiquilin-2": "UBQLN2", "ubiquilin2": "UBQLN2", "ubiquilin 2": "UBQLN2",
# SQSTM1 / p62
"p62": "SQSTM1", "sequestosome-1": "SQSTM1", "sequestosome 1": "SQSTM1",
# Profilin
"profilin 1": "PFN1", "profilin-1": "PFN1",
# Dynactin
"dynactin": "DCTN1", "dynactin 1": "DCTN1",
# Matrin
"matrin 3": "MATR3", "matrin-3": "MATR3",
# Other
"angiogenin": "ANG",
"senataxin": "SETX",
}
# ββ Compound alias table βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
_COMPOUND_ALIASES: dict[str, str] = {
"riluzole": "riluzole", "Riluzole": "riluzole",
"edaravone": "edaravone", "Edaravone": "edaravone", "MCI-186": "edaravone",
"tofersen": "tofersen", "Tofersen": "tofersen",
"BIIB067": "tofersen", "biib067": "tofersen",
"AMX0035": "AMX0035", "amx0035": "AMX0035",
"sodium phenylbutyrate": "AMX0035",
"tauroursodeoxycholic acid": "AMX0035",
"TUDCA": "AMX0035",
"masitinib": "masitinib", "Masitinib": "masitinib", "AB1010": "masitinib",
"bosutinib": "bosutinib", "Bosutinib": "bosutinib", "SKI-606": "bosutinib",
"mexiletine": "mexiletine", "Mexiletine": "mexiletine",
"memantine": "memantine", "Memantine": "memantine",
"rasagiline": "rasagiline", "Rasagiline": "rasagiline",
"NurOwn": "NurOwn", "MSC-NTF": "NurOwn",
"ozanezumab": "ozanezumab",
}
# ββ Mechanism normalization ββββββββββββββββββββββββββββββββββββββββββββββββββββ
_MECHANISM_ALIASES: dict[str, str] = {
"glutamate excitotoxicity": "glutamate_excitotoxicity",
"excitotoxicity": "glutamate_excitotoxicity",
"glutamatergic excitotoxicity": "glutamate_excitotoxicity",
"oxidative stress": "oxidative_stress",
"reactive oxygen species": "oxidative_stress",
"ROS": "oxidative_stress",
"neuroinflammation": "neuroinflammation",
"microglial activation": "neuroinflammation",
"astrocyte activation": "neuroinflammation",
"protein aggregation": "protein_aggregation",
"protein misfolding": "protein_aggregation",
"protein inclusions": "protein_aggregation",
"RNA metabolism": "RNA_metabolism_dysfunction",
"RNA processing": "RNA_metabolism_dysfunction",
"RNA-binding protein dysfunction": "RNA_metabolism_dysfunction",
"stress granules": "RNA_metabolism_dysfunction",
"mitochondrial dysfunction": "mitochondrial_dysfunction",
"mitochondrial impairment": "mitochondrial_dysfunction",
"axonal transport": "axonal_transport_defect",
"axonal transport defect": "axonal_transport_defect",
"autophagy": "autophagy_impairment",
"mitophagy": "autophagy_impairment",
"ubiquitin proteasome": "autophagy_impairment",
"TDP-43 pathology": "TDP43_pathology",
"TDP-43 aggregation": "TDP43_pathology",
"TDP-43 mislocalization": "TDP43_pathology",
"antisense oligonucleotide": "antisense_oligonucleotide",
"ASO": "antisense_oligonucleotide",
"gene therapy": "gene_therapy",
"stem cell": "stem_cell_therapy",
"neurodegeneration": "neurodegeneration",
"apoptosis": "apoptosis",
"DNA damage": "DNA_damage_repair",
}
def guess_entity_type(name: str) -> str:
"""Best-effort entity type inference from name. Used when type metadata is unavailable."""
n = name.strip()
if n.upper() in _GENE_ALIASES or n in _GENE_ALIASES:
return "Gene"
if n in _COMPOUND_ALIASES:
return "Compound"
return "Protein"
def normalize_entity(name: str, entity_type: str) -> str:
"""Return a canonical_id string in the form '<prefix>:<canonical_name>'."""
canonical = _resolve_name(name.strip(), entity_type)
return f"{_prefix(entity_type)}:{canonical}"
def _resolve_name(name: str, entity_type: str) -> str:
t = entity_type.lower()
if t == "gene":
return _GENE_ALIASES.get(name) or _GENE_ALIASES.get(name.upper()) or name.upper()
if t == "protein":
gene_hit = _GENE_ALIASES.get(name) or _GENE_ALIASES.get(name.upper())
if gene_hit:
return gene_hit
return name[0].upper() + name[1:] if name else name
if t == "compound":
return _COMPOUND_ALIASES.get(name) or _slugify(name)
if t == "mechanism":
return _MECHANISM_ALIASES.get(name) or _slugify(name)
return _slugify(name)
def _prefix(entity_type: str) -> str:
return {
"gene": "gene",
"protein": "protein",
"compound": "compound",
"pathway": "pathway",
"phenotype": "phenotype",
"mechanism": "mechanism",
}.get(entity_type.lower(), "entity")
def _slugify(text: str) -> str:
return re.sub(r"[^a-z0-9]+", "_", text.lower()).strip("_")
class CanonicalRegistry:
"""Persistent nameβcanonical_id mapping written to canonical_ids.json."""
def __init__(self, path: Path = CANONICAL_IDS_PATH) -> None:
self.path = path
self._data: dict[str, str] = {}
if path.exists():
self._data = json.loads(path.read_text())
def resolve(self, name: str, entity_type: str) -> str:
key = f"{entity_type.lower()}:{name}"
if key not in self._data:
self._data[key] = normalize_entity(name, entity_type)
return self._data[key]
def save(self) -> None:
self.path.parent.mkdir(parents=True, exist_ok=True)
self.path.write_text(json.dumps(self._data, indent=2, sort_keys=True))
def __len__(self) -> int:
return len(self._data)
|