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

# Connector civic — CIViC (Clinical Interpretation of Variants in Cancer)

Source https://civicdb.org — Washington University School of Medicine (McDonnell Genome Institute)
Tier / category 0 / evidence
Access GraphQL, POST https://civicdb.org/api/graphql, no authentication
Docs verified 2026-09-08 — https://docs.civicdb.org/en/latest/api.html, https://griffithlab.github.io/civic-v2/, live schema introspection
License CC0 1.0 Universal (content), MIT (application code) — licenseStatus: approved
Code packages/connectors/src/connectors/civic/ (manifest.ts, graphql.ts, normalize.ts, genes.ts, index.ts, civic.test.ts, fixtures/)

# License (quoted)

Docs FAQ, "How is CIViC licensed?" (https://docs.civicdb.org/en/latest/about/faq.html):

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.

Citation (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).

# Verified endpoints and fields (introspection 2026-09-08)

Relay 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.

Query Fields used
genes id name fullName entrezId description featureAliases deprecated
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 }
diseases id name displayName doid diseaseUrl diseaseAliases deprecated
therapies(hasLinkedEvidence: true) id name ncitId therapyUrl therapyAliases deprecated
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}

Enums 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.

Health check: { evidenceItems(first:1){ totalCount } }.

# Sync design

Five 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>/).

  1. 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.
  2. 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.
  3. 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}).
  4. therapies (linked to evidence only) → drugs by ncitCode → civicTherapyId → slug; drug_aliases (name as generic, aliases as synonym).
  5. 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).

# Knowledge edges (ACCEPTED and evidenceDirection = SUPPORTS only)

CIViC type Edge(s)
PREDICTIVE variant PREDICTS_RESPONSE_TO drug (direction sensitivity ← SENSITIVITYRESPONSE, resistance ← RESISTANCE / REDUCED_SENSITIVITY, else unknown; predictive = true; cancerContextIds = [cancer]) + drug INVESTIGATED_FOR cancer
PROGNOSTIC variant PROGNOSTIC_IN cancer (prognostic = true)
DIAGNOSTIC (significance POSITIVE) variant DIAGNOSTIC_OF cancer
PREDISPOSING (PREDISPOSITION / PATHOGENIC / LIKELY_PATHOGENIC) variant PREDISPOSES_TO cancer
ONCOGENIC / FUNCTIONAL gene ASSOCIATED_WITH cancer
every item with a cancer gene ASSOCIATED_WITH cancer

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).

# Publications

For 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.

# Variant type mapping (SO term → variant_type)

Evaluated 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.

About 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.

# Observed run (cancerindex_c, 2026-09-08, OncoTree-only ontology: 865 cancers, no NCIt/DOID codes yet)

Duration (full pass, 194 HTTP requests) 288 s first pass (all records created); 97 s on subsequent passes (records unchanged, edges refreshed in place)
Evidence items 11,968 (4,916 ACCEPTED, 4,794 with a disease resolved to a cancer)
Variants 5,097 (5,071 from the variants phase + profile-only variants); 793 later matched by ClinVar via clinvarVariationId
Genes touched 848 (734 CIViC genes + fusion partners), minimal rows created for symbols absent from genes
Drugs / aliases 661 / 3,774
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
Publications (stubs) / publication edges 2,243 / 10,511
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)
Diseases resolved 239 / 880 (27 %) — 113 ONTOLOGY_EXACT + 28 ALIAS distinct DOIDs; 220 cancer_codes doid written
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)

The 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.

# Limitations / notes

  • No incremental API: each run re-crawls everything (~200 requests, ~5 min). Cursors only make interruptions resumable.
  • dbSNP ids exist only as aliases (RS…); myVariantInfo is not queried.
  • 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).
  • ASCO/ASH abstracts have no PMID → no publication stub.
  • Evidence items whose disease is a non-cancer phenotype (e.g. "Acute Mountain Sickness") are kept with cancerId = null.
  • 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.