src/scenes/BiochemicalEntityDetails/Gene/MetadataSection.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import BaseMetadataSection from "../MetadataSection";
import { LoadExternalContent, LoadContent } from "../LoadContent";
import { naturalSort, isOrthoDbId, isUniProtId } from "~/utils/utils";
class MetadataSection extends Component {
static propTypes = {
"set-scene-metadata": PropTypes.func.isRequired,
};
static blankResponse = "No description available.";
constructor(props) {
super(props);
this.state = { metadata: null };
}
static processDescriptionFromUniprot(query, data) {
if (data == null || data.length === 0) {
return MetadataSection.blankResponse;
}
for (const datum of data) {
if (datum == null) {
continue;
}
let response = null;
let comments = null;
if (
datum.comments &&
datum.comments.length &&
datum.comments[0] &&
datum.comments[0].text &&
datum.comments[0].text.length
) {
comments = datum.comments[0].text[0].value;
}
if (isOrthoDbId(query)) {
if (comments) {
response = comments;
}
} else {
const properties = [];
// gene
let geneName = null;
let geneUrl = null;
if (datum.gene && datum.gene.length && datum.gene[0]) {
if (datum.gene[0].name && datum.gene[0].name.value) {
geneName = datum.gene[0].name.value;
} else if (datum.gene[0].olnNames && datum.gene[0].olnNames[0]) {
if (datum.gene[0].olnNames[0].value) {
geneName = datum.gene[0].olnNames[0].value;
} else if (
datum.gene[0].olnNames[0].evidences &&
datum.gene[0].olnNames[0].evidences.length
) {
geneName = datum.gene[0].olnNames[0].value;
geneUrl = datum.gene[0].olnNames[0].evidences[0].source.url;
}
}
}
if (geneName != null) {
if (geneUrl != null) {
properties.push({
key: "Gene",
value: (
<a href={geneUrl} target="_blank" rel="noopener noreferrer">
{geneName}
</a>
),
});
} else {
properties.push({
key: "Gene",
value: geneName,
});
}
}
// organism
if (
datum.organism &&
datum.organism.names &&
datum.organism.names.length &&
datum.organism.names[0] &&
datum.organism.names[0].value
) {
properties.push({
key: "Organism",
value: (
<a
href={
"https://www.ncbi.nlm.nih.gov/taxonomy/" +
datum.organism.taxonomy
}
target="_blank"
rel="noopener noreferrer"
>
{datum.organism.names[0].value.split("(")[0].trim()}
</a>
),
});
}
// comments
if (comments) {
properties.push({
key: "Comments",
value: comments,
});
}
// sequence
if (datum.sequence && datum.sequence.sequence) {
const seq = datum.sequence.sequence;
const aaPerLine = 115;
const formattedSeq = [];
for (
let iLine = 0;
iLine < Math.ceil(seq.length / aaPerLine);
iLine++
) {
formattedSeq.push(
<li key={iLine}>
{seq.slice(
iLine * aaPerLine,
Math.min((iLine + 1) * aaPerLine, seq.length)
)}
</li>
);
}
properties.push({
key: <span>Sequence ({seq.length} aa)</span>,
value: <ul className="sequence">{formattedSeq}</ul>,
});
}
if (properties.length) {
response = (
<ul className="key-value-list link-list">
{properties.map((property) => {
return (
<li key={property.key}>
<b>{property.key}</b>: {property.value}
</li>
);
})}
</ul>
);
}
}
if (response != null) {
return response;
}
}
return MetadataSection.blankResponse;
}
static processRelatedReactions(organism, relatedReactions) {
const formattedResults = [];
for (const reaction of relatedReactions) {
const id = reaction.kinlaw_id;
const formattedResult = {};
formattedResults.push(formattedResult);
let substrateIds = null;
let productIds = null;
for (const participant of reaction.reaction_participant) {
if ("substrate_aggregate" in participant) {
substrateIds = participant.substrate_aggregate;
substrateIds.sort();
} else if ("product_aggregate" in participant) {
productIds = participant.product_aggregate;
productIds.sort();
}
}
const substrateNames = reaction.substrates[0].map(
(substrate) => substrate.substrate_name
);
const productNames = reaction.products[0].map(
(product) => product.product_name
);
substrateNames.sort(naturalSort);
productNames.sort(naturalSort);
let equation =
formatSide(substrateNames) + " → " + formatSide(productNames);
let route =
"/reaction/" +
substrateIds.join(",") +
"-->" +
productIds.join(",") +
"/";
if (organism) {
route += organism + "/";
}
let title;
let name;
let ecNumber;
if ("ec_meta" in reaction) {
const ecMeta = reaction["ec_meta"];
title = ecMeta["ec_name"];
name = ecMeta["ec_name"];
ecNumber = ecMeta["ec_number"];
} else {
title = equation;
name = equation;
equation = null;
ecNumber = null;
}
formattedResult["title"] = title;
formattedResult["content"] = (
<div key={id} className="subsection">
<div className="subsection-title">
<Link to={route}>{name}</Link>
</div>
{ecNumber != null && (
<div className="subsection-description">
<div>{equation}</div>
<div>
EC:{" "}
<a
href={"https://enzyme.expasy.org/EC/" + ecNumber}
target="_blank"
rel="noopener noreferrer"
>
{ecNumber}
</a>
</div>
</div>
)}
</div>
);
}
return formattedResults.map((result) => {
return result.content;
});
}
getMetadataUrl(query) {
if (isOrthoDbId(query)) {
return "kegg/get_meta/?kegg_ids=" + query;
} else if (isUniProtId(query)) {
return "proteins/meta/meta_combo/?uniprot_id=" + query;
} else {
return null;
}
}
static processMetadata(query, organism, rawData) {
if (isOrthoDbId(query)) {
return MetadataSection.processOrthoDbGroupMetadata(
query,
organism,
rawData
);
} else {
return MetadataSection.processUniProtProteinMetadata(
query,
organism,
rawData
);
}
}
static processOrthoDbGroupMetadata(query, organism, rawData) {
if (!Array.isArray(rawData)) {
return;
}
let processedData = {};
processedData.title = rawData[0].orthodb_name;
processedData.orthoDbId = query;
processedData.uniprotId = null;
processedData.description = null;
processedData.ecCode = null;
processedData.descriptionUrl =
"https://www.ebi.ac.uk/proteins/api/proteins/OrthoDB:" +
query +
"?offset=0&size=10";
processedData.relatedLinksUrl =
"proteins/related/related_reactions_by_kegg/?ko=" +
processedData.orthoDbId;
return processedData;
}
static processUniProtProteinMetadata(query, organism, rawData) {
if (!Array.isArray(rawData)) {
return;
}
let processedData = {};
processedData.title = rawData[0].protein_name.split("(")[0].trim();
processedData.orthoDbId = null;
processedData.uniprotId = query;
processedData.description = null;
processedData.ecCode = rawData[0].ec_number;
processedData.descriptionUrl =
"https://www.ebi.ac.uk/proteins/api/proteins?offset=0&size=1&accession=" +
query;
processedData.relatedLinksUrl =
"proteins/related/related_reactions_by_uniprot/?uniprot_id=" + query;
return processedData;
}
static formatTitle(processedData) {
return processedData.title;
}
static formatMetadata(query, organism, processedData) {
const sections = [];
sections.push({
id: "id",
title: "Id",
content: query,
});
if (processedData.descriptionUrl) {
sections.push({
id: "description",
title: "Description",
content: (
<div>
<LoadExternalContent
url={processedData.descriptionUrl}
format-results={(data) => {
return MetadataSection.processDescriptionFromUniprot(
query,
data
);
}}
error-message={MetadataSection.blankResponse}
/>
</div>
),
});
}
const crossRefs = [];
if (processedData.orthoDbId != null) {
crossRefs.push({
key: "OrthoDB",
value: (
<a
href={"https://www.orthodb.org/?query=" + processedData.orthoDbId}
target="_blank"
rel="noopener noreferrer"
>
{" "}
{processedData.orthoDbId}
</a>
),
});
} else {
crossRefs.push({
key: "OrthoDB",
value: "None",
});
}
if (processedData.uniprotId != null) {
crossRefs.push({
key: "UniProt",
value: (
<a
href={"https://www.uniprot.org/uniprot/" + processedData.uniprotId}
target="_blank"
rel="noopener noreferrer"
>
{" "}
{processedData.uniprotId}
</a>
),
});
}
sections.push({
id: "cross-refs",
title: "Cross references",
content: (
<ul className="key-value-list link-list">
{crossRefs.map((desc) => {
return (
<li key={desc.key}>
<b>{desc.key}</b>: {desc.value}
</li>
);
})}
</ul>
),
});
if (processedData.relatedLinksUrl != null) {
sections.push({
id: "reactions",
title: "Reactions",
content: (
<div>
<LoadContent
url={processedData.relatedLinksUrl}
format-results={MetadataSection.processRelatedReactions.bind(
null,
organism
)}
/>
</div>
),
});
} else {
sections.push({
id: "reactions",
title: "Reactions",
content: "No data is available.",
});
}
return sections;
}
render() {
return (
<BaseMetadataSection
entity-type="ortholog group"
get-metadata-url={this.getMetadataUrl}
process-metadata={MetadataSection.processMetadata}
format-title={MetadataSection.formatTitle}
format-metadata={MetadataSection.formatMetadata}
set-scene-metadata={this.props["set-scene-metadata"]}
/>
);
}
}
function formatSide(parts) {
return parts.join(" + ");
}
export { MetadataSection };