SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%
12.4 KB · 94 lines markdown
Rendered Raw Blame History
1# Connector `civic` — CIViC (Clinical Interpretation of Variants in Cancer)23| | |4|---|---|5| Source | https://civicdb.org — Washington University School of Medicine (McDonnell Genome Institute) |6| Tier / category | 0 / evidence |7| Access | GraphQL, `POST https://civicdb.org/api/graphql`, no authentication |8| Docs verified | 2026-09-08 — https://docs.civicdb.org/en/latest/api.html, https://griffithlab.github.io/civic-v2/, live schema introspection |9| License | **CC0 1.0 Universal** (content), MIT (application code) — `licenseStatus: approved` |10| Code | `packages/connectors/src/connectors/civic/` (`manifest.ts`, `graphql.ts`, `normalize.ts`, `genes.ts`, `index.ts`, `civic.test.ts`, `fixtures/`) |1112## License (quoted)1314Docs FAQ, "How is CIViC licensed?" (https://docs.civicdb.org/en/latest/about/faq.html):1516> The content of CIViC, hosted by Washington University School of Medicine is released under the Creative Commons Public Domain Dedication (CC0 1.0 Universal) and the source code for the CIViC application is licensed under the MIT License.1718Citation (FAQ "How do I cite CIViC?"): Griffith M, Spies NC, Krysiak K, et al. *CIViC is a community knowledgebase for expert crowdsourcing the clinical interpretation of variants in cancer.* Nat Genet 2017 — **PMID 28138153**. CIViC disclaimer: "intended for purely research purposes" — CancerIndex mirrors this (no treatment recommendations).1920## Verified endpoints and fields (introspection 2026-09-08)2122Relay connections, `first` ≤ 100 (a request for 200 silently returns 100), `after` = `pageInfo.endCursor`. Counts observed: genes 734, variants 5,071, diseases 877, therapies 33,697 (655 with `hasLinkedEvidence: true`), molecularProfiles 5,667, evidenceItems 11,546 (default = NON_REJECTED) / **11,968 with `status: ALL`** / 4,916 ACCEPTED.2324| Query | Fields used |25|---|---|26| `genes` | `id name fullName entrezId description featureAliases deprecated` |27| `variants` | `id name feature{id name featureType} variantAliases variantTypes{name soid}`; `… on GeneVariant { alleleRegistryId clinvarIds hgvsDescriptions maneSelectTranscript coordinates{referenceBuild ensemblVersion chromosome start stop referenceBases variantBases representativeTranscript coordinateType} }`; `… on FusionVariant { viccCompliantName fusion{fivePrimeGene{name entrezId} threePrimeGene{…}} fivePrimeCoordinates{…} threePrimeCoordinates{…} }`; `… on FactorVariant { ncitId }` |28| `diseases` | `id name displayName doid diseaseUrl diseaseAliases deprecated` |29| `therapies(hasLinkedEvidence: true)` | `id name ncitId therapyUrl therapyAliases deprecated` |30| `evidenceItems(status: ALL)` | `id name description status evidenceType evidenceLevel evidenceDirection significance evidenceRating therapyInteractionType variantOrigin disease{id name doid} therapies{id name ncitId} molecularProfile{id name variants{id name feature{…}}} source{id citation citationId sourceType pmcId publicationYear ascoAbstractId title journal sourceUrl retracted} phenotypes{id name hpoId}` |3132Enums verified: EvidenceType `DIAGNOSTIC PROGNOSTIC PREDICTIVE PREDISPOSING FUNCTIONAL ONCOGENIC`; EvidenceLevel `A–E`; EvidenceDirection `SUPPORTS DOES_NOT_SUPPORT NA`; EvidenceSignificance (24 values, e.g. `SENSITIVITYRESPONSE RESISTANCE REDUCED_SENSITIVITY BETTER_OUTCOME POOR_OUTCOME POSITIVE NEGATIVE PREDISPOSITION PROTECTIVENESS ONCOGENICITY …`); EvidenceStatus `ACCEPTED SUBMITTED REJECTED`; SourceSource `PUBMED ASCO ASH`; ReferenceBuild `NCBI36 GRCH37 GRCH38`; FeatureInstanceTypes `GENE FACTOR FUSION REGION`.3334Health check: `{ evidenceItems(first:1){ totalCount } }`.3536## Sync design3738Five phases, each a paged crawl whose `endCursor` and progress are stored in `ctx.cursor` (`{pass, genes:{after,done,seen}, variants, diseases, therapies, evidence, diseaseMap}`), so a run interrupted by the time budget or a crash resumes where it stopped. A completed pass (`completedAt`) starts a fresh pass on the next run (CIViC has no changed-since filter; every run is a full crawl). `dry_run` fetches 10 records of each entity and validates them. Every record goes through `ctx.upsertSourceRecord` (idempotency + raw lake `data/raw/civic/<date>/<entity>/`).39401. **genes** → `genes` rows by symbol via the shared `GeneCache` (`civic/genes.ts`): existing symbol reused; otherwise a *minimal* row (symbol, name null, status Approved, hgncId null) that HGNC later enriches; `civicGeneId` / `ncbiGeneId` filled only when empty.412. **variants** → `variants` (+ `variant_aliases`, incl. the ClinGen Allele Registry id `CA…`): slug `slugify("<feature>-<name>")` (`-civic-<id>` suffix on collision); `civicVariantId`; `clinvarVariationId` = first numeric `clinvarIds` entry (placeholders `"NONE FOUND"`, `"N/A"` dropped); `dbsnpIds` from aliases `RS…`; HGVS c./p./g. split from `hgvsDescriptions` (RefSeq preferred over Ensembl); assembly `GRCH37/GRCH38 → GRCh37/GRCh38`; coordinates JSON list (`role: variant | five_prime | three_prime`); fusions: `fusionPartners = [5′, 3′]`, anchored on the 3′ (else 5′) gene; factors/regions carry no gene.423. **diseases** → `CancerResolver.byCode('doid')` → `byLabel(name)` → `byLabel(alias)` (recorded as `ALIAS`). On a hit: `cancer_codes (system 'doid', matchType = resolver match type)` and the CIViC name as `cancer_aliases` synonym. Misses → `unresolved_labels` (`entity_kind cancer`, context `{doid, civicDiseaseId, aliases}`).434. **therapies** (linked to evidence only) → `drugs` by `ncitCode` → `civicTherapyId` → slug; `drug_aliases` (name as `generic`, aliases as `synonym`).445. **evidence items** → `civic_evidence_items` (native structure, all statuses; `cancerId/cancerMatchType`, `geneIds/geneSymbols/variantIds/civicVariantIds` from the molecular profile, `therapyIds`, `pmid` when `sourceType = PUBMED`), one `provenance` row per item (`sourceRecordId` = CIViC id, `sourceUrl https://civicdb.org/evidence/<id>`, `evidenceType expert_curation`, `accessLevel open`, `license CC0 1.0`; reused on re-runs).4546### Knowledge edges (ACCEPTED and `evidenceDirection = SUPPORTS` only)4748| CIViC type | Edge(s) |49|---|---|50| PREDICTIVE | variant **PREDICTS_RESPONSE_TO** drug (direction `sensitivity` ← SENSITIVITYRESPONSE, `resistance` ← RESISTANCE / REDUCED_SENSITIVITY, else `unknown`; `predictive = true`; `cancerContextIds = [cancer]`) + drug **INVESTIGATED_FOR** cancer |51| PROGNOSTIC | variant **PROGNOSTIC_IN** cancer (`prognostic = true`) |52| DIAGNOSTIC (significance POSITIVE) | variant **DIAGNOSTIC_OF** cancer |53| PREDISPOSING (PREDISPOSITION / PATHOGENIC / LIKELY_PATHOGENIC) | variant **PREDISPOSES_TO** cancer |54| ONCOGENIC / FUNCTIONAL | gene **ASSOCIATED_WITH** cancer |55| every item with a cancer | gene **ASSOCIATED_WITH** cancer |5657`evidenceLevel` = CIViC letter (never re-scaled), `evidenceScore` = `evidenceRating`, `evidenceCategory = curated_evidence`, `sourceRecordId` = evidence id, `provenanceIds = [provenance]`. Upsert on the unique key (source/target/type/source_id/source_record_id); `supportCount` stays 1 because the key already includes the evidence item (an increment per run would inflate support with every nightly crawl) — `lastSeenAt` and the evidence fields are refreshed. `DOES_NOT_SUPPORT` / `NA` items, DIAGNOSTIC NEGATIVE and PREDISPOSING PROTECTIVENESS/benign items are kept in `civic_evidence_items` only (an edge would invert their meaning).5859### Publications6061For ACCEPTED items with a PMID a **stub** `publications` row is ensured (`pmid`, title = source title or citation, journal, `pubYear`, `publicationTypes ['stub']`, no abstract) and `publication_entity_edges` (publication → cancer / gene / variant / drug, `method civic_curation`, `status validated`) are added. The PubMed connector enriches stubs.6263## Variant type mapping (SO term → `variant_type`)6465Evaluated in order on the SO term names: fusion/translocation → `fusion`; loss of heterozygosity → `loss_of_heterozygosity`; methylation → `epigenetic`; transcript amplification → `amplification`; transcript ablation / exon loss / feature truncation → `deletion_cna`; copy number change → `amplification` / `deletion_cna` / `structural` by name; splice → `splice`; regulatory region / promoter → `promoter_mutation`; delins / inframe indel / frameshift → `indel`; insertion / duplication → `insertion`; deletion → `deletion`; missense / stop gained / stop lost / start lost / synonymous / SNP → `SNV`; expression → `expression`; structural → `structural`.6667About half of CIViC variants carry no SO term (`variantTypes: []`). Documented **name heuristics** then apply: AMPLIFICATION → `amplification`; LOSS/DELETION → `deletion_cna`; EXPRESSION → `expression`; METHYLATION → `epigenetic`; PROMOTER → `promoter_mutation`; FUSION / `::` → `fusion`; SPLICE → `splice`; `Exon N deletion/skipping` → `deletion`; `delins` → `indel`; frameshift (`N150fs`, `E173Rfs*29`) → `indel`; `ins` → `insertion`; `dup` → `insertion`; `del` → `deletion`; single amino-acid substitution or nonsense (`V600E`, `Q1189*`, `p.R1276*`, `Y98N (c.292T>A)`) → `SNV` — the SO class CIViC assigns to such variants (Missense / Stop Gained); DNA-level MNVs cannot be told apart from the protein name, the raw payload keeps `variantTypes: []` for audit; `Mutation` / `Wildtype` / `Variation` → `other`; anything else stays `null`.6869## Observed run (cancerindex_c, 2026-09-08, OncoTree-only ontology: 865 cancers, no NCIt/DOID codes yet)7071| | |72|---|---|73| Duration (full pass, 194 HTTP requests) | 288 s first pass (all records created); 97 s on subsequent passes (records unchanged, edges refreshed in place) |74| Evidence items | 11,968 (4,916 ACCEPTED, 4,794 with a disease resolved to a cancer) |75| Variants | 5,097 (5,071 from the variants phase + profile-only variants); 793 later matched by ClinVar via `clinvarVariationId` |76| Genes touched | 848 (734 CIViC genes + fusion partners), minimal rows created for symbols absent from `genes` |77| Drugs / aliases | 661 / 3,774 |78| Knowledge edges | 8,427 from 3,044 ACCEPTED + SUPPORTS items with a resolved cancer or a drug — PREDICTS_RESPONSE_TO 4,007 (sensitivity 2,550, resistance 1,441, unknown 16), ASSOCIATED_WITH 2,067, INVESTIGATED_FOR 1,866, DIAGNOSTIC_OF 260, PROGNOSTIC_IN 211, PREDISPOSES_TO 16 |79| Publications (stubs) / publication edges | 2,243 / 10,511 |80| Variant types (after SO + name heuristics) | SNV 2,464 · expression 450 · fusion 446 · indel 402 · other 284 · deletion 194 · insertion 121 · splice 95 · deletion_cna 80 · amplification 73 · structural 28 · epigenetic 11 · LOH 5 · promoter 4 · **null 440 (8.6 %)** (e.g. `c.89c297`, unparseable free text) |81| Diseases resolved | **239 / 880 (27 %)** — 113 ONTOLOGY_EXACT + 28 ALIAS distinct DOIDs; 220 `cancer_codes doid` written |82| Top unresolved disease labels | Acute Megakaryocytic Leukemia, EZB-MYC+ Diffuse Large B-cell Lymphoma, Myoepithelial Tumors Of Soft Tissue, Poorly Differentiated Chordoma, Thyroid Cancer, Unclassified Mesenchymal Sarcoma, Undifferentiated Pleomorphic Sarcoma, A53 Diffuse Large B-cell Lymphoma, Acoustic Neuroma, Acral Lentiginous Melanoma, Acute Biphenotypic Leukemia, Acute Leukemia, Acute Lymphoblastic Leukemia, Acute Monocytic Leukemia, Acute Mountain Sickness (non-cancer phenotype used as disease) |8384The low resolution rate is an artefact of the development database: with the NCIt ontology loaded (synonyms + DOID cross-references stored as `cancer_codes`), `byCode('doid')` and `byLabel` will resolve the large majority; CIViC DOIDs are written back as `cancer_codes` so subsequent runs hit `EXACT_IDENTIFIER`. Idempotency: a second full pass reports every source record `unchanged`, updates edges in place (no duplicates) and reuses provenance rows.8586## Limitations / notes8788- No incremental API: each run re-crawls everything (~200 requests, ~5 min). Cursors only make interruptions resumable.89- dbSNP ids exist only as aliases (`RS…`); `myVariantInfo` is not queried.90- Complex molecular profiles (several variants, e.g. "EGFR L858R AND EGFR T790M") produce one edge per variant — the profile itself is kept on the evidence row (`molecularProfileId/Name`).91- ASCO/ASH abstracts have no PMID → no publication stub.92- Evidence items whose disease is a non-cancer phenotype (e.g. "Acute Mountain Sickness") are kept with `cancerId = null`.93- SDK note for the integrator: `RunContext.flushFieldStats` fails (`types` NOT NULL) when a field is null in every observed record of a run **and** already stored with an empty type set — `array_agg` over an empty set yields NULL; wrap it in `COALESCE(…, '{}')`. Not hit by this connector.94