Skip to content

Commit

Permalink
add new feature changes
Browse files Browse the repository at this point in the history
  • Loading branch information
shashankbrgowda committed Oct 24, 2023
1 parent 08210d8 commit f6bcba3
Show file tree
Hide file tree
Showing 8 changed files with 349 additions and 69 deletions.
2 changes: 1 addition & 1 deletion packages/apollo-shared/src/Changes/AddFeatureChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ export class AddFeatureChange extends FeatureChange {
const childIds = this.getChildFeatureIds(addedFeature)
const allIdsV2 = [_id, ...childIds]
const [newFeatureDoc] = await featureModel.create(
[{ allIds: allIdsV2, ...addedFeature }],
[{ allIds: allIdsV2, status: 0, ...addedFeature }],
{ session },
)
logger.verbose?.(`Added docId "${newFeatureDoc._id}"`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getRoot, getSnapshot } from 'mobx-state-tree'
import React, { useEffect, useMemo, useRef, useState } from 'react'

import { ApolloInternetAccountModel } from '../../ApolloInternetAccount/model'
import { AddFeature } from '../../components/AddFeature'
import { AddChildFeature } from '../../components/AddChildFeature'
import { CopyFeature } from '../../components/CopyFeature'
import { DeleteFeature } from '../../components/DeleteFeature'
import { Collaborator } from '../../session'
Expand Down Expand Up @@ -577,7 +577,7 @@ function ApolloRendering(props: ApolloRenderingProps) {
const currentAssemblyId = getAssemblyId(region.assemblyName)
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
AddFeature,
AddChildFeature,
{
session,
handleClose: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { alpha } from '@mui/material'
import { AnnotationFeatureI } from 'apollo-mst'

import {
AddFeature,
AddChildFeature,
CopyFeature,
DeleteFeature,
ModifyFeatureAttribute,
Expand Down Expand Up @@ -221,7 +221,7 @@ export abstract class Glyph {
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
AddFeature,
AddChildFeature,
{
session,
handleClose: () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AnnotationFeatureI } from 'apollo-mst'

import { ChangeManager } from '../../ChangeManager'
import {
AddFeature,
AddChildFeature,
CopyFeature,
DeleteFeature,
ModifyFeatureAttribute,
Expand Down Expand Up @@ -37,7 +37,7 @@ export function featureContextMenuItems(
onClick: () => {
;(session as unknown as AbstractSessionModel).queueDialog(
(doneCallback) => [
AddFeature,
AddChildFeature,
{
session,
handleClose: () => {
Expand Down
247 changes: 247 additions & 0 deletions packages/jbrowse-plugin-apollo/src/components/AddChildFeature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
import { AbstractSessionModel } from '@jbrowse/core/util'
import {
Button,
DialogActions,
DialogContent,
DialogContentText,
FormControl,
InputLabel,
MenuItem,
Select,
SelectChangeEvent,
TextField,
} from '@mui/material'
import { AnnotationFeatureI } from 'apollo-mst'
import { AddFeatureChange } from 'apollo-shared'
import ObjectID from 'bson-objectid'
import React, { useState } from 'react'

import { ChangeManager } from '../ChangeManager'
import { isOntologyClass } from '../OntologyManager'
import OntologyStore from '../OntologyManager/OntologyStore'
import { ApolloSessionModel } from '../session'
import { Dialog } from './Dialog'
import { OntologyTermAutocomplete } from './OntologyTermAutocomplete'

interface AddChildFeatureProps {
session: ApolloSessionModel
handleClose(): void
sourceFeature: AnnotationFeatureI
sourceAssemblyId: string
changeManager: ChangeManager
}

enum PhaseEnum {
zero = 0,
one = 1,
two = 2,
}

export function AddChildFeature({
changeManager,
handleClose,
session,
sourceAssemblyId,
sourceFeature,
}: AddChildFeatureProps) {
const { notify } = session as unknown as AbstractSessionModel
const [end, setEnd] = useState(String(sourceFeature.end))
const [start, setStart] = useState(String(sourceFeature.start))
const [type, setType] = useState('')
const [phase, setPhase] = useState('')
const [phaseAsNumber, setPhaseAsNumber] = useState<PhaseEnum>()
const [showPhase, setShowPhase] = useState<boolean>(false)
const [errorMessage, setErrorMessage] = useState('')
const [typeWarningText, setTypeWarningText] = useState('')

async function fetchValidDescendantTerms(
parentFeature: AnnotationFeatureI | undefined,
ontologyStore: OntologyStore,
_signal: AbortSignal,
) {
if (!parentFeature) {
return
}
// since this is a child of an existing feature, restrict the autocomplete choices to valid
// parts of that feature
const parentTypeTerms = await ontologyStore.getTermsWithLabelOrSynonym(
parentFeature.type,
{ includeSubclasses: false },
)
// eslint-disable-next-line unicorn/no-array-callback-reference
const parentTypeClassTerms = parentTypeTerms.filter(isOntologyClass)
if (parentTypeTerms.length === 0) {
return
}
const subpartTerms = await ontologyStore.getClassesThat(
'part_of',
parentTypeClassTerms,
)
if (subpartTerms.length > 0) {
setTypeWarningText('')
} else {
setTypeWarningText(
`Type "${parentFeature.type}" does not have any children in the ontology`,
)
}
return subpartTerms
}

async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setErrorMessage('')
if (showPhase && phase === '') {
setErrorMessage('The phase is REQUIRED for all CDS features.')
return
}
const change = new AddFeatureChange({
changedIds: [sourceFeature._id],
typeName: 'AddFeatureChange',
assembly: sourceAssemblyId,
addedFeature: {
_id: new ObjectID().toHexString(),
gffId: '',
refSeq: sourceFeature.refSeq,
start: Number(start),
end: Number(end),
type,
phase: phaseAsNumber,
},
parentFeatureId: sourceFeature._id,
})
await changeManager.submit?.(change)
notify('Feature added successfully', 'success')
handleClose()
event.preventDefault()
}
function handleChangeType(newType: string) {
setErrorMessage('')
setType(newType)
if (newType.startsWith('CDS')) {
setShowPhase(true)
setPhase('')
} else {
setShowPhase(false)
}
}
async function handleChangePhase(e: SelectChangeEvent<string>) {
setErrorMessage('')
setPhase(e.target.value)

switch (Number(e.target.value)) {
case 0: {
setPhaseAsNumber(PhaseEnum.zero)
break
}
case 1: {
setPhaseAsNumber(PhaseEnum.one)
break
}
case 2: {
setPhaseAsNumber(PhaseEnum.two)
break
}
default: {
// eslint-disable-next-line unicorn/no-useless-undefined
setPhaseAsNumber(undefined)
}
}
}
const error = Number(end) <= Number(start)
return (
<Dialog
open
title="Add new child feature"
handleClose={handleClose}
maxWidth={false}
data-testid="add-feature-dialog"
>
<form onSubmit={onSubmit}>
<DialogContent style={{ display: 'flex', flexDirection: 'column' }}>
<TextField
margin="dense"
id="start"
label="Start"
type="number"
fullWidth
variant="outlined"
value={start}
onChange={(e) => setStart(e.target.value)}
/>
<TextField
margin="dense"
id="end"
label="End"
type="number"
fullWidth
variant="outlined"
value={end}
onChange={(e) => setEnd(e.target.value)}
error={error}
helperText={error ? '"End" must be greater than "Start"' : null}
/>
{/* <Select value={type} onChange={handleChangeType} label="Type">
{(possibleChildTypes ?? []).map((option) => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</Select> */}
<OntologyTermAutocomplete
session={session}
ontologyName="Sequence Ontology"
style={{ width: 170 }}
value={type}
filterTerms={isOntologyClass}
fetchValidTerms={fetchValidDescendantTerms.bind(
null,
sourceFeature,
)}
renderInput={(params) => (
<TextField
{...params}
label="Type"
variant="outlined"
fullWidth
error={Boolean(typeWarningText)}
helperText={typeWarningText}
/>
)}
onChange={(oldValue, newValue) => {
if (newValue) {
handleChangeType(newValue)
}
}}
/>
{showPhase ? (
<FormControl>
<InputLabel>Phase</InputLabel>
<Select value={phase} onChange={handleChangePhase}>
<MenuItem value={0}>0</MenuItem>
<MenuItem value={1}>1</MenuItem>
<MenuItem value={2}>2</MenuItem>
</Select>
</FormControl>
) : null}
</DialogContent>
<DialogActions>
<Button
variant="contained"
type="submit"
disabled={error || !(start && end && type)}
>
Submit
</Button>
<Button variant="outlined" type="submit" onClick={handleClose}>
Cancel
</Button>
</DialogActions>
</form>
{errorMessage ? (
<DialogContent>
<DialogContentText color="error">{errorMessage}</DialogContentText>
</DialogContent>
) : null}
</Dialog>
)
}
Loading

0 comments on commit f6bcba3

Please sign in to comment.