Skip to content

Commit a3f40de

Browse files
committed
adding variant documentation
1 parent b130c66 commit a3f40de

File tree

6 files changed

+141
-22
lines changed

6 files changed

+141
-22
lines changed

book/src/help/variant-data.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Variant Data
2+
3+
Phenoboard uses the [VariantValidator](https://variantvalidator.org/) API to validate HGVS expressions and ensure that variant descriptions are syntactically correct and consistent with reference sequences.
4+
5+
---
6+
7+
## Gene Information
8+
9+
To describe a variant unambiguously, Phenoboard requires the following:
10+
11+
- **Gene symbol**
12+
- **HGNC identifier**
13+
- **Reference transcript**
14+
15+
Providing all three ensures that variants are interpreted consistently across databases, publications, and analysis tools.
16+
17+
---
18+
19+
## HGNC Identifier
20+
21+
The **HUGO Gene Nomenclature Committee (HGNC)** provides standardized names for human genes.
22+
23+
Each gene has:
24+
25+
- An **approved gene symbol** (e.g., *MED16*)
26+
- A unique **HGNC identifier** (e.g., **HGNC:17556**)
27+
28+
Using the HGNC identifier avoids ambiguity because gene symbols can occasionally change or be reused in different contexts.
29+
30+
Example:
31+
32+
> The gene encoding mediator complex subunit 16 has
33+
> **Symbol:** *MED16*
34+
> **HGNC ID:** HGNC:17556
35+
36+
You can search for gene information at the HGNC website:
37+
https://www.genenames.org/
38+
39+
---
40+
41+
## Transcript Information
42+
43+
Most human genes produce multiple transcripts (isoforms) due to **alternative splicing**. As a result, the same genomic variant can have **different coordinates and protein consequences** depending on the transcript used.
44+
45+
For example, the genomic variant:
46+
47+
- NC_000013.11:g.32363225A>G
48+
49+
occurs in the **BRCA2** gene but maps differently across transcripts:
50+
51+
- NM_000059.4:c.8023A>G → NP_000050.3:p.(Ile2675Val)
52+
- NM_001406719.1:c.7927A>G → NP_001393648.1:p.(Ile2643Val)
53+
- NM_001406722.1:c.1606A>G → NP_001393651.1:p.(Ile536Val)
54+
55+
One transcript is non-coding:
56+
57+
- NR_176251.1:n.8222A>G
58+
59+
This example illustrates an important principle:
60+
61+
!!! important "Why transcripts matter"
62+
A variant does not have a single consequence — its interpretation depends on the reference transcript.
63+
64+
Because of this, publications and clinical reports **must always specify both**:
65+
66+
1. The variant description
67+
2. The transcript accession (e.g., NM_000059.4)
68+
69+
Older literature sometimes omits transcript information. In such cases, determining the correct transcript may require careful investigation and is sometimes impossible. When uncertainty remains, we recommend contacting the authors or excluding the publication from curation.
70+
71+
---
72+
73+
## MANE Select Transcripts
74+
75+
A **MANE Select transcript** is a standardized reference transcript chosen to promote consistent variant reporting.
76+
77+
MANE (Matched Annotation from NCBI and EMBL-EBI) transcripts have:
78+
79+
- Identical exon structure between **RefSeq** and **Ensembl/GENCODE**
80+
- High biological and clinical relevance
81+
- Broad community acceptance
82+
83+
There is typically **one MANE Select transcript per protein-coding gene**.
84+
85+
!!! tip "Recommendation"
86+
We strongly recommend using the **RefSeq MANE Select transcript** whenever available.
87+
88+
The Human Phenotype Ontology (HPO) project also curates variants using RefSeq MANE transcripts (for example: NM_130837.3).
89+
90+
---
91+
92+
## Entering Data in Phenoboard
93+
94+
Phenoboard provides tools to simplify data entry.
95+
96+
### Automatic retrieval
97+
98+
Use the **Fetch HGNC/Transcript Info** button to automatically retrieve:
99+
100+
- HGNC identifier
101+
- RefSeq MANE Select transcript
102+
103+
from the HGNC database.
104+
105+
### Manual lookup
106+
107+
Alternatively, you can search manually:
108+
109+
HGNC website: https://www.genenames.org/
110+
111+
---
112+
113+
## Summary
114+
115+
To ensure accurate variant interpretation:
116+
117+
- Always provide the **gene symbol**
118+
- Always include the **HGNC identifier**
119+
- Always specify the **reference transcript**
120+
- Prefer the **MANE Select transcript** when available

src/app/cohortdialog/cohortdialog.component.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ <h3>Gene Info</h3>
4646
<app-help-button
4747
title="Entering gene data"
4848
[lines]="[
49-
'Go to the HGNC website (https://www.genenames.org/) to find the HGNC identifier and the MANE transcript for the gene.',
50-
'Phenoboard expects to use the RefSeq MANE select transcript identifier.',
51-
'For instance, for the gene <i>OPA1</i>, choose the HGNC identifier <b>HGNC:8140</b> and the transcript identifier <b>NM_130837.3</b>.'
52-
]" />
49+
'Enter the HGNC identifier and the MANE transcript for the gene.',
50+
'The <b>Fetch HGNC/Transcript Info</b> button will get this information from the HGNC resource'
51+
]"
52+
helpUrl="https://p2gx.github.io/phenoboard/help/variant-data.html#gene-data" />
5353
</div>
5454

