diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-grid-formatter.js b/src/webcomponents/variant/interpretation/variant-interpreter-grid-formatter.js
index df74b2016..d29fd3201 100644
--- a/src/webcomponents/variant/interpretation/variant-interpreter-grid-formatter.js
+++ b/src/webcomponents/variant/interpretation/variant-interpreter-grid-formatter.js
@@ -1017,36 +1017,38 @@ export default class VariantInterpreterGridFormatter {
`;
}
- static geneFeatureOverlapFormatter(variant, opencgaSession) {
+ static rearrangementFeatureOverlapFormatter(variant, genes, opencgaSession) {
if (variant?.annotation?.consequenceTypes) {
const overlaps = [];
- (variant.annotation.consequenceTypes || []).forEach(ct => {
- if (Array.isArray(ct.exonOverlap) && ct.exonOverlap?.length > 0) {
- ct.exonOverlap.map(exon => {
- overlaps.push({
- geneName: ct.geneName || "",
- transcript: ct.transcript || ct.ensemblTranscriptId || "",
- feature: `exon (${exon.number || "-"})`,
- });
- });
- } else if (Array.isArray(ct.sequenceOntologyTerms) && ct.sequenceOntologyTerms?.length > 0) {
- ct.sequenceOntologyTerms.forEach(term => {
- if (term.name === "intron_variant") {
- overlaps.push({
- geneName: ct.geneName || "",
- transcript: ct.transcript || ct.ensemblTranscriptId || "",
- feature: "intron",
- });
- } else if (term.name === "5_prime_UTR_variant" || term.name === "3_prime_UTR_variant") {
+ (variant.annotation.consequenceTypes || [])
+ .filter(ct => genes.has(ct.geneName || ct.geneId || ""))
+ .forEach(ct => {
+ if (Array.isArray(ct.exonOverlap) && ct.exonOverlap?.length > 0) {
+ ct.exonOverlap.map(exon => {
overlaps.push({
- geneName: ct.geneName || "",
+ geneName: ct.geneName || ct.geneId || "",
transcript: ct.transcript || ct.ensemblTranscriptId || "",
- feature: `${term.name.charAt(0)}'-UTR`,
+ feature: `exon (${exon.number || "-"})`,
});
- }
- });
- }
- });
+ });
+ } else if (Array.isArray(ct.sequenceOntologyTerms) && ct.sequenceOntologyTerms?.length > 0) {
+ ct.sequenceOntologyTerms.forEach(term => {
+ if (term.name === "intron_variant") {
+ overlaps.push({
+ geneName: ct.geneName || ct.geneId || "",
+ transcript: ct.transcript || ct.ensemblTranscriptId || "",
+ feature: "intron",
+ });
+ } else if (term.name === "5_prime_UTR_variant" || term.name === "3_prime_UTR_variant") {
+ overlaps.push({
+ geneName: ct.geneName || ct.geneId || "",
+ transcript: ct.transcript || ct.ensemblTranscriptId || "",
+ feature: `${term.name.charAt(0)}'-UTR`,
+ });
+ }
+ });
+ }
+ });
if (overlaps.length > 0) {
const maxDisplayedOverlaps = 3;
@@ -1092,4 +1094,26 @@ export default class VariantInterpreterGridFormatter {
return "-";
}
+ static rearrangementGeneFormatter(variants, genesByVariant, opencgaSession) {
+ const separator = `
`;
+ return variants
+ .map((variant, index) => {
+ let resultHtml = "-";
+ const genes = Array.from(genesByVariant[variant.id] || []);
+
+ if (genes.length > 0) {
+ const genesLinks = genes.map(gene => {
+ const tooltip = VariantGridFormatter.getGeneTooltip(gene, opencgaSession?.project?.organism?.assembly);
+ return `
+ ${gene}
+ `;
+ });
+ resultHtml = genesLinks.join(" ");
+ }
+
+ return `Variant ${index + 1}: ${resultHtml}
`;
+ })
+ .join(separator);
+ }
+
}
diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-rearrangement-grid.js b/src/webcomponents/variant/interpretation/variant-interpreter-rearrangement-grid.js
index 4b15bc83d..4f5f3e0d8 100644
--- a/src/webcomponents/variant/interpretation/variant-interpreter-rearrangement-grid.js
+++ b/src/webcomponents/variant/interpretation/variant-interpreter-rearrangement-grid.js
@@ -75,6 +75,10 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
this.review = false;
this.variantsReview = null;
+ // OpenCGA returns the same genes in both variants of the rearrangement
+ // This map is used to assign the correct genes to each variant
+ this.genesByVariant = {};
+
// Set colors
// consequenceTypesImpact;
// eslint-disable-next-line no-undef
@@ -204,6 +208,58 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
return pairs;
}
+ generateGenesMapFromVariants(variants, offset = 5000) {
+ this.genesByVariant = {};
+ const genesList = new Set();
+ (variants || []).forEach(variant => {
+ if (variant?.annotation?.consequenceTypes) {
+ variant.annotation.consequenceTypes.forEach(ct => {
+ if (ct.geneName || ct.geneId) {
+ genesList.add(ct.geneName || ct.geneId);
+ }
+ });
+ }
+ });
+
+ // Request gene info to cellbase
+ if (genesList.size > 0) {
+ const genesIds = Array.from(genesList);
+ return this.opencgaSession.cellbaseClient
+ .getGeneClient(genesIds.join(","), "info", {
+ "include": "id,name,chromosome,start,end",
+ })
+ .then(response => {
+ // 1. Map each gene with it's correct position
+ const genes = new Map();
+ genesIds.forEach((geneId, index) => {
+ if (response?.responses?.[index]?.results?.[0]) {
+ genes.set(geneId, response.responses[index].results[0]);
+ }
+ });
+
+ // 2. Assign genes to each variant
+ variants.forEach(variant => {
+ const id = variant.id;
+ this.genesByVariant[id] = new Set();
+ (variant?.annotation?.consequenceTypes || []).forEach(ct => {
+ const gene = genes.get(ct.geneName || ct.geneId);
+
+ // Check if this gene exists and overlaps this variant
+ if (gene) {
+ const start = gene.start - offset;
+ const end = gene.end + offset;
+ const variantStart = Math.min(variant.start, variant.end);
+ const variantEnd = Math.max(variant.start, variant.end);
+ if (variant.chromosome === gene.chromosome && variantStart <= end && start <= variantEnd) {
+ this.genesByVariant[id].add(ct.geneName || ct.geneId);
+ }
+ }
+ });
+ });
+ });
+ }
+ }
+
renderVariants() {
if (this.clinicalVariants && this.clinicalVariants.length > 0) {
this.renderLocalVariants();
@@ -250,6 +306,8 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
variantGrid: this,
ajax: params => {
+ let rearrangementResponse = null;
+
// Make a deep clone object to manipulate the query sent to OpenCGA
const internalQuery = JSON.parse(JSON.stringify(this.query));
@@ -272,15 +330,24 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
this.opencgaSession.opencgaClient.clinical().queryVariant(filters)
.then(res => {
this.isApproximateCount = res.responses[0].attributes?.approximateCount ?? false;
+ rearrangementResponse = res;
+ // Generate map of genes to variants
+ return this.generateGenesMapFromVariants(res.responses[0].results);
+ })
+ .then(() => {
// pairs will have the following format: [[v1, v2], [v3, v4], [v5, v6]];
- const pairs = this.generateRowsFromVariants(res.responses[0].results);
+ const results = rearrangementResponse.responses[0].results;
+ const pairs = this.generateRowsFromVariants(results);
// It's important to overwrite results array
- res.responses[0].results = pairs;
+ rearrangementResponse.responses[0].results = pairs;
- params.success(res);
+ params.success(rearrangementResponse);
+ })
+ .catch(error => {
+ console.error(error);
+ params.error(error);
})
- .catch(e => params.error(e))
.finally(() => {
LitUtils.dispatchCustomEvent(this, "queryComplete", null);
});
@@ -309,9 +376,29 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
this.table = $("#" + this.gridId);
this.table.bootstrapTable("destroy");
this.table.bootstrapTable({
- data: variants,
columns: this._getDefaultColumns(),
- sidePagination: "local",
+ sidePagination: "server",
+ // Josemi Note 2024-01-31: we have added the ajax function for local variants for getting genes info
+ // and map the genes to each variant
+ ajax: params => {
+ const tableOptions = $(this.table).bootstrapTable("getOptions");
+ const limit = params.data.limit || tableOptions.pageSize;
+ const skip = params.data.offset || 0;
+ const rows = variants.slice(skip, skip + limit);
+
+ // Generate map of genes to variants
+ this.generateGenesMapFromVariants(rows)
+ .then(() => params.success(rows))
+ .catch(error => params.error(error));
+ },
+ // Josemi Note 2024-01-31: we use this method to tell bootstrap-table how many rows we have in our data
+ responseHandler: response => {
+ return {
+ total: variants.length,
+ rows: response,
+ };
+ },
+
iconsPrefix: GridCommons.GRID_ICONS_PREFIX,
icons: GridCommons.GRID_ICONS,
@@ -509,7 +596,9 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
field: "gene",
rowspan: 2,
colspan: 1,
- formatter: (value, row, index) => VariantGridFormatter.geneFormatter(row[0], index, this.query, this.opencgaSession),
+ formatter: (value, row) => {
+ return VariantInterpreterGridFormatter.rearrangementGeneFormatter(row, this.genesByVariant, this.opencgaSession);
+ },
halign: "center",
visible: this.gridCommons.isColumnVisible("gene"),
},
@@ -607,7 +696,7 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
colspan: 1,
rowspan: 1,
formatter: (value, rows) => {
- return VariantInterpreterGridFormatter.geneFeatureOverlapFormatter(rows[0], this.opencgaSession);
+ return VariantInterpreterGridFormatter.rearrangementFeatureOverlapFormatter(rows[0], this.genesByVariant[rows[0].id], this.opencgaSession);
},
halign: "center",
valign: "top",
@@ -619,7 +708,7 @@ export default class VariantInterpreterRearrangementGrid extends LitElement {
colspan: 1,
rowspan: 1,
formatter: (value, rows) => {
- return VariantInterpreterGridFormatter.geneFeatureOverlapFormatter(rows[1], this.opencgaSession);
+ return VariantInterpreterGridFormatter.rearrangementFeatureOverlapFormatter(rows[1], this.genesByVariant[rows[1].id], this.opencgaSession);
},
halign: "center",
valign: "top",