From ab1df24997e25b9fe3b06542219084dac5f482b1 Mon Sep 17 00:00:00 2001 From: Carlos Cruz Date: Fri, 29 Nov 2024 13:57:54 +0000 Subject: [PATCH] [Platform]: study profile feedback (#565) --- apps/platform/src/pages/StudyPage/Header.tsx | 103 +++++----- apps/platform/src/pages/StudyPage/Profile.tsx | 20 +- .../src/pages/StudyPage/ProfileHeader.tsx | 186 ------------------ .../src/pages/StudyPage/StudyPage.gql | 4 +- .../src/pages/StudyPage/StudyPage.tsx | 31 +-- ...ofileHeader.gql => StudyProfileHeader.gql} | 1 - .../pages/StudyPage/StudyProfileHeader.tsx | 142 +++++++++++++ .../GWASCredibleSetsSummary.gql | 2 +- .../src/study/GWASCredibleSets/Body.tsx | 11 +- .../GWASCredibleSetsQuery.gql | 4 +- .../GWASCredibleSetsSummaryFragment.gql | 2 +- .../src/study/GWASCredibleSets/index.ts | 8 +- .../src/study/QTLCredibleSets/Body.tsx | 4 +- .../QTLCredibleSets/QTLCredibleSetsQuery.gql | 6 +- .../QTLCredibleSetsSummaryFragment.gql | 2 +- .../src/study/QTLCredibleSets/index.ts | 5 +- .../src/study/SharedTraitStudies/Body.tsx | 16 +- .../SharedTraitStudiesQuery.gql | 36 ++-- .../src/study/SharedTraitStudies/index.ts | 9 +- 19 files changed, 278 insertions(+), 314 deletions(-) delete mode 100644 apps/platform/src/pages/StudyPage/ProfileHeader.tsx rename apps/platform/src/pages/StudyPage/{ProfileHeader.gql => StudyProfileHeader.gql} (97%) create mode 100644 apps/platform/src/pages/StudyPage/StudyProfileHeader.tsx diff --git a/apps/platform/src/pages/StudyPage/Header.tsx b/apps/platform/src/pages/StudyPage/Header.tsx index 644c0bc64..2818075a5 100644 --- a/apps/platform/src/pages/StudyPage/Header.tsx +++ b/apps/platform/src/pages/StudyPage/Header.tsx @@ -17,54 +17,71 @@ type HeaderProps = { }; function Header({ - loading, - studyId, - backgroundTraits, - targetId, - diseases, - studyCategory - }: HeaderProps) { - + loading, + studyId, + backgroundTraits, + targetId, + diseases, + projectId, +}: HeaderProps) { let traitLinks, sourceLink; - if (studyCategory === 'GWAS') { + if (projectId === "GCST") { if (diseases?.length) { - traitLinks = d.id)} - names={diseases.map(d => d.name)} - /> + traitLinks = ( + d.id)} + names={diseases.map(d => d.name)} + /> + ); } sourceLink = { id: "GWAS Catalog", url: `https://www.ebi.ac.uk/gwas/studies/${studyId}`, }; - } else if (studyCategory === 'FINNGEN') { + } else if (projectId === "FINNGEN_R11") { if (diseases?.length) { - traitLinks = d.id)} - names={diseases.map(d => d.name)} - /> + traitLinks = ( + d.id)} + names={diseases.map(d => d.name)} + /> + ); } sourceLink = { id: "FinnGen", url: `https://r10.finngen.fi/pheno/${studyId}`, }; - } else { // QTL + } else if (projectId === "UKB_PPP_EUR") { + if (diseases?.length) { + traitLinks = ( + d.id)} + names={diseases.map(d => d.name)} + /> + ); + } + sourceLink = { + id: "UKB", + url: "https://www.synapse.org/Synapse:syn51364943/wiki/622119", + }; + } else { + // QTL if (targetId) { - traitLinks = + traitLinks = ( + + ); } sourceLink = { id: "eQTL Catalog", url: "https://www.ebi.ac.uk/eqtl/Studies/", }; - }; + } return ( + <> {traitLinks} - { - studyCategory === "GWAS" && backgroundTraits?.length > 0 && - t.id)} - names={backgroundTraits.map(t => t.name)} - /> - } - + {projectId === "GWAS" && backgroundTraits?.length > 0 && ( + t.id)} + names={backgroundTraits.map(t => t.name)} + /> + )} + } /> ); - } -export default Header; \ No newline at end of file +export default Header; diff --git a/apps/platform/src/pages/StudyPage/Profile.tsx b/apps/platform/src/pages/StudyPage/Profile.tsx index bd94f3e91..1e135061f 100644 --- a/apps/platform/src/pages/StudyPage/Profile.tsx +++ b/apps/platform/src/pages/StudyPage/Profile.tsx @@ -13,7 +13,7 @@ import GWASCredidbleSetsSummary from "sections/src/study/GWASCredibleSets/Summar import QTLCredibleSetsSummary from "sections/src/study/QTLCredibleSets/Summary"; import client from "../../client"; -import ProfileHeader from "./ProfileHeader"; +import ProfileHeader from "./StudyProfileHeader"; const SharedTraitStudiesSection = lazy(() => import("sections/src/study/SharedTraitStudies/Body")); const GWASCredibleSetsSection = lazy(() => import("sections/src/study/GWASCredibleSets/Body")); @@ -23,7 +23,7 @@ const QTLCredibleSetsSection = lazy(() => import("sections/src/study/QTLCredible // (the summary cannot be written as a fragment as it gets further studies) const summaries = [GWASCredidbleSetsSummary, QTLCredibleSetsSummary]; -const STUDY = "gwasStudy"; +const STUDY = "study"; const STUDY_PROFILE_SUMMARY_FRAGMENT = summaryUtils.createSummaryFragment( summaries, "Gwas", @@ -31,13 +31,15 @@ const STUDY_PROFILE_SUMMARY_FRAGMENT = summaryUtils.createSummaryFragment( ); const STUDY_PROFILE_QUERY = gql` query StudyProfileQuery($studyId: String!, $diseaseIds: [String!]!) { - gwasStudy(studyId: $studyId) { - studyId + study(studyId: $studyId) { + id ...StudyProfileHeaderFragment ...StudyProfileSummaryFragment } - sharedTraitStudies: gwasStudy(diseaseIds: $diseaseIds, page: { size: 2, index: 0 }) { - studyId + sharedTraitStudies: studies(diseaseIds: $diseaseIds, page: { size: 2, index: 0 }) { + rows { + id + } } } ${ProfileHeader.fragments.profileHeader} @@ -53,11 +55,9 @@ type ProfileProps = { }[]; }; -function Profile({ studyId, studyType, diseases }: ProfileProps) { +function Profile({ studyId, studyType, projectId, diseases }: ProfileProps) { const diseaseIds = diseases?.map(d => d.id) || []; - console.log({ studyType }); - return ( - + {studyType === "gwas" && ( diff --git a/apps/platform/src/pages/StudyPage/ProfileHeader.tsx b/apps/platform/src/pages/StudyPage/ProfileHeader.tsx deleted file mode 100644 index 18e821b65..000000000 --- a/apps/platform/src/pages/StudyPage/ProfileHeader.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { - usePlatformApi, - Link, - Field, - ProfileHeader as BaseProfileHeader, - Tooltip, -} from "ui"; -import { Typography, Box } from "@mui/material"; - -import STUDY_PROFILE_HEADER_FRAGMENT from "./ProfileHeader.gql"; - -type samplesType = { - ancestry: string; - sampleSize: number; -}[]; - -function formatSamples(samples: samplesType) { - return samples - .map(({ ancestry, sampleSize }) => `${ancestry}: ${sampleSize}`) - .join(", "); -} - -type ProfileHeaderProps = { - studyCategory: string; -}; - -function ProfileHeader({ studyCategory }: ProfileHeaderProps) { - const { loading, error, data } = usePlatformApi(); - - // TODO: Errors! - if (error) return null; - - const { - publicationFirstAuthor, - publicationDate, - publicationJournal, - pubmedId, - nSamples, - initialSampleSize, - replicationSamples, - traitFromSource, - nCases, - nControls, - cohorts, - ldPopulationStructure, - qualityControls, - analysisFlags, - discoverySamples, - } = data?.gwasStudy?.[0] || {}; - - return ( - - <> - - { - studyCategory === "GWAS" || studyCategory === "QTL" - ? publicationFirstAuthor - : "FINNGEN" - } - - - { - studyCategory === "GWAS" || studyCategory === "QTL" - ? publicationDate - : "2023" - } - - - { - (studyCategory === "GWAS" || studyCategory === "QTL") && - publicationJournal - } - - - { - (studyCategory === "GWAS" || studyCategory === "QTL") && pubmedId - ? - {pubmedId} - - : null - } - - - {traitFromSource} - - - {nSamples} - - - { - studyCategory === "GWAS" - ? initialSampleSize - : studyCategory === "FINNGEN" - ? (discoverySamples?.length - ? (initialSampleSize - ? - Initial sample size: {initialSampleSize} - - } - showHelpIcon - > - {formatSamples(discoverySamples)} - - : formatSamples(discoverySamples) - ) - : null - ) - : null - } - - - { - studyCategory === "GWAS" && - replicationSamples?.length && - formatSamples(replicationSamples) - } - - - { - (studyCategory === "GWAS" || studyCategory === "FINNGEN") && - (typeof nCases === "number") && - nCases - } - - - { - (studyCategory === "GWAS" || studyCategory === "FINNGEN") && - (typeof nControls === "number") && - nControls - } - - - { - ( - (studyCategory === "GWAS" && cohorts?.length) || - (studyCategory === "FINNGEN") - ) && (ldPopulationStructure?.length - ? - - LD populations and relative sample sizes - - {ldPopulationStructure.map(({ ldPopulation, relativeSampleSize }) => ( - - - {ldPopulation}: {relativeSampleSize} - - - ))} - - } - showHelpIcon - > - {studyCategory === 'GWAS' ? cohorts.join(", ") : "FinnGen"} - - : (studyCategory === 'GWAS' ? cohorts.join(", ") : "FinnGen") - ) - } - - - { - studyCategory === "GWAS" && - qualityControls?.length && - qualityControls.join(", ") - } - - - { - studyCategory === "GWAS" && - analysisFlags?.length && - analysisFlags.join(", ") - } - - - - ); -} - -ProfileHeader.fragments = { - profileHeader: STUDY_PROFILE_HEADER_FRAGMENT, -}; - -export default ProfileHeader; diff --git a/apps/platform/src/pages/StudyPage/StudyPage.gql b/apps/platform/src/pages/StudyPage/StudyPage.gql index eff1ac171..115d798bf 100644 --- a/apps/platform/src/pages/StudyPage/StudyPage.gql +++ b/apps/platform/src/pages/StudyPage/StudyPage.gql @@ -1,6 +1,6 @@ query StudyPageQuery($studyId: String!) { - gwasStudy(studyId: $studyId) { - studyId + study(studyId: $studyId) { + id studyType projectId backgroundTraits { diff --git a/apps/platform/src/pages/StudyPage/StudyPage.tsx b/apps/platform/src/pages/StudyPage/StudyPage.tsx index 3738eeab0..81dba7ef6 100644 --- a/apps/platform/src/pages/StudyPage/StudyPage.tsx +++ b/apps/platform/src/pages/StudyPage/StudyPage.tsx @@ -14,7 +14,6 @@ import Header from "./Header"; import NotFoundPage from "../NotFoundPage"; import STUDY_PAGE_QUERY from "./StudyPage.gql"; import Profile from "./Profile"; -import { getStudyCategory } from "sections/src/utils/getStudyCategory"; function StudyPage() { const location = useLocation(); @@ -25,27 +24,26 @@ function StudyPage() { variables: { studyId }, }); - const studyInfo = data?.gwasStudy?.[0]; - - if (data && !studyInfo) { + if (data && !data.study) { return ; } - - const studyType = studyInfo?.studyType; + const study = data?.study; + const studyType = study?.studyType; + const projectId = study?.projectId; return (
@@ -67,10 +65,15 @@ function StudyPage() { - + - + diff --git a/apps/platform/src/pages/StudyPage/ProfileHeader.gql b/apps/platform/src/pages/StudyPage/StudyProfileHeader.gql similarity index 97% rename from apps/platform/src/pages/StudyPage/ProfileHeader.gql rename to apps/platform/src/pages/StudyPage/StudyProfileHeader.gql index f2d960d37..5f83a1d3d 100644 --- a/apps/platform/src/pages/StudyPage/ProfileHeader.gql +++ b/apps/platform/src/pages/StudyPage/StudyProfileHeader.gql @@ -1,5 +1,4 @@ fragment StudyProfileHeaderFragment on Gwas { - studyId publicationFirstAuthor publicationDate publicationJournal diff --git a/apps/platform/src/pages/StudyPage/StudyProfileHeader.tsx b/apps/platform/src/pages/StudyPage/StudyProfileHeader.tsx new file mode 100644 index 000000000..0f58c0243 --- /dev/null +++ b/apps/platform/src/pages/StudyPage/StudyProfileHeader.tsx @@ -0,0 +1,142 @@ +import { usePlatformApi, Link, Field, ProfileHeader as BaseProfileHeader, Tooltip } from "ui"; +import { Typography, Box } from "@mui/material"; + +import STUDY_PROFILE_HEADER_FRAGMENT from "./StudyProfileHeader.gql"; + +type samplesType = { + ancestry: string; + sampleSize: number; +}[]; + +function formatSamples(samples: samplesType) { + return samples.map(({ ancestry, sampleSize }) => `${ancestry}: ${sampleSize}`).join(", "); +} + +type ProfileHeaderProps = { + studyCategory: string; +}; + +function ProfileHeader({ studyCategory }: ProfileHeaderProps) { + const { loading, error, data } = usePlatformApi(); + + // TODO: Errors! + if (error) return null; + + const { + publicationFirstAuthor, + publicationDate, + publicationJournal, + pubmedId, + nSamples, + initialSampleSize, + replicationSamples, + traitFromSource, + nCases, + nControls, + cohorts, + ldPopulationStructure, + qualityControls, + analysisFlags, + discoverySamples, + } = data?.study || {}; + + return ( + + <> + + {publicationFirstAuthor} + + + {publicationDate} + + + {publicationJournal} + + + + {pubmedId} + + + + {traitFromSource} + + + {nSamples} + + + {studyCategory === "GWAS" ? ( + initialSampleSize + ) : studyCategory === "FINNGEN" ? ( + discoverySamples?.length ? ( + initialSampleSize ? ( + + Initial sample size: {initialSampleSize} + + } + showHelpIcon + > + {formatSamples(discoverySamples)} + + ) : ( + formatSamples(discoverySamples) + ) + ) : null + ) : null} + + + {studyCategory === "GWAS" && + replicationSamples?.length && + formatSamples(replicationSamples)} + + + {nCases} + + + {nControls} + + + {((studyCategory === "GWAS" && cohorts?.length) || studyCategory === "FINNGEN") && + (ldPopulationStructure?.length ? ( + + + LD populations and relative sample sizes + + {ldPopulationStructure.map(({ ldPopulation, relativeSampleSize }) => ( + + + {ldPopulation}: {relativeSampleSize} + + + ))} + + } + showHelpIcon + > + {studyCategory === "GWAS" ? cohorts.join(", ") : "FinnGen"} + + ) : studyCategory === "GWAS" ? ( + cohorts.join(", ") + ) : ( + "FinnGen" + ))} + + + {studyCategory === "GWAS" && qualityControls?.length && qualityControls.join(", ")} + + + {studyCategory === "GWAS" && analysisFlags?.length && analysisFlags.join(", ")} + + + + ); +} + +ProfileHeader.fragments = { + profileHeader: STUDY_PROFILE_HEADER_FRAGMENT, +}; + +export default ProfileHeader; diff --git a/packages/sections/src/evidence/GWASCredibleSets/GWASCredibleSetsSummary.gql b/packages/sections/src/evidence/GWASCredibleSets/GWASCredibleSetsSummary.gql index b2196e55a..73a881d7c 100644 --- a/packages/sections/src/evidence/GWASCredibleSets/GWASCredibleSetsSummary.gql +++ b/packages/sections/src/evidence/GWASCredibleSets/GWASCredibleSetsSummary.gql @@ -1,4 +1,4 @@ -fragment GWASCredibleSetsSummaryFragment on Disease { +fragment EvidenceGWASCredibleSetsSummaryFragment on Disease { gwasCredibleSets: evidences( ensemblIds: [$ensgId] enableIndirect: true diff --git a/packages/sections/src/study/GWASCredibleSets/Body.tsx b/packages/sections/src/study/GWASCredibleSets/Body.tsx index ee313d983..e91f2c804 100644 --- a/packages/sections/src/study/GWASCredibleSets/Body.tsx +++ b/packages/sections/src/study/GWASCredibleSets/Body.tsx @@ -164,7 +164,7 @@ function Body({ id, entity }: BodyProps) { size: table5HChunkSize, index: 0, }, - dataPath: "data.gwasStudy[0].credibleSets", + dataPath: "data.study.credibleSets", size: table5HChunkSize, }); @@ -179,20 +179,17 @@ function Body({ id, entity }: BodyProps) { definition={definition} entity={entity} request={request} - renderDescription={() => } + renderDescription={() => } renderBody={() => ( <> - + diff --git a/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsQuery.gql b/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsQuery.gql index 94a076a73..f95ee8e5d 100644 --- a/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsQuery.gql +++ b/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsQuery.gql @@ -1,6 +1,6 @@ query GWASCredibleSetsQuery($studyId: String!, $size: Int!, $index: Int!) { - gwasStudy(studyId: $studyId) { - studyId + study(studyId: $studyId) { + id credibleSets(page: { size: $size, index: $index }) { count rows { diff --git a/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsSummaryFragment.gql b/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsSummaryFragment.gql index d61d85048..da3ec68b4 100644 --- a/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsSummaryFragment.gql +++ b/packages/sections/src/study/GWASCredibleSets/GWASCredibleSetsSummaryFragment.gql @@ -2,4 +2,4 @@ fragment GWASCredibleSetsSummaryFragment on Gwas { gwasCredibleSets: credibleSets(page: { size: 1, index: 0 }) { count } -} \ No newline at end of file +} diff --git a/packages/sections/src/study/GWASCredibleSets/index.ts b/packages/sections/src/study/GWASCredibleSets/index.ts index 02c993b8a..6524684d9 100644 --- a/packages/sections/src/study/GWASCredibleSets/index.ts +++ b/packages/sections/src/study/GWASCredibleSets/index.ts @@ -3,6 +3,8 @@ export const definition = { id, name: "GWAS Credible Sets", shortName: "GW", - hasData: data => data?.[0]?.gwasCredibleSets?.count > 0 || - data?.[0]?.credibleSets?.count > 0, -}; \ No newline at end of file + hasData: data => { + console.log(data, "foo"); + return data?.gwasCredibleSets?.count > 0 || data?.credibleSets?.count > 0; + }, +}; diff --git a/packages/sections/src/study/QTLCredibleSets/Body.tsx b/packages/sections/src/study/QTLCredibleSets/Body.tsx index b0b05ad55..5e727a02d 100644 --- a/packages/sections/src/study/QTLCredibleSets/Body.tsx +++ b/packages/sections/src/study/QTLCredibleSets/Body.tsx @@ -109,7 +109,7 @@ function Body({ id, entity }: BodyProps) { definition={definition} entity={entity} request={request} - renderDescription={() => } + renderDescription={() => } renderBody={() => ( diff --git a/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsQuery.gql b/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsQuery.gql index e61e64e39..6f8138dbb 100644 --- a/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsQuery.gql +++ b/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsQuery.gql @@ -1,6 +1,6 @@ query QTLCredibleSetsQuery($studyId: String!) { - gwasStudy(studyId: $studyId) { - studyId + study(studyId: $studyId) { + id credibleSets(page: { size: 2000, index: 0 }) { count rows { @@ -22,4 +22,4 @@ query QTLCredibleSetsQuery($studyId: String!) { } } } -} \ No newline at end of file +} diff --git a/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsSummaryFragment.gql b/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsSummaryFragment.gql index a7cd3c8ac..f79cacbee 100644 --- a/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsSummaryFragment.gql +++ b/packages/sections/src/study/QTLCredibleSets/QTLCredibleSetsSummaryFragment.gql @@ -2,4 +2,4 @@ fragment QTLCredibleSetsSummaryFragment on Gwas { qtlCredibleSets: credibleSets(page: { size: 1, index: 0 }) { count } -} \ No newline at end of file +} diff --git a/packages/sections/src/study/QTLCredibleSets/index.ts b/packages/sections/src/study/QTLCredibleSets/index.ts index 710587472..2b75d831c 100644 --- a/packages/sections/src/study/QTLCredibleSets/index.ts +++ b/packages/sections/src/study/QTLCredibleSets/index.ts @@ -3,6 +3,5 @@ export const definition = { id, name: "molQTL Credible Sets", shortName: "QT", - hasData: data => data?.[0]?.qtlCredibleSets?.count > 0 || - data?.[0]?.credibleSets?.count > 0, -}; \ No newline at end of file + hasData: data => data?.qtlCredibleSets?.count > 0 || data?.credibleSets?.count > 0, +}; diff --git a/packages/sections/src/study/SharedTraitStudies/Body.tsx b/packages/sections/src/study/SharedTraitStudies/Body.tsx index e208a2744..d68d512aa 100644 --- a/packages/sections/src/study/SharedTraitStudies/Body.tsx +++ b/packages/sections/src/study/SharedTraitStudies/Body.tsx @@ -15,7 +15,7 @@ function getColumns(diseaseIds: string[]) { { id: "studyId", label: "Study", - renderCell: ({ studyId }) => {studyId}, + renderCell: ({ id }) => {id}, }, { id: "sharedDiseases", @@ -137,16 +137,16 @@ type BodyProps = { entity: string; }; -const parseStudies = (studyId, gwasStudy) => { - const studies = []; +const parseStudies = (studyId, studies) => { + const parsedStudies = []; const studyIds = new Set([studyId]); - for (const study of gwasStudy) { + for (const study of studies) { if (!studyIds.has(study.studyId)) { - studies.push(study); + parsedStudies.push(study); studyIds.add(study.studyId); } } - return studies; + return parsedStudies; }; export function Body({ studyId, diseaseIds, entity }: BodyProps) { @@ -164,10 +164,10 @@ export function Body({ studyId, diseaseIds, entity }: BodyProps) { } renderBody={() => { - const rows = request.data?.gwasStudy ? parseStudies(studyId, request.data.gwasStudy) : []; + const rows = request.data?.studies ? parseStudies(studyId, request.data.studies.rows) : []; return ( { - if (data?.sharedTraitStudies) { // summary - return data.sharedTraitStudies.some(study => ( - study.studyId !== data.gwasStudy?.[0]?.studyId - )); - } - return data?.length > 0; + return data?.sharedTraitStudies?.rows.length > 0 || data?.rows.length > 0; }, -}; \ No newline at end of file +};