File size: 3,783 Bytes
0ccfe4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Optional


@dataclass
class ALSPaper:
    pmid: str
    title: str
    abstract: str
    authors: list[str]
    year: int
    doi: str
    mesh_terms: list[str]
    # Populated after PMC fetch (None if not Open Access)
    full_text: Optional[str] = None
    # Populated after Semantic Scholar enrichment
    citation_count: int = 0
    # Populated after entity extraction
    entity_names: list[str] = field(default_factory=list)

    def to_dict(self) -> dict:
        return {
            "pmid": self.pmid,
            "title": self.title,
            "abstract": self.abstract,
            "authors": self.authors,
            "year": self.year,
            "doi": self.doi,
            "mesh_terms": self.mesh_terms,
            "full_text": self.full_text,
            "citation_count": self.citation_count,
            "entity_names": self.entity_names,
        }

    @classmethod
    def from_dict(cls, d: dict) -> ALSPaper:
        return cls(
            pmid=d["pmid"],
            title=d["title"],
            abstract=d["abstract"],
            authors=d.get("authors", []),
            year=d.get("year", 0),
            doi=d.get("doi", ""),
            mesh_terms=d.get("mesh_terms", []),
            full_text=d.get("full_text"),
            citation_count=d.get("citation_count", 0),
            entity_names=d.get("entity_names", []),
        )


@dataclass
class ExtractedEntity:
    type: str  # Gene | Protein | Compound | Pathway | Phenotype | Mechanism
    name: str  # raw name from text
    canonical_id: str  # normalized canonical identifier
    confidence: float  # 0.0–1.0
    mentions: int  # occurrence count in paper


@dataclass
class EntityRelationship:
    source: str  # canonical_id of source entity
    target: str  # canonical_id of target entity
    relation_type: str  # BINDS | INHIBITS | ASSOCIATED_WITH | TESTED_IN | EXPRESSED_IN | CO_OCCURS
    evidence_pmids: list[str]
    confidence: float  # average confidence across supporting papers
    evidence_text: str = ""  # representative excerpt from the paper


@dataclass
class PaperExtractionResult:
    pmid: str
    entities: list[ExtractedEntity]
    relationships: list[EntityRelationship]

    def to_dict(self) -> dict:
        return {
            "pmid": self.pmid,
            "entities": [
                {
                    "type": e.type,
                    "name": e.name,
                    "canonical_id": e.canonical_id,
                    "confidence": e.confidence,
                    "mentions": e.mentions,
                }
                for e in self.entities
            ],
            "relationships": [
                {
                    "source": r.source,
                    "target": r.target,
                    "relation_type": r.relation_type,
                    "evidence_pmids": r.evidence_pmids,
                    "confidence": r.confidence,
                    "evidence_text": r.evidence_text,
                }
                for r in self.relationships
            ],
        }


@dataclass
class TrialSummary:
    nct_id: str
    title: str
    phase: str
    status: str
    interventions: list[str]
    target_entities: list[str]  # canonical_ids of targeted genes/proteins/compounds
    sponsor: str = ""
    start_date: str = ""
    url: str = ""


@dataclass
class ResearchLandscape:
    query: str
    mechanisms: list[str]  # 2–3 key mechanism bullet points
    entities: list[dict]  # {name, type, description, paper_count}
    papers: list[dict]  # top 5: {pmid, title, year, doi, citation_count}
    trials: list[TrialSummary]
    evidence_count: int  # total papers retrieved
    generated_at: str  # ISO timestamp