diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..0f61a1a1b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +npm-debug.log +.git +dist +package-lock.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..6e87a003d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..1490f5497 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +ARG DOCKER_PULL_TAG=latest +ARG REG=100225593120.dkr.ecr.us-east-1.amazonaws.com +FROM ${REG}/agr_base_linux_env:${DOCKER_PULL_TAG} as build-stage + +WORKDIR /workdir/agr_ui + +ADD . . + +RUN mkdir /workdir/agr_ui/build + +ARG NODE_ENV=production +ENV NODE_ENV ${NODE_ENV} + +RUN /bin/bash -c 'PS1=x && . /root/.profile && nvm install && nvm use && npm install --legacy-peer-deps && npm run build' + +FROM nginx + +WORKDIR /workdir/agr_ui/build + +COPY --from=build-stage /workdir/agr_ui/build /workdir/agr_ui/build +COPY --from=build-stage /workdir/agr_ui/nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 3000 diff --git a/Makefile b/Makefile index 78bbee9d1..17d266ec4 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,15 @@ test-alb-deploy: prod-alb-deploy: npx aws-cdk deploy prod-alb-stack +stage-ui-deploy: + npx aws-cdk deploy agr-ui-stage + +test-ui-deploy: + npx aws-cdk deploy agr-ui-test + +prod-ui-deploy: + npx aws-cdk deploy agr-ui-production + uirun: npm start diff --git a/cdk/amplify-production-stack.ts b/cdk/amplify-production-stack.ts index cc1487575..88fd3b705 100644 --- a/cdk/amplify-production-stack.ts +++ b/cdk/amplify-production-stack.ts @@ -21,7 +21,14 @@ export class AmplifyProductionStack extends cdk.Stack { { source: 'https://alliancegenome.org', target: 'https://www.alliancegenome.org', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, { source: '/api/<*>', target: 'https://prod-alb.alliancegenome.org/api/<*>', status: amplify.RedirectStatus.REWRITE }, - + + { source: '/jbrowse2', target: 'https://www.alliancegenome.org/jbrowse2/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, + { source: '/jbrowse2/', target: 'https://main.dgaayxgqoarxf.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowse2/<*>', target: 'https://main.dgaayxgqoarxf.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXTJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xt9_1/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXLJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xl9_2/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseData/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/RGDJBrowse/<*>', target: 'https://rgd.mcw.edu/jbrowse2/<*>', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein', target: 'https://www.alliancegenome.org/jbrowse/worms/protein/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, { source: '/jbrowse/worms/protein/', target: 'https://main.djgvd7iswt7yy.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein/<*>', target: 'https://main.djgvd7iswt7yy.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, diff --git a/cdk/amplify-stage-stack.ts b/cdk/amplify-stage-stack.ts index 8c157aac7..5b329b4ad 100644 --- a/cdk/amplify-stage-stack.ts +++ b/cdk/amplify-stage-stack.ts @@ -17,6 +17,13 @@ export class AmplifyStageStack extends cdk.Stack { const stage_paths = [ { source: '/api/<*>', target: 'https://stage-api.alliancegenome.org/api/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowse2', target: 'https://stage.alliancegenome.org/jbrowse2/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, + { source: '/jbrowse2/', target: 'https://stage.dgaayxgqoarxf.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowse2/<*>', target: 'https://stage.dgaayxgqoarxf.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXTJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xt9_1/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXLJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xl9_2/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseData/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/RGDJBrowse/<*>', target: 'https://rgd.mcw.edu/jbrowse2/<*>', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein', target: 'https://stage.alliancegenome.org/jbrowse/worms/protein/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, { source: '/jbrowse/worms/protein/', target: 'https://staging.djgvd7iswt7yy.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein/<*>', target: 'https://staging.djgvd7iswt7yy.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, diff --git a/cdk/amplify-test-stack.ts b/cdk/amplify-test-stack.ts index 28675c139..7b02d2f41 100644 --- a/cdk/amplify-test-stack.ts +++ b/cdk/amplify-test-stack.ts @@ -18,6 +18,13 @@ export class AmplifyTestStack extends cdk.Stack { const test_paths = [ { source: '/api/<*>', target: 'https://test-alb.alliancegenome.org/api/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowse2', target: 'https://test.alliancegenome.org/jbrowse2/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, + { source: '/jbrowse2/', target: 'https://stage.dgaayxgqoarxf.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowse2/<*>', target: 'https://stage.dgaayxgqoarxf.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXTJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xt9_1/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseXLJBrowse/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/data/xl9_2/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/XenBaseData/<*>', target: 'https://jbrowse.xenbase.org/XenJBrowse/<*>', status: amplify.RedirectStatus.REWRITE }, + { source: '/jbrowsedata/RGDJBrowse/<*>', target: 'https://rgd.mcw.edu/jbrowse2/<*>', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein', target: 'https://test.alliancegenome.org/jbrowse/worms/protein/', status: amplify.RedirectStatus.PERMANENT_REDIRECT }, { source: '/jbrowse/worms/protein/', target: 'https://main.djgvd7iswt7yy.amplifyapp.com/', status: amplify.RedirectStatus.REWRITE }, { source: '/jbrowse/worms/protein/<*>', target: 'https://main.djgvd7iswt7yy.amplifyapp.com/<*>', status: amplify.RedirectStatus.REWRITE }, diff --git a/cdk/cdk-app.ts b/cdk/cdk-app.ts index ecc6b5326..75d33045f 100644 --- a/cdk/cdk-app.ts +++ b/cdk/cdk-app.ts @@ -20,7 +20,7 @@ new AmplifyALBStack(app, 'stage-alb-stack', { new AmplifyALBStack(app, 'test-alb-stack', { stackName: 'test-alb-stack', dnsName: 'test', - targetInstanceId: 'i-003fafec7d050fd97', + targetInstanceId: 'i-0ccfdcd8c43104be5', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, @@ -30,7 +30,7 @@ new AmplifyALBStack(app, 'test-alb-stack', { new AmplifyALBStack(app, 'prod-alb-stack', { stackName: 'prod-alb-stack', dnsName: 'prod', - targetInstanceId: 'i-003fafec7d050fd97', + targetInstanceId: 'i-0ccfdcd8c43104be5', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 000000000..a4a9fe2de --- /dev/null +++ b/nginx.conf @@ -0,0 +1,7 @@ +server { + listen 3000; + root /workdir/agr_ui/build; + index index.html; + + error_page 404 =200 /index.html; +} diff --git a/package.json b/package.json index 9233b2bc0..d75a28090 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/react-fontawesome": "^0.2.0", "@geneontology/curie-util-es5": "^1.2.4", - "@geneontology/wc-gocam-viz": "0.0.51", + "@geneontology/wc-gocam-viz": "1.0.0", "@geneontology/wc-ribbon-strips": "0.0.37", "@geneontology/wc-ribbon-table": "0.0.57", "@testing-library/jest-dom": "^5.16.1", @@ -28,7 +28,7 @@ "custom-event-polyfill": "^1.0.6", "d3-selection": "2.0.0", "document-register-element": "1.13.1", - "generic-sequence-panel": "^1.4.0", + "generic-sequence-panel": "^1.4.1", "html-react-parser": "^0.10.0", "immutable": "^3.8.1", "lodash.clone": "^4.5.0", diff --git a/src/components/dataTable/AnnotatedEntitiesPopupCuration.js b/src/components/dataTable/AnnotatedEntitiesPopupCuration.js index f09ca7fe1..3558ccf9c 100644 --- a/src/components/dataTable/AnnotatedEntitiesPopupCuration.js +++ b/src/components/dataTable/AnnotatedEntitiesPopupCuration.js @@ -22,28 +22,30 @@ import AnnotationType from './AnnotationType'; import AssociationCellCuration from './AssociationCellCuration'; import AssertedGenes from './AssertedGenes'; import GeneticModifiersCellCuration from './GeneticModifiersCellCuration'; -import { buildProviderWithUrl } from './utils'; +import { buildProviderWithUrl, getIdentifier } from './utils'; import StrainBackground from './StrainBackground'; function renderLink(entity) { - const url = getResourceUrl(entity.subject.curie, entity.subject.type, entity.subject.subtype) + const curie = getIdentifier(entity.diseaseAnnotationSubject); + const url = getResourceUrl(curie, entity.diseaseAnnotationSubject.type, entity.diseaseAnnotationSubject.subtype) if (entity.type === 'AlleleDiseaseAnnotation') { - const innerText = entity.subject.alleleSymbol ? entity.subject.alleleSymbol.displayText : entity.subject.name; + const innerText = entity.diseaseAnnotationSubject.alleleSymbol ? entity.diseaseAnnotationSubject.alleleSymbol.displayText : entity.diseaseAnnotationSubject.name; const inner = ; - return {inner}; + return {inner}; } else if(entity.type === 'GeneDiseaseAnnotation'){ - const innerText = entity.subject.geneSymbol ? entity.subject.geneSymbol.displayText : entity.subject.name; + const innerText = entity.diseaseAnnotationSubject.geneSymbol ? entity.diseaseAnnotationSubject.geneSymbol.displayText : entity.diseaseAnnotationSubject.name; const inner = ; - return {inner}; + return {inner}; } else { - const inner = ; - return {inner}; + const inner = ; + return {inner}; } } -function AnnotatedEntitiesPopupCuration(props) { - const {children, entities, parentPage, mainRowCurie } = props; + + +function AnnotatedEntitiesPopupCuration({ children, entities, parentPage, mainRowCurie, pubModIds }) { if (!entities || !entities.length) { return null; @@ -68,11 +70,11 @@ function AnnotatedEntitiesPopupCuration(props) { Name Type Association - { parentPage === 'gene' ? Additional implicated genes : <> } + { parentPage === 'gene' || parentPage === 'disease' ? Additional implicated genes : <> } Experimental condition Genetic Modifiers - { parentPage === 'gene' ? Strain Background : <> } + { parentPage === 'gene' || parentPage === 'disease' ? Strain Background : <> } Genetic Sex Notes Annotation type @@ -88,19 +90,19 @@ function AnnotatedEntitiesPopupCuration(props) { return ( {renderLink(entity)} - + - { parentPage === 'gene' ? : <>} + { parentPage === 'gene' || parentPage === 'disease' ? : <>} - { parentPage === 'gene' ? : <> } + { parentPage === 'gene' || parentPage === 'disease' ? : <> } - + ) }) diff --git a/src/components/dataTable/AssertedGenes.js b/src/components/dataTable/AssertedGenes.js index f287f1524..d403449ee 100644 --- a/src/components/dataTable/AssertedGenes.js +++ b/src/components/dataTable/AssertedGenes.js @@ -1,21 +1,22 @@ import CollapsibleList from '../collapsibleList/collapsibleList'; -import ExternalLink from '../ExternalLink'; +import { Link } from 'react-router-dom'; +import { getIdentifier } from './utils'; function makeAssertedGeneLink(curie, geneSymbol) { if(curie) { const symbol = ; - return {symbol}; + return {symbol}; } return null; } function AssertedGenes({assertedGenes, mainRowCurie}) { - const filteredAssertedGenes = assertedGenes?.filter(gene => gene.curie !== mainRowCurie); + const filteredAssertedGenes = assertedGenes?.filter(gene => getIdentifier(gene) !== mainRowCurie); if(assertedGenes && assertedGenes.length > 1) { return ( - {filteredAssertedGenes.map(gene => makeAssertedGeneLink(gene.curie, gene.geneSymbol.displayText))} + {filteredAssertedGenes.map(gene => makeAssertedGeneLink(getIdentifier(gene), gene.geneSymbol.displayText))} ); } diff --git a/src/components/dataTable/DataTable.js b/src/components/dataTable/DataTable.js index 645a66c36..83579f99b 100644 --- a/src/components/dataTable/DataTable.js +++ b/src/components/dataTable/DataTable.js @@ -25,6 +25,8 @@ import HorizontalScroll from '../horizontalScroll'; import { buildTableQueryString } from '../../lib/utils'; import LoadingSpinner from '../loadingSpinner'; import DropdownNoDataFilter from './DropdownNoDataFilter'; +import {DOWNLOAD_BUTTON_THRESHOLD} from '../../constants'; +import { Link } from 'react-router-dom'; const DataTable = ({ className, @@ -120,6 +122,8 @@ const DataTable = ({ onSizePerPageChange: scrollIntoView }); + let disabled = paginationObj.options?.totalSize > DOWNLOAD_BUTTON_THRESHOLD; + columns.forEach(column => { const filterField = column.filterName || column.dataField; const columnFilter = filters && @@ -221,10 +225,21 @@ const DataTable = ({ ) } - {downloadUrl && - + { + downloadUrl && + + } + { + disabled && +
+ The table above cannot be downloaded because there are too many rows in the unfiltered table. + Please apply filter(s) to limit the number of rows to less than {DOWNLOAD_BUTTON_THRESHOLD} to enable the Download button or visit our + Downloads page + to download the entire data set. +
} ); diff --git a/src/components/dataTable/DiseaseQualifiersColumn.js b/src/components/dataTable/DiseaseQualifiersColumn.js index 7703a5886..33c7856ba 100644 --- a/src/components/dataTable/DiseaseQualifiersColumn.js +++ b/src/components/dataTable/DiseaseQualifiersColumn.js @@ -7,10 +7,10 @@ const DiseaseQualifiersColumn = ({qualifiers}) => { if (!qualifiers || !qualifiers.length) { return null; } - + return ( - {qualifiers.map(qualifier => qualifier)} + {qualifiers.map(qualifier => qualifier.replaceAll("_", " "))} ); }; diff --git a/src/components/dataTable/GeneticModifiersCellCuration.js b/src/components/dataTable/GeneticModifiersCellCuration.js index fc969aa93..80923132b 100644 --- a/src/components/dataTable/GeneticModifiersCellCuration.js +++ b/src/components/dataTable/GeneticModifiersCellCuration.js @@ -3,14 +3,16 @@ import { CollapsibleList } from '../collapsibleList'; import ExternalLink from '../ExternalLink'; import { getResourceUrl } from './getResourceUrl'; import { Link } from 'react-router-dom'; +import { getIdentifier } from './utils'; function GeneticModifierLink(modifier) { + const identifier = getIdentifier(modifier); switch(modifier?.type) { case 'Gene': if (modifier.geneSymbol) { return ( - + ); } @@ -18,13 +20,13 @@ function GeneticModifierLink(modifier) { case 'Allele': if (modifier.alleleSymbol) { return ( - + ); } break; case 'AffectedGenomicModel': - let url = getResourceUrl(modifier.curie, modifier.type, modifier.subtype); + let url = getResourceUrl(identifier, modifier.type, modifier.subtype); if (url && modifier.name) { return ( diff --git a/src/components/dataTable/ReferencesCellViaOrthologyCuration.js b/src/components/dataTable/ReferencesCellViaOrthologyCuration.js new file mode 100644 index 000000000..0ff4808e8 --- /dev/null +++ b/src/components/dataTable/ReferencesCellViaOrthologyCuration.js @@ -0,0 +1,13 @@ +import React from 'react'; + +import ExternalLink from '../ExternalLink'; + +const ReferenceCellViaOrthologyCuration = () => { + const curie = "MGI:6194238"; + const url = `http://www.informatics.jax.org/accession/${curie}`; + return ( + {curie} + ); +}; + +export default ReferenceCellViaOrthologyCuration; diff --git a/src/components/dataTable/StrainBackground.js b/src/components/dataTable/StrainBackground.js index f090b10cd..9854832b7 100644 --- a/src/components/dataTable/StrainBackground.js +++ b/src/components/dataTable/StrainBackground.js @@ -1,13 +1,16 @@ import ExternalLink from '../ExternalLink'; +import { getIdentifier } from './utils'; function StrainBackground({strainBackground}) { - if(strainBackground?.curie && strainBackground?.name) { - const strainName = ; - const strain = strainBackground.curie.slice('SGD:'.length); - return {strainName}; - } - return <>; + const indentifier = getIdentifier(strainBackground); + + if(!indentifier || !strainBackground?.name) return null; + + const strainName = ; + const strain = indentifier.slice('SGD:'.length); + + return {strainName}; } export default StrainBackground; diff --git a/src/components/dataTable/basedOnGeneCellCuration.js b/src/components/dataTable/basedOnGeneCellCuration.js index 7b5314997..96b33ff04 100644 --- a/src/components/dataTable/basedOnGeneCellCuration.js +++ b/src/components/dataTable/basedOnGeneCellCuration.js @@ -1,21 +1,22 @@ import { Link } from 'react-router-dom'; import { shortSpeciesName } from '../../lib/utils'; import { CollapsibleList } from '../../components/collapsibleList'; -import { removeDuplicates } from './utils'; +import { getIdentifier, removeDuplicates } from './utils'; import GeneSymbolCuration from '../GeneSymbolCuration'; +const GeneLink = ({ gene }) => { + const identifier = getIdentifier(gene); + return ( + + ({shortSpeciesName(gene.taxon.curie)}) + ); +}; const BasedOnGeneCellCuration = (genes) => { - if (!genes) { - return null; - } + if (!genes) return null; const uniqueGenes = removeDuplicates(genes, (gene) => gene.geneSymbol.displayText); return ( - {uniqueGenes.map(gene => ( - - ({shortSpeciesName(gene.taxon.curie)}) - - ))} + {uniqueGenes.map(gene => )} ); }; diff --git a/src/components/dataTable/downloadButton.js b/src/components/dataTable/downloadButton.js index 8071b59de..562562fa1 100644 --- a/src/components/dataTable/downloadButton.js +++ b/src/components/dataTable/downloadButton.js @@ -1,11 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -const DownloadButton = ({downloadUrl, text}) => { +const DownloadButton = ({downloadUrl, text, disabled = false}) => { return( diff --git a/src/components/dataTable/referencesCellCuration.js b/src/components/dataTable/referencesCellCuration.js index d1b918615..ad39d25bc 100644 --- a/src/components/dataTable/referencesCellCuration.js +++ b/src/components/dataTable/referencesCellCuration.js @@ -2,26 +2,26 @@ import React from 'react'; import ExternalLink from '../ExternalLink'; import { CollapsibleList } from '../collapsibleList'; -import { getMultipleReferencesCuriesAndUrls } from "./utils"; +import { getMultipleReferencesUrls } from "./utils"; const removeDuplicates = (refs) => { - const newArray = refs.map((ref) => [ref.curie, ref]); + const newArray = refs.map((ref) => [ref.pubModId, ref]); const newMap = new Map(newArray); const iterator = newMap.values(); const uniqueRefs = [...iterator]; return uniqueRefs; -} +}; -const ReferencesCellCuration = (refs) => { - const refStringsAndUrls = getMultipleReferencesCuriesAndUrls(refs); - const uniqueRefs = removeDuplicates(refStringsAndUrls); +const ReferencesCellCuration = ({ pubModIds }) => { + const refStringsAndUrls = getMultipleReferencesUrls(pubModIds); + const uniqueRefs = removeDuplicates(refStringsAndUrls); - return refs && + return pubModIds && { - uniqueRefs.map(({ curie, url }) => { - return {curie}; + uniqueRefs.map(({ pubModId, url }) => { + return {pubModId}; }) } ; diff --git a/src/components/dataTable/singleReferenceCellCuration.js b/src/components/dataTable/singleReferenceCellCuration.js index 4dd47e21c..84fe2f7f0 100644 --- a/src/components/dataTable/singleReferenceCellCuration.js +++ b/src/components/dataTable/singleReferenceCellCuration.js @@ -1,14 +1,19 @@ import React from 'react'; import ExternalLink from '../ExternalLink'; -import { getSingleReferenceCurieAndUrl } from "./utils"; - -const SingleReferenceCellCuration = ({singleReference}) => { - if (singleReference) { - const { curie, url } = getSingleReferenceCurieAndUrl(singleReference); - return {curie}; - } - return <>; +import { getSingleReferenceUrl } from "./utils"; + +const SingleReferenceCellCuration = ({ singleReference, pubModIds }) => { + if (!singleReference) return <>; + const pubModId = getSingleReferencePubModId(singleReference, pubModIds); + const { url } = getSingleReferenceUrl(pubModId); + return {pubModId}; +}; + +const getSingleReferencePubModId = (reference, pubModIds) => { + return reference.crossReferences + .filter(crossRef => pubModIds.includes(crossRef.referencedCurie)) + .map(crossRef => crossRef.referencedCurie)[0]; }; diff --git a/src/components/dataTable/utils.js b/src/components/dataTable/utils.js index 884066775..23ddaab65 100644 --- a/src/components/dataTable/utils.js +++ b/src/components/dataTable/utils.js @@ -18,65 +18,22 @@ export const getDistinctFieldValue = (response, field) => { )); }; -export function getRefStrings(referenceItems) { - if (!referenceItems) - return; - - let refStrings = referenceItems.map((referenceItem) => getRefString(referenceItem)); - - return refStrings.sort(); +export const getIdentifier = (subject) => { + if(!subject) return; + return subject.curie ? subject.curie : (subject.modEntityId ? subject.modEntityId : subject.modInternalId); } -export function getRefString(referenceItem) { - if (!referenceItem) - return; - - if (!referenceItem.cross_references && !referenceItem.crossReferences){ - return referenceItem.curie - } - let xrefCuries = referenceItem.crossReferences.map((crossReference) => crossReference.referencedCurie); - let primaryXrefCurie = ''; - - if (indexWithPrefix(xrefCuries, 'PMID:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'PMID:'), 1); - } else if (indexWithPrefix(xrefCuries, 'FB:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'FB:'), 1); - } else if (indexWithPrefix(xrefCuries, 'MGI:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'MGI:'), 1); - } else if (indexWithPrefix(xrefCuries, 'RGD:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'RGD:'), 1); - } else if (indexWithPrefix(xrefCuries, 'SGD:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'SGD:'), 1); - } else if (indexWithPrefix(xrefCuries, 'WB:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'WB:'), 1); - } else if (indexWithPrefix(xrefCuries, 'ZFIN:') > -1) { - [primaryXrefCurie] = xrefCuries.splice(indexWithPrefix(xrefCuries, 'ZFIN:'), 1); - } else { - [primaryXrefCurie] = xrefCuries.splice(0, 1); - } - - return primaryXrefCurie; -} - - -function indexWithPrefix(array, prefix) { - - for (let i = 0; i < array.length; i++) { - if (array[i].startsWith(prefix)) { - return i; - } - } - return -1; -} +export const getIsViaOrthology = (annotation) => { + return annotation.generatedRelationString.includes("orthology"); +}; -export const getSingleReferenceCurieAndUrl = (reference) => { - const curie = getRefString(reference); - const url = getResourceUrl(curie); - return {curie, url}; +export const getSingleReferenceUrl = (pubModId) => { + const url = getResourceUrl(pubModId); + return {pubModId, url}; } -export const getMultipleReferencesCuriesAndUrls = (references) => { - return references.map((reference) => getSingleReferenceCurieAndUrl(reference)); +export const getMultipleReferencesUrls = (pubModIds) => { + return pubModIds.sort().map((pubModId) => getSingleReferenceUrl(pubModId)); } const buildProvider = (annotation) => { diff --git a/src/components/disease/diseaseAnnotationTable.js b/src/components/disease/diseaseAnnotationTable.js index 36a6d112e..befa51752 100644 --- a/src/components/disease/diseaseAnnotationTable.js +++ b/src/components/disease/diseaseAnnotationTable.js @@ -9,7 +9,7 @@ import { SpeciesCell, } from '../dataTable'; import AnnotatedEntitiesPopupCuration from '../dataTable/AnnotatedEntitiesPopupCuration'; -import { getDistinctFieldValue, buildProvidersWithUrl } from '../dataTable/utils'; +import { getIdentifier, getDistinctFieldValue, buildProvidersWithUrl } from '../dataTable/utils'; import {compareByFixedOrder} from '../../lib/utils'; import {SPECIES_NAME_ORDER} from '../../constants'; import ProvidersCellCuration from '../dataTable/ProvidersCellCuration'; @@ -61,13 +61,18 @@ const DiseaseAnnotationTable = ({ hidden: !orthologGenes || !orthologGenes.length }, { - dataField: 'subject.curie', + dataField: 'subject', text: 'Gene', - formatter: (curie, row) => ( + formatter: (subject, row) => ( -
{GeneCellCuration(row.subject)}
+ - + Annotation details @@ -103,7 +108,7 @@ const DiseaseAnnotationTable = ({ formatter: diseaseQualifiers => , }, { - dataField: 'object.curie', + dataField: 'disease', text: 'Disease', filterable: true, headerStyle: {width: '150px'}, @@ -142,12 +147,12 @@ const DiseaseAnnotationTable = ({ formatter: BasedOnGeneCellCuration, }, { - dataField: 'references', + dataField: 'pubmedPubModIDs', text: 'References', filterable: true, filterName: 'reference', headerStyle: {width: '150px'}, - formatter: ReferencesCellCuration, + formatter: (pubModIds) => , } ]; diff --git a/src/components/orthology/index.js b/src/components/orthology/index.js index 52e834d71..ea5eb54b0 100644 --- a/src/components/orthology/index.js +++ b/src/components/orthology/index.js @@ -3,6 +3,8 @@ import OrthologyFilteredTable from './orthologyFilteredTable'; import HomologyUserGuide from '../homology/homologyUserGuide'; import OrthologyBasicInfo from './orthologyBasicInfo'; import StringencySelection from '../homology/stringencySelection'; +import OrthologyJBrowseLink from './orthologyJBrowseLink'; +import OrthologyJBrowseLinkPanel from './orthologyJBrowseLinkPanel'; export { OrthologyBasicInfo, @@ -10,6 +12,8 @@ export { OrthologyFilteredTable, HomologyUserGuide, StringencySelection, + OrthologyJBrowseLink, + OrthologyJBrowseLinkPanel }; export * from '../homology/utils'; diff --git a/src/components/orthology/orthologyJBrowseLink.js b/src/components/orthology/orthologyJBrowseLink.js new file mode 100644 index 000000000..ac1e1cd29 --- /dev/null +++ b/src/components/orthology/orthologyJBrowseLink.js @@ -0,0 +1,57 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import {stringify as stringifyQuery} from 'qs'; +import ExternalLink from '../ExternalLink'; +import { + getSpecies +} from '../../lib/utils'; + +const buildTrackList = (taxonid,filterlevel) => { + const trackList = getSpecies(taxonid).jBrowseOrthologyTracks.replace(/filter/g, filterlevel); + return trackList; +}; + +const buildAssembly = (taxonid) => { + return getSpecies(taxonid).jBrowseName.replace(' ', '_'); +} + +const buildLocation = (geneLocation, taxonid) => { + const chrom = (taxonid === 'NCBITaxon:559292' || (taxonid === 'NCBITaxon:8355' && !(geneLocation.chromosome.startsWith('Scaffold')) ) ) + ? 'chr' + geneLocation.chromosome : geneLocation.chromosome; + return chrom + ':' + geneLocation.start + '..' + geneLocation.end; +} + +const buildAnchor = (filterlevel) => { + return filterlevel === 'none' ? 'No filter' + : filterlevel === 'best' ? 'Best and Best Reverse' + : filterlevel === 'moderate' ? 'Moderate' + : filterlevel === 'stringent' ? 'Stringent' : ''; +} + +const OrthologyJBrowseLink = ({filterlevel, geneLocation, taxonid}) => { + return ( + + {buildAnchor(filterlevel)} + + ); +}; + +OrthologyJBrowseLink.propTypes = { + children: PropTypes.node, + geneLocation: PropTypes.shape({ + start: PropTypes.number, + end: PropTypes.number, + chromosome: PropTypes.string, + }), + taxonid: PropTypes.string.isRequired, + filterlevel: PropTypes.string.isRequired +}; + +export default OrthologyJBrowseLink; diff --git a/src/components/orthology/orthologyJBrowseLinkPanel.js b/src/components/orthology/orthologyJBrowseLinkPanel.js new file mode 100644 index 000000000..3285a0d15 --- /dev/null +++ b/src/components/orthology/orthologyJBrowseLinkPanel.js @@ -0,0 +1,57 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import OrthologyJBrowseLink from './orthologyJBrowseLink'; + +const geneLocationIsInvalid = (geneLocation) => { + if (typeof geneLocation.chromosome === 'undefined') { + return true; + } else if (typeof geneLocation.start === 'undefined') { + return true; + } else if (typeof geneLocation.end === 'undefined') { + return true; + } + return false; +} + +const OrthologyJBrowseLinkPanel = ({geneLocation, taxonid}) => { + return ( + (geneLocationIsInvalid(geneLocation) || taxonid === 'NCBITaxon:2697049') ? null : +
+ Links to orthology data in JBrowse by filter level: + + ,   + + ,   + + ,   + +
+ ); +}; + +OrthologyJBrowseLinkPanel.propTypes = { + geneLocation: PropTypes.shape({ + start: PropTypes.number, + end: PropTypes.number, + chromosome: PropTypes.string, + }), + taxonid: PropTypes.string.isRequired +}; + +export default OrthologyJBrowseLinkPanel; diff --git a/src/components/pathway/pathwayWidget.js b/src/components/pathway/pathwayWidget.js index a3c8018fb..03fa96f77 100644 --- a/src/components/pathway/pathwayWidget.js +++ b/src/components/pathway/pathwayWidget.js @@ -482,18 +482,10 @@ class PathwayWidget extends Component {
- + show-legend="true" + style={{ "maxWidth": "1280px" }}> +
:
diff --git a/src/components/variant/VariantJBrowseLink.js b/src/components/variant/VariantJBrowseLink.js index eabb97788..cee8a2e10 100644 --- a/src/components/variant/VariantJBrowseLink.js +++ b/src/components/variant/VariantJBrowseLink.js @@ -2,7 +2,11 @@ import React from 'react'; import PropTypes from 'prop-types'; import {stringify as stringifyQuery} from 'qs'; import ExternalLink from '../ExternalLink'; +import { + getSpecies +} from '../../lib/utils'; +// this will return when the get parameter 'highlight' is added to JB2 const calculateHighlight = (location, type) => { switch(type){ case 'insertion': @@ -16,16 +20,42 @@ const calculateHighlight = (location, type) => { } }; -const VariantJBrowseLink = ({children, location, type, geneSymbol, geneLocation, species}) => { +const LINK_BUFFER = 1.2; + +const buildTrackList = (taxonid) => { + const tracks = []; + const trackList = getSpecies(taxonid).jBrowsetracks.split(','); + const assembly = buildAssembly(taxonid); + for (const track of trackList) { + tracks.push( assembly + track ); + } + return tracks.join(','); +}; + +const buildAssembly = (taxonid) => { + return getSpecies(taxonid).jBrowseName.replace(' ', '_'); +} + +const buildLoc = (location) => { + const start = location.start || 0; + const end = location.end || 0 + const linkLength = end - start; + if (linkLength === 0 ) { return; } + let bufferedMin = Math.round(start - (linkLength * LINK_BUFFER / 2.5)); + bufferedMin = bufferedMin < 0 ? 0 : bufferedMin; + const bufferedMax = Math.round(end + (linkLength * LINK_BUFFER )); + return location.chromosome + ':' + bufferedMin + '..' + bufferedMax ; +} + +const VariantJBrowseLink = ({children, location, type, geneSymbol, geneLocation, taxonid}) => { return ( location ? @@ -47,8 +77,8 @@ VariantJBrowseLink.propTypes = { end: PropTypes.number, chromosome: PropTypes.string, }), - species: PropTypes.string.isRequired, - type: PropTypes.string + type: PropTypes.string, + taxonid: PropTypes.string.isRequired }; export default VariantJBrowseLink; diff --git a/src/constants.js b/src/constants.js index 395baf844..d3bf9c93e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -89,8 +89,8 @@ export const NAV_MENU = [ external: true, }, { - label: 'JBrowse', - route: '/jbrowse/?data=data%2FHomo%20sapiens', + label: 'JBrowse 2', + route: '/jbrowse2/', external: true, }, { @@ -264,6 +264,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/human/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000001405.40_GRCh38.p14_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants', + jBrowseOrthologyTracks: 'Homo_sapiens_all_genes,human2fly.filter.anchors,human2mouse.filter.anchors,human2rat.filter.anchors,human2worm.filter.anchors,human2xenopuslaevis.filter.anchors,human2xenopustropicalis.filter.anchors,human2yeast.filter.anchors,human2zebrafish.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -278,6 +280,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/MGI/mouse/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000001635.27_GRCm39_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants,_variants,_multiple-variant_alleles', + jBrowseOrthologyTracks:'Mus_musculus_all_genes,human2mouse.filter.anchors,mouse2fly.filter.anchors,mouse2worm.filter.anchors,mouse2xenopustropicalis.filter.anchors,mouse2yeast.filter.anchors,mouse2zebrafish.filter.anchors,rat2mouse.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -292,6 +296,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/RGD/rat/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_015227675.2_mRatBN7.2_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants,_variants', + jBrowseOrthologyTracks:'Rattus_norvegicus_all_genes,human2rat.filter.anchors,rat2fly.filter.anchors,rat2mouse.filter.anchors,rat2worm.filter.anchors,rat2xenopustropicalis.filter.anchors,rat2yeast.filter.anchors,rat2zebrafish.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -306,6 +312,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/XenBase/x_laevis/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/XENLA_9.2_genome.fa.gz', + jBrowsetracks: '_all_genes', + jBrowseOrthologyTracks:'Xenopus_laevis_all_genes,human2xenopuslaevis.filter.anchors,xenopuslaevis2xenopustropicalis.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: false, enableOrthologComparison: true, @@ -320,6 +328,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/XenBase/x_tropicalis/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/XENTR_9.1_genome.fa.gz', + jBrowsetracks: '_all_genes', + jBrowseOrthologyTracks:'Xenopus_tropicalis_all_genes,human2xenopustropicalis.filter.anchors,mouse2xenopustropicalis.filter.anchors,rat2xenopustropicalis.filter.anchors,xenopuslaevis2xenopustropicalis.filter.anchors,xenopustropicalis2fly.filter.anchors,xenopustropicalis2worm.filter.anchors,xenopustropicalis2yeast.filter.anchors,zebrafish2xenopustropicalis.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: false, enableOrthologComparison: true, @@ -334,6 +344,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/zfin/zebrafish-11/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000002035.6_GRCz11_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants,_variants', + jBrowseOrthologyTracks:'Danio_rerio_all_genes,human2zebrafish.filter.anchors,mouse2zebrafish.filter.anchors,rat2zebrafish.filter.anchors,zebrafish2fly.filter.anchors,zebrafish2worm.filter.anchors,zebrafish2xenopustropicalis.filter.anchors,zebrafish2yeast.filter.anchors', vertebrate: true, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -348,6 +360,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/FlyBase/fruitfly/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000001215.4_Release_6_plus_ISO1_MT_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants,_variants,_multiple-variant_alleles', + jBrowseOrthologyTracks:'Drosophila_melanogaster_all_genes,fly2yeast.filter.anchors,human2fly.filter.anchors,mouse2fly.filter.anchors,rat2fly.filter.anchors,worm2fly.filter.anchors,xenopustropicalis2fly.filter.anchors,zebrafish2fly.filter.anchors', vertebrate: false, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -362,6 +376,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/WormBase/c_elegans_PRJNA13758/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000002985.6_WBcel235_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants,_variants', + jBrowseOrthologyTracks:'Caenorhabditis_elegans_all_genes,human2worm.filter.anchors,mouse2worm.filter.anchors,rat2worm.filter.anchors,worm2fly.filter.anchors,worm2yeast.filter.anchors,xenopustropicalis2worm.filter.anchors,zebrafish2worm.filter.anchors', vertebrate: false, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -376,6 +392,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/${RELEASE}/SGD/yeast/`, jBrowseurltemplate: 'tracks/All_Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000146045.2_R64_genomic.fna.gz', + jBrowsetracks: '_all_genes,_ht_variants', + jBrowseOrthologyTracks:'Saccharomyces_cerevisiae_all_genes,fly2yeast.filter.anchors,human2yeast.filter.anchors,mouse2yeast.filter.anchors,rat2yeast.filter.anchors,worm2yeast.filter.anchors,xenopustropicalis2yeast.filter.anchors,zebrafish2yeast.filter.anchors', vertebrate: false, enableSingleCellExpressionAtlasLink: true, enableOrthologComparison: true, @@ -390,6 +408,8 @@ export const SPECIES = [ jBrowsenclistbaseurl: `https://s3.amazonaws.com/agrjbrowse/docker/3.2.0/SARS-CoV-2/`, jBrowseurltemplate: 'tracks/All Genes/{refseq}/trackData.jsonz', jBrowsefastaurl: 'https://s3.amazonaws.com/agrjbrowse/fasta/GCF_000001405.40_GRCh38.p14_genomic.fna.gz', + jBrowsetracks: '_all_genes', + jBrowseOrthologyTracks:'', suppressFlatten: true, vertebrate: false, enableSingleCellExpressionAtlasLink: false, @@ -416,3 +436,4 @@ export const GA_EVENT_ACTION = { SET_PAGE_SIZE: 'Set page size' }; +export const DOWNLOAD_BUTTON_THRESHOLD = 90000; diff --git a/src/containers/GeneAlleleDetailsPage/GeneAlleleDetailsTable.js b/src/containers/GeneAlleleDetailsPage/GeneAlleleDetailsTable.js index 5807bda66..a460cf4bc 100644 --- a/src/containers/GeneAlleleDetailsPage/GeneAlleleDetailsTable.js +++ b/src/containers/GeneAlleleDetailsPage/GeneAlleleDetailsTable.js @@ -120,6 +120,7 @@ const GeneAlleleDetailsTable = ({ isLoadingGene, gene, geneId }) => { location={variant.location} species={gene.species && gene.species.name} type={variant.variantType && variant.variantType.name} + taxonid={gene.species && gene.species.taxonId} >{variant.displayName}
) : null, diff --git a/src/containers/allelePage/AlleleSequenceView.js b/src/containers/allelePage/AlleleSequenceView.js index 06526553f..48194bc6c 100644 --- a/src/containers/allelePage/AlleleSequenceView.js +++ b/src/containers/allelePage/AlleleSequenceView.js @@ -22,6 +22,13 @@ function findFminFmax(genomeLocation, variants) { return { fmin, fmax }; } +function findAlleleStart(variants) { + if (variants && variants.data.results) { +//just get the start of the first variant + return variants[0].data.results.location.start; + } +} + const AlleleSequenceView = ({ allele }) => { const variants = useAllAlleleVariants(allele.id); const visibleTranscripts = useDataTableQuery(`/api/allele/${allele.id}/variants`); @@ -41,8 +48,9 @@ const AlleleSequenceView = ({ allele }) => { isoformFilter = visibleTranscripts.data[0].transcriptList.map(a => a.id.split(':').pop()); } - const { fmin, fmax } = findFminFmax(genomeLocation, variants); + //highjacking the htpVaraint to send the start of the variant itself + const htpVariant = findAlleleStart(variants); return ( { displayType='ISOFORM_AND_VARIANT' fmax={fmax} fmin={fmin} + htpVariant={htpVariant} geneSymbol={allele.symbol} genomeLocationList={genomeLocations} height='200px' diff --git a/src/containers/allelePage/AlleleToDiseaseTable.js b/src/containers/allelePage/AlleleToDiseaseTable.js index 274893283..45bf1f014 100644 --- a/src/containers/allelePage/AlleleToDiseaseTable.js +++ b/src/containers/allelePage/AlleleToDiseaseTable.js @@ -53,7 +53,7 @@ const AlleleToDiseaseTable = ({alleleId}) => { { dataField: 'primaryAnnotations', text: 'Annotation details', - formatter: entities => , + formatter: (entities, row) => , headerStyle: {width: '90px'}, }, { @@ -71,9 +71,9 @@ const AlleleToDiseaseTable = ({alleleId}) => { filterName: 'dataProvider', }, { - dataField: 'references', + dataField: 'pubmedPubModIDs', text: 'References', - formatter: references => ReferencesCellCuration(references), + formatter: (pubModIds) => , headerStyle: {width: '150px'}, filterable: true, filterName: 'reference', diff --git a/src/containers/allelePage/AlleleToVariantTable.js b/src/containers/allelePage/AlleleToVariantTable.js index 669d9fd25..e313f962e 100644 --- a/src/containers/allelePage/AlleleToVariantTable.js +++ b/src/containers/allelePage/AlleleToVariantTable.js @@ -32,6 +32,7 @@ const AlleleToVariantTable = ({allele = {}, alleleId}) => { location={location} species={species.name} type={type.name} + taxonid={species.taxonId} > {name} @@ -94,6 +95,7 @@ AlleleToVariantTable.propTypes = { }), species: PropTypes.shape({ name: PropTypes.string, + taxonid: PropTypes.string }) }), alleleId: PropTypes.any, diff --git a/src/containers/allelePage/VariantEffectDetails.js b/src/containers/allelePage/VariantEffectDetails.js index afb3d532f..4e6de8c03 100644 --- a/src/containers/allelePage/VariantEffectDetails.js +++ b/src/containers/allelePage/VariantEffectDetails.js @@ -63,6 +63,7 @@ const VariantEffectDetails = ({ location={variant.location} species={variant.species && variant.species.name} type={variant.variantType && variant.variantType.name} + taxonid={variant.species && variant.species.taxonId} > {variant.id} on {transcript.name} diff --git a/src/containers/allelePage/VariantSummary.js b/src/containers/allelePage/VariantSummary.js index 0759cc7f3..70197e483 100644 --- a/src/containers/allelePage/VariantSummary.js +++ b/src/containers/allelePage/VariantSummary.js @@ -39,7 +39,7 @@ const VariantSummary = ({variant}) => { } = variant || {}; const genomeLocation = getVariantGenomeLocation(variant); - + console.log(species); return ( <> Symbol @@ -51,6 +51,7 @@ const VariantSummary = ({variant}) => { location={location} species={species && species.name} type={type && type.name} + taxonid={species && species.taxonId} > {symbol || displayName || variantId} diff --git a/src/containers/diseasePage/DiseaseToGeneTable.js b/src/containers/diseasePage/DiseaseToGeneTable.js index 72fbfee68..3c982f387 100644 --- a/src/containers/diseasePage/DiseaseToGeneTable.js +++ b/src/containers/diseasePage/DiseaseToGeneTable.js @@ -1,113 +1,159 @@ import React from 'react'; import PropTypes from 'prop-types'; import { - EvidenceCodesCell, - GeneCell, - ReferenceCell, - SpeciesCell, + BasedOnGeneCellCuration, DataTable, - BasedOnGeneCell + EvidenceCodesCellCuration, + GeneCellCuration, + ReferencesCellCuration, } from '../../components/dataTable'; -import { - compareByFixedOrder, -} from '../../lib/utils'; -import AnnotatedEntitiesPopup - from '../../components/dataTable/AnnotatedEntitiesPopup'; -import DiseaseLink from '../../components/disease/DiseaseLink'; -import {getDistinctFieldValue} from '../../components/dataTable/utils'; -import {SPECIES_NAME_ORDER} from '../../constants'; -import ProvidersCell from '../../components/dataTable/ProvidersCell'; + +import ProvidersCellCuration from '../../components/dataTable/ProvidersCellCuration'; import useDataTableQuery from '../../hooks/useDataTableQuery'; -import SpeciesName from '../../components/SpeciesName'; import AssociationType from '../../components/AssociationType'; +import { buildProvidersWithUrl, getIsViaOrthology, getDistinctFieldValue, getIdentifier } from '../../components/dataTable/utils'; +import { compareByFixedOrder } from '../../lib/utils'; +import { SPECIES_NAME_ORDER } from '../../constants'; +import SpeciesCell from '../../components/dataTable/SpeciesCell'; +import SpeciesName from '../../components/SpeciesName'; +import DiseaseLinkCuration from '../../components/disease/DiseaseLinkCuration'; +import DiseaseQualifiersColumn from '../../components/dataTable/DiseaseQualifiersColumn'; +import AnnotatedEntitiesPopupCuration from '../../components/dataTable/AnnotatedEntitiesPopupCuration'; +import ReferenceCellViaOrthologyCuration from '../../components/dataTable/ReferencesCellViaOrthologyCuration'; -const DiseaseToGeneTable = ({id}) => { +const DiseaseToGeneTable = ({ id }) => { const { data: results, resolvedData, ...tableProps } = useDataTableQuery(`/api/disease/${id}/genes`, undefined, { sizePerPage: 10, }, {}, 60000); + + const columns = [ { - dataField: 'gene', + dataField: 'subject', text: 'Gene', - formatter: (gene, row) => ( - -
{GeneCell(gene)}
- - - Annotation details - - -
- ), + formatter: (subject, row) => { + const isViaOrthology = getIsViaOrthology(row); + return ( + +
+ +
+ {!isViaOrthology && ( + + + Annotation details + + + )} +
+ ); + }, + headerStyle: { width: '75px' }, filterable: true, filterName: 'geneName', - headerStyle: {width: '120px'}, }, { - dataField: 'species', + dataField: 'subject.taxon', text: 'Species', - formatter: species => , + headerStyle: { width: '100px' }, + formatter: species => , + filterName: 'species', filterable: getDistinctFieldValue(resolvedData, 'species').sort(compareByFixedOrder(SPECIES_NAME_ORDER)), filterFormatter: speciesName => {speciesName}, - headerStyle: {width: '105px'}, + filterType: 'checkbox', }, { - dataField: 'associationType', + dataField: 'generatedRelationString', text: 'Association', + helpPopupProps: { + id: 'disease-page--gene-disease-associations-table--association-help', + children:
+

"Is Implicated in" means that some variant of the gene is shown to function in causing or modifying a disease (for human) or a disease model state.

+

"Is a marker for" is used when there is evidence of an association but insufficient evidence to establish causality and does not necessarily imply that the existence of, or change in the biomarker is causal for the disease, but rather may result from it.

+
, + }, formatter: type => , + headerStyle: { width: '120px' }, + filterName: 'associationType', filterable: getDistinctFieldValue(resolvedData, 'associationType'), filterFormatter: type => , - headerStyle: {width: '110px'}, + filterType: 'checkbox', + }, + { + dataField: 'diseaseQualifiers', + text: 'Disease Qualifier', + headerStyle: { width: '100px' }, + formatter: diseaseQualifiers => , + filterable: getDistinctFieldValue(resolvedData, 'diseaseQualifiers'), + filterName: 'diseaseQualifier', + filterType: 'checkbox', }, { dataField: 'disease', text: 'Disease', - formatter: disease => , + headerStyle: { width: '150px' }, + formatter: (curie, row) => , filterable: true, - headerStyle: {width: '150px'}, }, { dataField: 'evidenceCodes', text: 'Evidence', - formatter: codes => , + helpPopupProps: { + id: 'disease-page--gene-disease-associations-table--evidence-help', + children: Mouse-over to decipher the evidence code. The Alliance uses these evidence codes to justify DO annotations., + }, + headerStyle: { width: '100px' }, + formatter: (codes) => , filterable: true, filterName: 'evidenceCode', - headerStyle: {width: '95px'}, }, { - dataField: 'orthologyGenes', + dataField: 'basedOnGenes', text: 'Based On', - formatter: BasedOnGeneCell, + helpPopupProps: { + id: 'disease-page--gene-disease-associations-table--based-on-help', + children: SGD uses orthology to human genes to associate yeast genes with the disease. + The Based On column is also used for inferred via-orthology disease annotations to indicate the gene that was directly (experimentally) annotated to the disease + + }, + headerStyle: { width: '100px' }, + formatter: BasedOnGeneCellCuration, filterable: true, filterName: 'basedOnGeneSymbol', - headerStyle: {width: '120px'}, }, { dataField: 'providers', text: 'Source', - formatter: providers => providers && - , + formatter: providers => providers && , + headerStyle: { width: '100px' }, filterable: true, - headerStyle: {width: '85px'}, - filterName: 'provider', + filterName: 'dataProvider', }, { - dataField: 'publications', + dataField: 'pubmedPubModIDs', text: 'References', - formatter: ReferenceCell, + headerStyle: { width: '150px' }, + formatter: (pubModIds, row) => { + const isViaOrthology = getIsViaOrthology(row); + if(!isViaOrthology) return + return + }, filterable: true, filterName: 'reference', - headerStyle: {width: '150px'}, - }, + } ]; - // need to pull out species in a separate field because we can't have - // two columns based on the gene field const rows = results.map(association => ({ - species: association.gene.species, + species: association.subject.taxon, + providers: buildProvidersWithUrl(association.primaryAnnotations), ...association, })); diff --git a/src/containers/genePage/alleleTable.js b/src/containers/genePage/alleleTable.js index 40b716eef..2fa14d7f3 100644 --- a/src/containers/genePage/alleleTable.js +++ b/src/containers/genePage/alleleTable.js @@ -19,7 +19,6 @@ import useDataTableQuery from '../../hooks/useDataTableQuery'; import useAllVariants from '../../hooks/useAllVariants'; const AlleleTable = ({ isLoadingGene, gene, geneId}) => { - const geneLocation = getSingleGenomeLocation(gene.genomeLocations); const tableProps = useDataTableQuery(`/api/gene/${geneId}/alleles`); @@ -170,6 +169,7 @@ const AlleleTable = ({ isLoadingGene, gene, geneId}) => { location={location} species={gene.species && gene.species.name} type={type.name} + taxonid={gene.species && gene.species.taxonId} >{id} } diff --git a/src/containers/genePage/genomeFeatureWrapper.js b/src/containers/genePage/genomeFeatureWrapper.js index a329f749e..085c47010 100644 --- a/src/containers/genePage/genomeFeatureWrapper.js +++ b/src/containers/genePage/genomeFeatureWrapper.js @@ -90,9 +90,18 @@ class GenomeFeatureWrapper extends Component { this.gfc.closeModal(); } - generateJBrowseLink(chr, start, end) { - const geneSymbolUrl = '&lookupSymbol=' + this.props.geneSymbol; - const externalJBrowsePrefix = '/jbrowse/?data=data%2F' + encodeURIComponent(getSpecies(this.props.species).jBrowseName); + generateJBrowseLink(chr, start, end, htpVariant) { + //const geneSymbolUrl = '&lookupSymbol=' + this.props.geneSymbol; + // maybe will use this ^^^ haven't decided + const assembly = (getSpecies(this.props.species).jBrowseName).replace(" ","_"); + var externalJBrowsePrefix = '/jbrowse2?tracklist=true&assembly=' + assembly; + + // htpVariant is a loc string of the format 'chr:start' for a variant + if (htpVariant) { + const pieces = htpVariant.split(':'); + externalJBrowsePrefix = externalJBrowsePrefix + '&highlight=' + pieces[0] + ':' + pieces[1] + '-' + pieces[1]; + } + const linkLength = end - start; let bufferedMin = Math.round(start - (linkLength * LINK_BUFFER / 2.0)); bufferedMin = bufferedMin < 0 ? 0 : bufferedMin; @@ -102,10 +111,13 @@ class GenomeFeatureWrapper extends Component { } const externalLocationString = chr + ':' + bufferedMin + '..' + bufferedMax; // TODO: handle bufferedMax exceeding chromosome length, though I think it has a good default. - const tracks = ['Variants', 'All Genes','Multiple-Variant Alleles', 'High Throughput Variants']; + const tracks = []; + const trackList = getSpecies(this.props.species).jBrowsetracks.split(','); + for (const track of trackList) { + tracks.push( assembly + track ); + } return externalJBrowsePrefix + - '&tracks=' + encodeURIComponent(tracks.join(',')) + - '&highlight=' + geneSymbolUrl + + '&tracks=' + tracks.join(',') + '&loc=' + encodeURIComponent(externalLocationString); } @@ -213,13 +225,14 @@ class GenomeFeatureWrapper extends Component { } render() { - const {assembly, id, displayType,genomeLocationList} = this.props; + const {assembly, id, displayType,genomeLocationList,htpVariant} = this.props; const genomeLocation = getSingleGenomeLocation(genomeLocationList); const coordinates = genomeLocationList.map(location => { + //htpVariant contains location of variant info; can use that to set hightlight return( - + {location.chromosome.toLowerCase().startsWith('chr') || location.chromosome.toLowerCase().startsWith('scaffold') ? location.chromosome : 'Chr' + location.chromosome}:{location.start}...{location.end} {location.strand} ({numeral((location.end - location.start) / 1000.0).format('0,0.00')} kb) diff --git a/src/containers/genePage/index.js b/src/containers/genePage/index.js index f4876d16b..549ded6da 100644 --- a/src/containers/genePage/index.js +++ b/src/containers/genePage/index.js @@ -2,7 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import { DataPage, PageNav, PageData, PageHeader } from '../../components/dataPage'; import BasicGeneInfo from './basicGeneInfo'; -import { OrthologyFilteredTable, HomologyUserGuide, OrthologyBasicInfo } from '../../components/orthology'; +import { + OrthologyFilteredTable, + HomologyUserGuide, + OrthologyBasicInfo, + OrthologyJBrowseLinkPanel +} from '../../components/orthology'; import ParalogyTable from '../../components/paralogy/paralogyTable' import ParalogyUserGuide from '../../components/paralogy/paralogyUserGuide' import GoUserGuide from '../../components/geneOntologyRibbon/goUserGuide'; @@ -135,6 +140,10 @@ const GenePage = ({geneId}) => { } title={ORTHOLOGY}> + } title={PARALOGY}> diff --git a/src/containers/layout/ReleaseBanner.js b/src/containers/layout/ReleaseBanner.js index dd0202306..786ec36ec 100644 --- a/src/containers/layout/ReleaseBanner.js +++ b/src/containers/layout/ReleaseBanner.js @@ -10,13 +10,13 @@ const ReleaseBanner = () => { isError, } = useRelease(); - const { releaseVersion, releaseDate } = releaseInfo || {}; + const { releaseVersion, snapShotDate } = releaseInfo || {}; return isLoading ? : ( Version: {isError ? 'Unknown' : releaseVersion}
- Date: { isError ? 'Unknown' : new Date(releaseDate).toDateString()} + Date: { isError ? 'Unknown' : new Date(snapShotDate).toDateString()}
); }; diff --git a/src/style.scss b/src/style.scss index bb549479e..8ba9902ed 100644 --- a/src/style.scss +++ b/src/style.scss @@ -124,4 +124,48 @@ wc-ribbon-cell.ribbon__subject--cell--no-annotation { --gocam-panel-process-font-weight: 400; --gocam-panel-h1-color: #2598C5; --gocam-panel-h1-weight: 400; +} + +wc-gocam-viz { + --activity-background-active: #af732a; + --activity-color-active: #fff; + --button-background: #DDD; + --button-color: #000; + --button-background-hover: #BBB; + --button-border-width: 1px; + --button-border-color: #ccc; + + // Panel + --panel-header-background: #ececec; + --panel-border-color: #AAA; + + // Process + --process-label-background: #7a548d; + --process-label-color: #fff; + + --process-label-padding: 0.5rem; + --activity-padding: 0.5rem; + --gene-product-padding: 0 0 0.5rem 0; + --function-label-padding: 0 0 0.5rem 0; + + --graph-padding: 8px; + +--panel-header-padding: 8px; + +--panel-border-color: #CCC; + +--panel-border-width: 1px; +--process-border-width: 1px; + + +//Legend +--legend-padding: 8px; +--legend-header-padding: 8px; + +} + + +.gocam-viz-legend-container { + padding: 0 1.2rem; + width: 800px; } \ No newline at end of file