5555

src/app/newtemplate/newtemplate.component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ async mendelian(): Promise<void> {
166166
if (rawValue) {
167167
const entryA: CohortEntry = {
168168
...rawValue,
169-
geneTranscriptList: [] // Ensure the optional list is initialized if needed
169+
geneTranscriptList: []
170170
};
171171
this.diseaseA.set(entryA);
172172
} else {
173-
this.notificationService.showError("Could not retrieve disease A");
173+
this.notificationService.showError("Could not retrieve disease");
174174
return;
175175
}
176176

@@ -193,7 +193,7 @@ async mendelian(): Promise<void> {
193193
const acronymB = diseaseB.cohortAcronym;
194194
const geneA = diseaseA.symbol;
195195
const geneB = diseaseB.symbol;
196-
const acronym = `${geneA}-${acronymA}-${geneB}-${acronymB}`;
196+
const acronym = `${geneA}_${acronymA}_${geneB}_${acronymB}`;
197197
const cohort = await this.configService.createNewMeldedTemplate(toDiseaseData(diseaseA), toDiseaseData(diseaseB), acronym);
198198
this.resetCohort();
199199
this.pendingCohort.set(cohort);
@@ -209,7 +209,7 @@ private async createMendelianTemplate(): Promise<void> {
209209
}
210210
const acronym = diseaseA.cohortAcronym;
211211
const gene = diseaseA.symbol;
212-
const fullAcronym = `${gene}-${acronym}`;
212+
const fullAcronym = `${gene}_${acronym}`;
213213
const ctype: CohortType = "mendelian";
214214
const template = await this.configService.createNewTemplate(
215215
toDiseaseData(diseaseA),

src/app/services/config.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ export class ConfigService {
378378
return await invoke<{ hgncId: string, maneSelect: string}>('fetch_hgnc_data', {symbol: symbol});
379379
}
380380

381-
/**
381+
/**
382382
* Adjusts x and y coordinates to ensure a menu stays within the viewport.
383383
*/
384384
calculateMenuPosition(

src/app/services/etl_session_service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ export class EtlSessionService {
127127
const value = val.toLowerCase().trim();
128128
const femaleSymbols = new Set(["female", "woman","f", "w", "girl", "fem"]);
129129
const maleSymbols = new Set(["male","man","m","boy", "masc"]);
130-
const unknownSymbols = new Set(["u", "ukn", "unknown", "?"]);
130+
const unknownSymbols = new Set(["u", "ukn", "unknown", "?", "n.k."]);
131131
const otherSymbols = new Set(["o", "other"]);
132132
if (femaleSymbols.has(value)) {
133133
return "F";

src/app/tableeditor/tableeditor.component.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -727,18 +727,17 @@ export class TableEditorComponent implements OnInit {
727727
const dto = this.etl_service.etlDto();
728728
if (! dto) return;
729729
const rect = this.contextMenuAnchor.getBoundingClientRect();
730-
const modalHeight = 300;
731-
const margin = 8;
732-
733-
let top = rect.top + window.scrollY;
734-
let left = rect.left + window.scrollX;
735-
// Flip above if needed
736-
if (top + modalHeight > window.scrollY + window.innerHeight) {
737-
top = rect.bottom + window.scrollY - modalHeight - margin;
738-
}
739-
740-
this.editModalPosition = { x:left, y: top };
741-
this.editModalVisible = true;
730+
const safePos = this.configService.calculateMenuPosition(
731+
rect.left,
732+
rect.top,
733+
250, // Estimated Modal Width
734+
300 // Estimated Modal Height
735+
);
736+
this.editModalPosition = {
737+
x: safePos.x + window.scrollX,
738+
y: safePos.y + window.scrollY
739+
};
740+
this.editModalVisible = true;
742741

743742
const cell = this.contextMenuCellValue;
744743
this.editingValue = cell;

0 commit comments

Comments
 (0)