Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added core submission logic #370

Draft
wants to merge 1 commit into
base: rc
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { CollapsibleColorProps, CollapsibleDisplayProps } from './BaseCollapsibl
import { getReviewInfo } from 'app/shared/util/firebase/firebase-utils';
import { notifyError } from 'app/oncokb-commons/components/util/NotificationUtils';
import DiffViewer, { FirebaseContent } from 'app/components/diff-viewer/DiffViewer';
import { FirebaseGeneReviewService } from 'app/service/firebase/firebase-gene-review-service';
import { DrugCollection, Gene } from 'app/shared/model/firebase/firebase.model';

export enum ReviewType {
CREATE,
Expand Down Expand Up @@ -62,13 +64,16 @@ const ReviewCollapsibleBootstrapClass = {
};

export interface IReviewCollapsibleProps {
drugListRef: DrugCollection;
entrezGeneId: number;
gene: Gene;
hugoSymbol: string;
baseReviewLevel: BaseReviewLevel;
isGermline: boolean;
firebase: FirebaseContent;
parentDelete?: (reviewlLevelId: string, action: ActionType, isPending?: boolean) => void;
rootDelete?: (isPending?: boolean) => void;
handleAccept: (hugoSymbol: string, reviewLevels: ReviewLevel[], isGermline: boolean, isAcceptAll?: boolean) => Promise<void>;
handleAccept: FirebaseGeneReviewService['acceptChanges'];
handleReject: (hugoSymbol: string, reviewLevels: ReviewLevel[], isGermline: boolean) => Promise<void>;
handleCreateAction: (hugoSymbol: string, reviewLevel: ReviewLevel, isGermline: boolean, action: ActionType) => Promise<void>;
disableActions?: boolean;
Expand All @@ -87,6 +92,9 @@ export const ReviewCollapsible = ({
firebase,
disableActions = false,
isRoot = false,
drugListRef,
entrezGeneId,
gene,
}: IReviewCollapsibleProps) => {
const [rootReview, setRootReview] = useState<BaseReviewLevel>(baseReviewLevel);
const [reviewChildren, setReviewChildren] = useState<BaseReviewLevel[]>([]);
Expand Down Expand Up @@ -193,7 +201,7 @@ export const ReviewCollapsible = ({
// After marking collapsible as pending, it will be removed from the view. Now we need to save to firebase
try {
if (action === ActionType.ACCEPT) {
await handleAccept(hugoSymbol, getReviewLevelsForActions(), isGermline);
await handleAccept({ hugoSymbol, reviewLevels: getReviewLevelsForActions(), isGermline, drugListRef, entrezGeneId, gene });
} else if (action === ActionType.REJECT) {
await handleReject(hugoSymbol, getReviewLevelsForActions(), isGermline);
}
Expand Down Expand Up @@ -363,6 +371,9 @@ export const ReviewCollapsible = ({
handleCreateAction={handleCreateAction}
parentDelete={deleteHandlerForChild}
disableActions={disableActions}
gene={gene}
entrezGeneId={entrezGeneId}
drugListRef={drugListRef}
firebase={{
path: getGenePathFromValuePath(hugoSymbol, childReview.valuePath, isGermline),
db: firebase?.db,
Expand Down
30 changes: 25 additions & 5 deletions src/main/webapp/app/pages/curation/review/ReviewPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getSectionClassName } from 'app/shared/util/utils';
import { IRootStore } from 'app/stores';
import { get, ref } from 'firebase/database';
import { observer } from 'mobx-react';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Alert, Col, FormGroup, Input, Label, Row } from 'reactstrap';
import { RouteComponentProps } from 'react-router-dom';
import { useMatchGeneEntity } from 'app/hooks/useMatchGeneEntity';
Expand All @@ -23,8 +23,12 @@ import _ from 'lodash';
import { ReviewCollapsible } from '../collapsible/ReviewCollapsible';
import { notifyError } from 'app/oncokb-commons/components/util/NotificationUtils';
import { AsyncSaveButton } from 'app/shared/button/AsyncSaveButton';
import { DrugCollection, Gene, MetaReview } from 'app/shared/model/firebase/firebase.model';
import { IGene } from 'app/shared/model/gene.model';

interface IReviewPageProps extends StoreProps, RouteComponentProps<{ hugoSymbol: string }> {}
interface IReviewPageProps extends StoreProps, RouteComponentProps<{ hugoSymbol: string }> {
geneEntity: IGene;
}

const ReviewPage: React.FunctionComponent<IReviewPageProps> = (props: IReviewPageProps) => {
const pathname = props.location.pathname;
Expand All @@ -36,8 +40,8 @@ const ReviewPage: React.FunctionComponent<IReviewPageProps> = (props: IReviewPag
const firebaseGenePath = getFirebaseGenePath(isGermline, hugoSymbol);
const firebaseMetaReviewPath = `${getFirebaseMetaGenePath(isGermline, hugoSymbol)}/review`;

const [geneData, setGeneData] = useState(null);
const [metaReview, setMetaReview] = useState(null);
const [geneData, setGeneData] = useState<Gene | null>(null);
const [metaReview, setMetaReview] = useState<MetaReview | null>(null);

const [isReviewFinished, setIsReviewFinished] = useState(false);

Expand All @@ -47,6 +51,11 @@ const ReviewPage: React.FunctionComponent<IReviewPageProps> = (props: IReviewPag
const [editorsToAcceptChangesFrom, setEditorsToAcceptChangesFrom] = useState<string[]>([]);
const [isAcceptingAll, setIsAcceptingAll] = useState(false);

const drugListRef: DrugCollection = useMemo(() => {
const group = _.mapValues(_.groupBy(props.drugList, 'uuid'), x => x[0]);
return group as DrugCollection;
}, [props.drugList]);

const fetchFirebaseData = () => {
// Fetch the data when the user enters review mode. We don't use a listener
// because there shouldn't be another user editing the gene when it is being reviewed.
Expand Down Expand Up @@ -99,7 +108,15 @@ const ReviewPage: React.FunctionComponent<IReviewPageProps> = (props: IReviewPag
}
try {
setIsAcceptingAll(true);
await props.acceptReviewChangeHandler(hugoSymbol, reviewLevels, isGermline, true);
await props.acceptReviewChangeHandler({
hugoSymbol,
reviewLevels,
isGermline,
isAcceptAll: true,
gene: geneData,
entrezGeneId: props.geneEntity.entrezGeneId,
drugListRef,
});
fetchFirebaseData();
} catch (error) {
notifyError(error);
Expand Down Expand Up @@ -180,6 +197,9 @@ const ReviewPage: React.FunctionComponent<IReviewPageProps> = (props: IReviewPag
<Row>
<Col>
<ReviewCollapsible
gene={geneData}
entrezGeneId={geneEntity.entrezGeneId}
drugListRef={drugListRef}
hugoSymbol={hugoSymbol}
isGermline={isGermline}
baseReviewLevel={rootReview}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { ReviewLevel } from 'app/shared/util/firebase/firebase-review-utils';
import { ReviewAction } from 'app/config/constants/firebase';
import _ from 'lodash';
import { ActionType } from 'app/pages/curation/collapsible/ReviewCollapsible';
import { EvidenceApi } from 'app/shared/api/manual/evidence-api';
import { createMockGene, createMockMutation } from 'app/shared/util/core-submission/core-submission.mocks';

describe('Firebase Gene Review Service', () => {
const DEFAULT_USERNAME = 'Test User';
Expand All @@ -20,6 +22,7 @@ describe('Firebase Gene Review Service', () => {
const mockMetaService = mock<FirebaseMetaService>();
const mockHistoryService = mock<FirebaseHistoryService>();
const mockVusService = mock<FirebaseVusService>();
const mockEvidenceClient = mock<EvidenceApi>();
let firebaseGeneReviewService: FirebaseGeneReviewService;

beforeEach(() => {
Expand All @@ -34,6 +37,7 @@ describe('Firebase Gene Review Service', () => {
mockMetaService,
mockHistoryService,
mockVusService,
mockEvidenceClient,
);
jest.useFakeTimers().setSystemTime(DEFAULT_DATE);
});
Expand Down Expand Up @@ -117,7 +121,14 @@ describe('Firebase Gene Review Service', () => {
},
});

await firebaseGeneReviewService.acceptChanges(hugoSymbol, [reviewLevel], false);
await firebaseGeneReviewService.acceptChanges({
hugoSymbol,
reviewLevels: [reviewLevel],
isGermline: false,
gene,
drugListRef: {},
entrezGeneId: 0,
});

expect(mockHistoryService.addHistory).toHaveBeenCalled();
// We expect the lastReviewed to be cleared when accepting changes
Expand Down Expand Up @@ -154,7 +165,17 @@ describe('Firebase Gene Review Service', () => {
},
});

await firebaseGeneReviewService.acceptChanges(hugoSymbol, [reviewLevel], false);
await firebaseGeneReviewService.acceptChanges({
hugoSymbol,
reviewLevels: [reviewLevel],
isGermline: false,
gene: createMockGene({
name: hugoSymbol,
mutations: [createMockMutation()],
}),
drugListRef: {},
entrezGeneId: 0,
});

expect(mockHistoryService.addHistory).toHaveBeenCalled();
expect(mockFirebaseRepository.deleteFromArray).toHaveBeenCalledWith('Genes/BRAF/mutations', [0]);
Expand Down Expand Up @@ -186,7 +207,17 @@ describe('Firebase Gene Review Service', () => {
},
});

await firebaseGeneReviewService.acceptChanges(hugoSymbol, [reviewLevel], false);
await firebaseGeneReviewService.acceptChanges({
hugoSymbol,
reviewLevels: [reviewLevel],
isGermline: false,
gene: createMockGene({
name: hugoSymbol,
mutations: [createMockMutation()],
}),
drugListRef: {},
entrezGeneId: 0,
});

expect(mockHistoryService.addHistory).toHaveBeenCalled();
expect(mockFirebaseRepository.deleteFromArray).toHaveBeenCalledWith('Genes/BRAF/mutations', [0]);
Expand Down Expand Up @@ -218,8 +249,18 @@ describe('Firebase Gene Review Service', () => {
},
});

// An entity is created once all its changes have been accepted or rejected.
await firebaseGeneReviewService.handleCreateAction(hugoSymbol, reviewLevel, false, ActionType.ACCEPT);
await firebaseGeneReviewService.acceptChanges({
hugoSymbol,
reviewLevels: [reviewLevel],
isGermline: false,
gene: createMockGene({
name: hugoSymbol,
mutations: [createMockMutation()],
}),
drugListRef: {},
entrezGeneId: 0,
});

const expectedMutation = _.cloneDeep(mutation);
delete expectedMutation.name_review.added;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FirebaseMetaService } from 'app/service/firebase/firebase-meta-service'
import { AuthStore } from 'app/stores';
import { FirebaseRepository } from 'app/stores/firebase/firebase-repository';
import _ from 'lodash';
import { Review } from '../../shared/model/firebase/firebase.model';
import { DrugCollection, Gene, Review } from '../../shared/model/firebase/firebase.model';
import { buildHistoryFromReviews } from '../../shared/util/firebase/firebase-history-utils';
import { extractArrayPath, parseFirebaseGenePath } from '../../shared/util/firebase/firebase-path-utils';
import {
Expand All @@ -23,26 +23,31 @@ import { FirebaseVusService } from './firebase-vus-service';
import { SentryError } from 'app/config/sentry-error';
import { ActionType } from 'app/pages/curation/collapsible/ReviewCollapsible';
import { GERMLINE_PATH } from 'app/config/constants/constants';
import { getEvidence, pathToGetEvidenceArgs } from 'app/shared/util/core-submission/core-submission';
import { EvidenceApi } from 'app/shared/api/manual/evidence-api';

export class FirebaseGeneReviewService {
firebaseRepository: FirebaseRepository;
authStore: AuthStore;
firebaseMetaService: FirebaseMetaService;
firebaseHistoryService: FirebaseHistoryService;
firebaseVusService: FirebaseVusService;
evidenceClient: EvidenceApi;

constructor(
firebaseRepository: FirebaseRepository,
authStore: AuthStore,
firebaseMetaService: FirebaseMetaService,
firebaseHistoryService: FirebaseHistoryService,
firebaseVusService: FirebaseVusService,
evidenceClient: EvidenceApi,
) {
this.firebaseRepository = firebaseRepository;
this.authStore = authStore;
this.firebaseMetaService = firebaseMetaService;
this.firebaseHistoryService = firebaseHistoryService;
this.firebaseVusService = firebaseVusService;
this.evidenceClient = evidenceClient;
}

updateReviewableContent = async (
Expand Down Expand Up @@ -84,10 +89,62 @@ export class FirebaseGeneReviewService {
}
};

acceptChanges = async (hugoSymbol: string, reviewLevels: ReviewLevel[], isGermline: boolean, isAcceptAll = false) => {
acceptChanges = async ({
gene,
hugoSymbol,
reviewLevels,
isGermline,
isAcceptAll = false,
drugListRef,
entrezGeneId,
}: {
gene: Gene;
hugoSymbol: string;
reviewLevels: ReviewLevel[];
isGermline: boolean;
isAcceptAll?: boolean;
drugListRef: DrugCollection;
entrezGeneId: number;
}) => {
const geneFirebasePath = getFirebaseGenePath(isGermline, hugoSymbol);
const vusFirebasePath = getFirebaseVusPath(isGermline, hugoSymbol);

let evidences: ReturnType<typeof getEvidence> = {};
let hasEvidences = false;
try {
for (const reviewLevel of reviewLevels) {
if (!(isCreateReview(reviewLevel) && isAcceptAll)) {
const args = pathToGetEvidenceArgs({
gene,
valuePath: reviewLevel.valuePath,
updateTime: new Date().getTime(),
drugListRef,
entrezGeneId,
});
if (args) {
evidences = {
...getEvidence(args),
};
hasEvidences = true;
}
}
}
} catch (error) {
throw new SentryError('Failed to create evidences when accepting changes in review mode', { hugoSymbol, reviewLevels, isGermline });
}

try {
if (hasEvidences) {
await this.evidenceClient.submitEvidences(evidences);
}
} catch (error) {
throw new SentryError('Failed to submit evidences to core when accepting changes in review mode', {
hugoSymbol,
reviewLevels,
isGermline,
});
}

const reviewHistory = buildHistoryFromReviews(this.authStore.fullName, reviewLevels);
try {
await this.firebaseHistoryService.addHistory(hugoSymbol, reviewHistory, isGermline);
Expand Down
3 changes: 3 additions & 0 deletions src/main/webapp/app/shared/api/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
CancerTypeResourceApi,
ArticleResourceApi,
} from './generated/curation/api';
import { EvidenceApi } from './manual/evidence-api';

export const fdaSubmissionClient = new FdaSubmissionResourceApi(null, '', axiosInstance);
export const geneClient = new GeneResourceApi(null, '', axiosInstance);
Expand All @@ -22,3 +23,5 @@ export const flagClient = new FlagResourceApi(null, '', axiosInstance);
export const associationClient = new AssociationResourceApi(null, '', axiosInstance);
export const cancerTypeClient = new CancerTypeResourceApi(null, '', axiosInstance);
export const articleClient = new ArticleResourceApi(null, '', axiosInstance);

export const evidenceClient = new EvidenceApi(null, '', axiosInstance);
Loading
Loading