From 83927756ad3db93a325cddf9f94ad727c7c35a04 Mon Sep 17 00:00:00 2001 From: gebingbing Date: Wed, 27 Nov 2024 12:45:31 -0500 Subject: [PATCH 1/4] Add readOnly to CurationPageStore --- README.md | 27 ++++++++++++++++--- .../app/pages/curation/CurationPage.tsx | 16 ++++++++++- .../curation/button/AddMutationButton.tsx | 4 ++- .../app/pages/curation/button/RCTButton.tsx | 14 +++++++--- .../collapsible/CancerTypeCollapsible.tsx | 18 ++++++++++--- .../collapsible/MutationCollapsible.tsx | 18 ++++++++++--- .../collapsible/TherapyCollapsible.tsx | 10 +++++-- .../collapsible/TherapyDropdownGroup.tsx | 12 +++++---- .../header/GenomicIndicatorsHeader.tsx | 12 +++++++-- .../header/MutationsSectionHeader.tsx | 9 ++++++- .../app/pages/curation/list/TherapiesList.tsx | 5 +++- .../app/shared/icons/MutationConvertIcon.tsx | 3 ++- .../shared/table/GenomicIndicatorsTable.tsx | 12 ++++++--- src/main/webapp/app/shared/table/VusTable.tsx | 8 +++++- .../webapp/app/stores/curation-page.store.ts | 12 +++++++-- 15 files changed, 147 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index a431eb763..63f9dc007 100644 --- a/README.md +++ b/README.md @@ -302,19 +302,40 @@ We use the [WebDriverIO](https://webdriver.io/) framework for our end-to-end tes 1. Follow the [Set up](#set-up) instructions -2. Start up just the local client +2. Edit `initializeFirebase` function in firbase-app.store.ts + +```sh + initializeFirebase() { + // Add the code at the begin of the function, you can find parameter value at application-dev.yml + if (AppConfig.serverConfig.frontend) { + AppConfig.serverConfig.frontend.firebase = { + enabled: true, + apiKey: "api-key", + authDomain: "auth-domain", + databaseUrl: "database-url", + projectId: "project-id", + storageBucket: "storage-bucket", + messagingSenderId: "messageing-sender-id", + appId: "app-id", + measurementId: "measurement-id", + connectToFirebaseEmulators: false, + }; + } +``` + +3. Start up just the local client ```sh yarn start ``` -3. Start the firebase emulator +4. Start the firebase emulator ```sh yarn run firebase-emulator ``` -4. Run web driver IO +5. Run web driver IO ```sh yarn run wdio diff --git a/src/main/webapp/app/pages/curation/CurationPage.tsx b/src/main/webapp/app/pages/curation/CurationPage.tsx index 079b45506..918f14b24 100644 --- a/src/main/webapp/app/pages/curation/CurationPage.tsx +++ b/src/main/webapp/app/pages/curation/CurationPage.tsx @@ -25,10 +25,11 @@ import { notifyError } from 'app/oncokb-commons/components/util/NotificationUtil import LoadingIndicator, { LoaderSize } from 'app/oncokb-commons/components/loadingIndicator/LoadingIndicator'; import { parseHistory } from 'app/shared/util/firebase/firebase-history-utils'; import { useMatchGeneEntity } from 'app/hooks/useMatchGeneEntity'; -import { Unsubscribe, get, ref } from 'firebase/database'; +import { Unsubscribe, get, ref, onValue } from 'firebase/database'; import { getLocationIdentifier, getTooltipHistoryList } from 'app/components/geneHistoryTooltip/gene-history-tooltip-utils'; import GeneticTypeTabs, { GENETIC_TYPE } from './geneticTypeTabs/GeneticTypeTabs'; import GeneticTypeTabHeader from './header/GeneticTypeTabHeader'; +import FlagStore from 'app/entities/flag/flag.store'; export interface ICurationPageProps extends StoreProps, RouteComponentProps<{ hugoSymbol: string }> {} @@ -86,6 +87,11 @@ export const CurationPage = (props: ICurationPageProps) => { } if (geneEntity && props.firebaseInitSuccess) { const cleanupCallbacks: Unsubscribe[] = []; + cleanupCallbacks.push( + onValue(ref(props.firebaseDb, firebaseMetaCurrentReviewerPath), snapshot => { + props.setReadOnly(snapshot.val() ? true : false); + }), + ); cleanupCallbacks.push(props.addHistoryListener(firebaseHistoryPath)); cleanupCallbacks.push(props.addMutationListListener(mutationsPath)); return () => { @@ -127,6 +133,7 @@ export const CurationPage = (props: ICurationPageProps) => { Gene Type @@ -147,6 +154,7 @@ export const CurationPage = (props: ICurationPageProps) => { })} /> { { <>
{
({ firebaseDb: firebaseAppStore.firebaseDb, firebaseInitSuccess: firebaseAppStore.firebaseInitSuccess, @@ -329,6 +341,8 @@ const mapStoreToProps = ({ setOpenMutationCollapsibleIndex: openMutationCollapsibleStore.setOpenMutationCollapsibleIndex, toggleOncoKBSidebar: layoutStore.toggleOncoKBSidebar, isGermline: routerStore.isGermline, + readOnly: curationPageStore.readOnly, + setReadOnly: curationPageStore.setReadOnly, }); type StoreProps = ReturnType; diff --git a/src/main/webapp/app/pages/curation/button/AddMutationButton.tsx b/src/main/webapp/app/pages/curation/button/AddMutationButton.tsx index f0ca51dea..8c4a4d3ed 100644 --- a/src/main/webapp/app/pages/curation/button/AddMutationButton.tsx +++ b/src/main/webapp/app/pages/curation/button/AddMutationButton.tsx @@ -7,9 +7,11 @@ const AddMutationButton: React.FunctionComponent<{ onClickHandler: (show: boolean) => void; showIcon?: boolean; showFullTitle?: boolean; -}> = ({ showAddMutationModal, onClickHandler, showIcon = true, showFullTitle = false }) => { + disabled?: boolean; +}> = ({ showAddMutationModal, onClickHandler, showIcon = true, showFullTitle = false, disabled = false }) => { return (
}} onClick={() => setIsConvertingToVus(true)} /> @@ -329,7 +331,7 @@ const MutationCollapsible = ({ ? { overlay: Cannot modify because mutation is associated with genomic indicator(s) } : null } - disabled={isAssociatedWithGenomicIndicator} + disabled={isAssociatedWithGenomicIndicator || readOnly} /> Cannot delete because mutation is associated with genomic indicator(s) } : null } - disabled={isAssociatedWithGenomicIndicator} + disabled={isAssociatedWithGenomicIndicator || readOnly} /> } @@ -369,7 +371,7 @@ const MutationCollapsible = ({ } name="mutationSummary" parseRefs - disabled={oncogenicity === ''} + disabled={oncogenicity === '' || readOnly} disabledMessage={'Not curatable: mutation summary is only curatable when oncogenicity is specified.'} /> {isGermline ? ( Pathogenicity @@ -416,6 +419,7 @@ const MutationCollapsible = ({ ) : ( <> Oncogenic @@ -444,6 +448,7 @@ const MutationCollapsible = ({ }))} /> Mutation Effect @@ -470,6 +475,7 @@ const MutationCollapsible = ({ )} <> Penetrance @@ -524,6 +531,7 @@ const MutationCollapsible = ({ }))} /> <> Mechanism of Inheritance @@ -577,6 +586,7 @@ const MutationCollapsible = ({ }))} /> @@ -321,6 +325,7 @@ const mapStoreToProps = ({ firebaseGeneService, firebaseVusService, openMutationCollapsibleStore, + curationPageStore, }: IRootStore) => ({ firebaseDb: firebaseAppStore.firebaseDb, addVus: firebaseVusService.addVus, @@ -331,6 +336,7 @@ const mapStoreToProps = ({ addMutation: firebaseGeneService.addMutation, setOpenMutationCollapsibleIndex: openMutationCollapsibleStore.setOpenMutationCollapsibleIndex, sendVusToCore: firebaseVusService.sendVusToCore.bind(firebaseVusService), + readOnly: curationPageStore.readOnly, }); type StoreProps = Partial>; diff --git a/src/main/webapp/app/stores/curation-page.store.ts b/src/main/webapp/app/stores/curation-page.store.ts index 3fce1033b..c53d87812 100644 --- a/src/main/webapp/app/stores/curation-page.store.ts +++ b/src/main/webapp/app/stores/curation-page.store.ts @@ -1,6 +1,6 @@ import { IRootStore } from 'app/stores/createStore'; import { Alteration as ApiAlteration, AlterationAnnotationStatus, AnnotateAlterationBody } from 'app/shared/api/generated/curation'; -import { makeAutoObservable } from 'mobx'; +import { makeAutoObservable, action, observable } from 'mobx'; import { REFERENCE_GENOME } from 'app/config/constants/constants'; import { Alteration as FirebaseAlt } from 'app/shared/model/firebase/firebase.model'; import { alterationControllerClient } from 'app/shared/api/clients'; @@ -73,9 +73,13 @@ export class CurationPageStore { get: (hugoSymbol: string, mutations: MutationQuery[]) => this.getAnnotatedAltsCache(hugoSymbol, mutations), fetch: (hugoSymbol: string, mutations: MutationQuery[]) => this.fetchAnnotatedAltsCache(hugoSymbol, mutations), }; + public readOnly: boolean = false; constructor(protected rootStore: IRootStore) { - makeAutoObservable(this); + makeAutoObservable(this, { + readOnly: observable, + setReadOnly: action.bound, + }); } async annotateAlterations(queries: AnnotateAlterationBody[]) { @@ -133,6 +137,10 @@ export class CurationPageStore { .filter(query => query.queryId && this.annotatedAltsCache.cache[query.queryId] && this.annotatedAltsCache.cache[query.queryId].result) .map(query => (query.queryId !== undefined ? this.annotatedAltsCache.cache[query.queryId].result : undefined)); } + + setReadOnly(isAnotherUserReviewing: boolean) { + this.readOnly = isAnotherUserReviewing ? true : false; + } } export default CurationPageStore; From 0dafc063d3651c751567fce83c6ab54323bfe3a3 Mon Sep 17 00:00:00 2001 From: gebingbing Date: Thu, 5 Dec 2024 14:57:46 -0500 Subject: [PATCH 2/4] Add a banner and update RCT --- .../app/pages/curation/CurationPage.tsx | 2 + .../app/pages/curation/button/RCTButton.tsx | 9 +--- .../collapsible/CancerTypeCollapsible.tsx | 4 +- .../collapsible/TherapyCollapsible.tsx | 2 +- .../pages/curation/header/ReadOnlyBanner.tsx | 49 +++++++++++++++++++ 5 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx diff --git a/src/main/webapp/app/pages/curation/CurationPage.tsx b/src/main/webapp/app/pages/curation/CurationPage.tsx index 918f14b24..16946d67a 100644 --- a/src/main/webapp/app/pages/curation/CurationPage.tsx +++ b/src/main/webapp/app/pages/curation/CurationPage.tsx @@ -29,6 +29,7 @@ import { Unsubscribe, get, ref, onValue } from 'firebase/database'; import { getLocationIdentifier, getTooltipHistoryList } from 'app/components/geneHistoryTooltip/gene-history-tooltip-utils'; import GeneticTypeTabs, { GENETIC_TYPE } from './geneticTypeTabs/GeneticTypeTabs'; import GeneticTypeTabHeader from './header/GeneticTypeTabHeader'; +import ReadOnlyBanner from './header/ReadOnlyBanner'; import FlagStore from 'app/entities/flag/flag.store'; export interface ICurationPageProps extends StoreProps, RouteComponentProps<{ hugoSymbol: string }> {} @@ -123,6 +124,7 @@ export const CurationPage = (props: ICurationPageProps) => { firebaseGeneExists && hugoSymbol ? ( <> + {props.readOnly && }
diff --git a/src/main/webapp/app/pages/curation/button/RCTButton.tsx b/src/main/webapp/app/pages/curation/button/RCTButton.tsx index 86145b586..1a69b942f 100644 --- a/src/main/webapp/app/pages/curation/button/RCTButton.tsx +++ b/src/main/webapp/app/pages/curation/button/RCTButton.tsx @@ -13,16 +13,9 @@ import { Button } from 'reactstrap'; export interface IRCTButtonProps extends StoreProps { cancerTypePath: string; relevantCancerTypesInfoPath: string; // path to dx, px, or tx - readOnly?: boolean; } -function RCTButton({ - cancerTypePath, - relevantCancerTypesInfoPath, - firebaseDb, - relevantCancerTypesModalStore, - readOnly = false, -}: IRCTButtonProps) { +function RCTButton({ cancerTypePath, relevantCancerTypesInfoPath, firebaseDb, relevantCancerTypesModalStore, readOnly }: IRCTButtonProps) { const [cancerType, setCancerType] = useState(); const [relevantCancerTypesInfo, setRelevantCancerTypesInfo] = useState(); diff --git a/src/main/webapp/app/pages/curation/collapsible/CancerTypeCollapsible.tsx b/src/main/webapp/app/pages/curation/collapsible/CancerTypeCollapsible.tsx index a97bb8808..5bd333a86 100644 --- a/src/main/webapp/app/pages/curation/collapsible/CancerTypeCollapsible.tsx +++ b/src/main/webapp/app/pages/curation/collapsible/CancerTypeCollapsible.tsx @@ -211,7 +211,7 @@ function CancerTypeCollapsible({ action={ <> - + } badge={} @@ -242,7 +242,7 @@ function CancerTypeCollapsible({ action={ <> - + } badge={} diff --git a/src/main/webapp/app/pages/curation/collapsible/TherapyCollapsible.tsx b/src/main/webapp/app/pages/curation/collapsible/TherapyCollapsible.tsx index 7d6e7f96e..090236265 100644 --- a/src/main/webapp/app/pages/curation/collapsible/TherapyCollapsible.tsx +++ b/src/main/webapp/app/pages/curation/collapsible/TherapyCollapsible.tsx @@ -109,7 +109,7 @@ function TherapyCollapsible({ action={ <> - + { diff --git a/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx new file mode 100644 index 000000000..c5d36f7ba --- /dev/null +++ b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx @@ -0,0 +1,49 @@ +import { componentInject } from 'app/shared/util/typed-inject'; +import { Meta } from 'app/shared/model/firebase/firebase.model'; +import React, { useEffect, useState } from 'react'; +import { observer } from 'mobx-react'; +import { IRootStore } from 'app/stores'; +import { onValue, ref } from 'firebase/database'; +import { getFirebaseMetaGenePath } from 'app/shared/util/firebase/firebase-utils'; +import { BiInfoCircle } from 'react-icons/bi'; + +export interface IReadOnlyBanner extends StoreProps { + hugoSymbol: string; +} + +const ReadOnlyBanner = ({ isGermline, hugoSymbol, firebaseDb }: IReadOnlyBanner) => { + const firebaseMetaPath = getFirebaseMetaGenePath(isGermline, hugoSymbol); + const [meta, setMeta] = useState(); + const [isVisible, setIsVisible] = useState(true); + + useEffect(() => { + if (!firebaseDb) { + return; + } + const subscribe = onValue(ref(firebaseDb, firebaseMetaPath), snapshot => { + setMeta(snapshot.val()); + }); + return () => subscribe?.(); + }, []); + + const handleClose = () => { + setIsVisible(false); + }; + + return meta && isVisible ? ( +
+ + This page is currently in read-only mode because {meta.review.currentReviewer} is currently reviewing. + +
+ ) : null; +}; + +const mapStoreToProps = ({ firebaseAppStore, routerStore }: IRootStore) => ({ + firebaseDb: firebaseAppStore.firebaseDb, + isGermline: routerStore.isGermline, +}); + +type StoreProps = Partial>; + +export default componentInject(mapStoreToProps)(observer(ReadOnlyBanner)); From b881e033c4475606645510f805e9b34d9445de84 Mon Sep 17 00:00:00 2001 From: gebingbing Date: Mon, 16 Dec 2024 18:23:02 -0500 Subject: [PATCH 3/4] update of ReadOnlyBanner --- src/main/webapp/app/pages/curation/CurationPage.tsx | 2 +- .../app/pages/curation/header/ReadOnlyBanner.tsx | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/pages/curation/CurationPage.tsx b/src/main/webapp/app/pages/curation/CurationPage.tsx index 16946d67a..254d5eb9b 100644 --- a/src/main/webapp/app/pages/curation/CurationPage.tsx +++ b/src/main/webapp/app/pages/curation/CurationPage.tsx @@ -124,13 +124,13 @@ export const CurationPage = (props: ICurationPageProps) => { firebaseGeneExists && hugoSymbol ? ( <> - {props.readOnly && }
+ {props.readOnly && }
diff --git a/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx index c5d36f7ba..7713070f1 100644 --- a/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx +++ b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx @@ -26,15 +26,13 @@ const ReadOnlyBanner = ({ isGermline, hugoSymbol, firebaseDb }: IReadOnlyBanner) return () => subscribe?.(); }, []); - const handleClose = () => { - setIsVisible(false); - }; - return meta && isVisible ? (
- This page is currently in read-only mode because {meta.review.currentReviewer} is currently reviewing. - + + {isGermline ? 'This Germline ' : 'This Somatic '} + is currently in read only mode because {meta.review.currentReviewer} is currently reviewing. +
) : null; }; From 32396c2f2dfc98a87709b369424acd5994b63d77 Mon Sep 17 00:00:00 2001 From: gebingbing Date: Mon, 23 Dec 2024 17:57:42 -0500 Subject: [PATCH 4/4] update banner test --- src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx index 7713070f1..997e03821 100644 --- a/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx +++ b/src/main/webapp/app/pages/curation/header/ReadOnlyBanner.tsx @@ -30,8 +30,8 @@ const ReadOnlyBanner = ({ isGermline, hugoSymbol, firebaseDb }: IReadOnlyBanner)
- {isGermline ? 'This Germline ' : 'This Somatic '} - is currently in read only mode because {meta.review.currentReviewer} is currently reviewing. + {isGermline ? 'The germline ' : 'The somatic '} + section is currently in read only mode because {meta.review.currentReviewer} is currently reviewing.
) : null;