Skip to content

Commit

Permalink
Multi-level synteny rendering (#4430)
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin authored Sep 27, 2024
1 parent 343e755 commit 57059aa
Show file tree
Hide file tree
Showing 81 changed files with 2,016 additions and 3,105 deletions.
23 changes: 23 additions & 0 deletions auth_test_utils/HTTPBasicAuthServer/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable no-console */
import express from 'express'
import path from 'path'
import expressBasicAuth from 'express-basic-auth'
import cors from 'cors'

const app = express()
const port = 3040

app.use(cors())

app.use(
'/data',
expressBasicAuth({
users: {
admin: 'password',
},
}),
express.static(path.join(__dirname, '..', '..', 'test_data', 'volvox')),
)

console.log('HTTP BasicAuth Server listening on port', port)
app.listen(port)
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import path from 'path'
/* eslint-disable no-console */
import express, { Request } from 'express'
import path from 'path'
import cors from 'cors'
import bodyParser from 'body-parser'
import oauthServer from './oauth/server'

import oauthServer from '../oauth/server'

const router = express.Router() // Instantiate a new router
const router = express.Router()

const filePath = path.join(__dirname, '../public/oauthAuthenticate.html')

Expand Down Expand Up @@ -44,4 +46,25 @@ router.post(

router.post('/token', oauthServer.token()) // Sends back token

export default router
const app = express()
const port = 3030

app.use(cors())

// Here we are configuring express to use body-parser as middle-ware.
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

app.use('/oauth', router) // routes to access the auth stuff
app.use(
'/data',
oauthServer.authenticate(),
express.static(path.join(__dirname, '..', '..', 'test_data', 'volvox')),
)

console.log(
'The redirect-uri is http://localhost:3000, must be running jbrowse-web on this port e.g. the default dev server port',
)

console.log('OAuth Server listening on port', port)
app.listen(port)
File renamed without changes.
File renamed without changes.
11 changes: 11 additions & 0 deletions auth_test_utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# extra_test_utils

These start up authenticated servers for testing HTTP Basic and OAuth based file
access in jbrowse-web

## Usage

1. Run `yarn oauth` in a tab
2. Run `yarn basicauth` in another tab
3. Start jbrowse-web dev server
4. Visit http://localhost:3000/?config=test_data/volvox/config_auth.json
10 changes: 10 additions & 0 deletions auth_test_utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"scripts": {
"oauth": "tsx OAuthServer/app.ts",
"basicauth": "tsx HTTPBasicAuthServer/app.ts"
},
"packages": {},
"dependencies": {
"tsx": "^4.19.1"
}
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"@types/dompurify": "^3.0.1",
"@types/escape-html": "^1.0.2",
"@types/express": "^4.17.8",
"@types/express-oauth-server": "^2.0.4",
"@types/express-oauth-server": "^2.0.7",
"@types/get-value": "^3.0.1",
"@types/is-object": "^1.0.0",
"@types/jest": "^29.2.4",
Expand Down Expand Up @@ -101,15 +101,15 @@
"dotenv": "^16.3.1",
"dotenv-expand": "^11.0.3",
"electron": "32.1.0",
"electron-builder": "next",
"electron-builder": "^25.1.6",
"electron-mock-ipc": "^0.3.8",
"eslint": "^9.0.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^5.1.0-rc-d48603a5-20240813",
"eslint-plugin-react-refresh": "^0.4.3",
"eslint-plugin-tsdoc": "^0.3.0",
"eslint-plugin-unicorn": "^55.0.0",
"express": "^4.18.2",
"express": "^4.0.0",
"express-basic-auth": "^1.2.1",
"find-yarn-workspace-root": "^2.0.0",
"globals": "^15.9.0",
Expand Down
8 changes: 6 additions & 2 deletions packages/app-core/src/ui/App/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import DialogQueue from './DialogQueue'
import AppFab from './AppFab'
import ViewsContainer from './ViewsContainer'

// lazies
const DrawerWidget = lazy(() => import('./DrawerWidget'))

const useStyles = makeStyles()(theme => ({
Expand All @@ -39,9 +40,12 @@ interface Props {
HeaderButtons?: React.ReactElement
session: SessionWithFocusedViewAndDrawerWidgets & {
savedSessionNames: string[]
menus: { label: string; menuItems: JBMenuItem[] }[]
renameCurrentSession: (arg: string) => void
menus: {
label: string
menuItems: JBMenuItem[]
}[]
snackbarMessages: SnackbarMessage[]
renameCurrentSession: (arg: string) => void
popSnackbarMessage: () => unknown
}
}
Expand Down
14 changes: 8 additions & 6 deletions packages/app-core/src/ui/App/ViewLauncher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const useStyles = makeStyles()(theme => ({
const ViewLauncher = observer(({ session }: { session: AppSession }) => {
const { classes } = useStyles()
const { pluginManager } = getEnv(session)
const viewTypes = pluginManager.getElementTypeRecord('view').all()
const viewTypes = pluginManager.getViewElements()
const [value, setValue] = useState(viewTypes[0]?.name || '')
return (
<Paper className={classes.selectPaper}>
Expand All @@ -49,11 +49,13 @@ const ViewLauncher = observer(({ session }: { session: AppSession }) => {
setValue(event.target.value)
}}
>
{viewTypes.map(({ displayName, name }) => (
<MenuItem key={name} value={name}>
{displayName}
</MenuItem>
))}
{viewTypes
.filter(({ viewMetadata }) => !viewMetadata.hiddenFromGUI)
.map(({ displayName, name }) => (
<MenuItem key={name} value={name}>
{displayName}
</MenuItem>
))}
</Select>
</FormControl>
<FormControl className={classes.m2}>
Expand Down
4 changes: 4 additions & 0 deletions packages/core/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ export default class PluginManager {
return this.getElementTypeRecord(groupName).all()
}

getViewElements() {
return this.getElementTypesInGroup('view') as ViewType[]
}

getTrackElements() {
return this.getElementTypesInGroup('track') as TrackType[]
}
Expand Down
30 changes: 9 additions & 21 deletions packages/core/assemblyManager/assemblyManager.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import {
addDisposer,
cast,
getParent,
types,
Instance,
IAnyType,
} from 'mobx-state-tree'
import { when } from '../util'
import { reaction } from 'mobx'

// locals
import { when } from '../util'
import { readConfObject, AnyConfigurationModel } from '../configuration'
import assemblyFactory, { Assembly } from './assembly'
import PluginManager from '../PluginManager'
Expand Down Expand Up @@ -164,20 +165,17 @@ function assemblyManagerFactory(conf: IAnyType, pm: PluginManager) {
reaction(
() => self.assemblyList,
assemblyConfs => {
self.assemblies.forEach(asm => {
for (const asm of self.assemblies) {
if (!asm.configuration) {
this.removeAssembly(asm)
}
})
assemblyConfs.forEach(conf => {
if (
!self.assemblies.some(
a => a.name === readConfObject(conf, 'name'),
)
) {
}
for (const conf of assemblyConfs) {
const name = readConfObject(conf, 'name')
if (!self.assemblies.some(a => a.name === name)) {
this.addAssembly(conf)
}
})
}
},
{ fireImmediately: true, name: 'assemblyManagerAfterAttach' },
),
Expand Down Expand Up @@ -207,16 +205,6 @@ function assemblyManagerFactory(conf: IAnyType, pm: PluginManager) {
addAssembly(configuration: Conf) {
self.assemblies.push({ configuration })
},

/**
* #action
* private: you would generally want to add to manipulate
* jbrowse.assemblies, session.sessionAssemblies, or
* session.temporaryAssemblies instead of using this directly
*/
replaceAssembly(idx: number, configuration: Conf) {
self.assemblies[idx] = cast({ configuration })
},
}))
}

Expand Down
11 changes: 10 additions & 1 deletion packages/core/pluggableElementTypes/ViewType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,22 @@ type BasicView = React.ComponentType<{
model: any
session?: IAnyStateTreeNode
}>

type ViewComponentType = React.LazyExoticComponent<BasicView> | BasicView

interface ViewMetadata {
hiddenFromGUI?: boolean
}

export default class ViewType extends PluggableElementBase {
ReactComponent: ViewComponentType

stateModel: IAnyModelType

displayTypes: DisplayType[] = []

viewMetadata: ViewMetadata = {}

// extendedName can be used for when you extend a given view type, and want
// to register all of that view types displays to yourself
//
Expand All @@ -30,12 +37,14 @@ export default class ViewType extends PluggableElementBase {
constructor(stuff: {
name: string
displayName?: string
ReactComponent: ViewComponentType
stateModel: IAnyModelType
extendedName?: string
viewMetadata?: ViewMetadata
ReactComponent: ViewComponentType
}) {
super(stuff)
this.ReactComponent = stuff.ReactComponent
this.viewMetadata = stuff.viewMetadata || {}
this.stateModel = stuff.stateModel
this.extendedName = stuff.extendedName
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,16 @@ export async function renderToSvg(model: BSV, opts: ExportSvgOptions) {

<SVGRuler model={displayResults[0]!.view} fontSize={fontSize} />
</g>
<SVGTracks
textHeight={textHeight}
trackLabels={trackLabels}
fontSize={fontSize}
model={displayResults[0]!.view}
displayResults={displayResults[0]!.data}
offset={offset}
trackLabelOffset={trackLabelOffset}
/>
<g transform={`translate(0 ${offset})`}>
<SVGTracks
textHeight={textHeight}
trackLabels={trackLabels}
fontSize={fontSize}
model={displayResults[0]!.view}
displayResults={displayResults[0]!.data}
trackLabelOffset={trackLabelOffset}
/>
</g>
</g>
) : null}

Expand All @@ -110,15 +111,16 @@ export async function renderToSvg(model: BSV, opts: ExportSvgOptions) {
</text>
<SVGRuler model={displayResults[1]!.view} fontSize={fontSize} />
</g>
<SVGTracks
textHeight={textHeight}
trackLabels={trackLabels}
fontSize={fontSize}
model={displayResults[1]!.view}
displayResults={displayResults[1]!.data}
offset={offset}
trackLabelOffset={trackLabelOffset}
/>
<g transform={`translate(0 ${offset})`}>
<SVGTracks
textHeight={textHeight}
trackLabels={trackLabels}
fontSize={fontSize}
model={displayResults[1]!.view}
displayResults={displayResults[1]!.data}
trackLabelOffset={trackLabelOffset}
/>
</g>
</g>
) : null}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,31 @@ export default class MCScanAnchorsAdapter extends BaseFeatureDataAdapter {
}

async hasDataForRefName() {
// determining this properly is basically a call to getFeatures
// so is not really that important, and has to be true or else
// getFeatures is never called (BaseFeatureDataAdapter filters it out)
// determining this properly is basically a call to getFeatures so is not
// really that important, and has to be true or else getFeatures is never
// called (BaseFeatureDataAdapter filters it out)
return true
}

async getRefNames() {
// we cannot determine this accurately
getAssemblyNames() {
const assemblyNames = this.getConf('assemblyNames') as string[]
return assemblyNames
}

async getRefNames(opts: BaseOptions = {}) {
// @ts-expect-error
const r1 = opts.regions?.[0].assemblyName
const { feats } = await this.setup(opts)

const idx = this.getAssemblyNames().indexOf(r1)
if (idx !== -1) {
const set = new Set<string>()
for (const feat of feats) {
set.add(idx === 0 ? feat[0].refName : feat[1].refName)
}
return [...set]
}
console.warn('Unable to do ref renaming on adapter')
return []
}

Expand Down
Loading

0 comments on commit 57059aa

Please sign in to comment.