From e2a12ba9bb15e1802cfe26afa9d69d45888e4616 Mon Sep 17 00:00:00 2001 From: Bruce Martin Date: Thu, 9 Apr 2020 08:13:41 -0600 Subject: [PATCH] diffexp limit UI and configuration (#1336) * warning on maxCount for diffexp * cleanup logging * clarification * make the limits configurable * make diff exp limit work * danger! * remove debugging code * fix merge with master * fix unit tests Co-authored-by: Colin Megill --- .../src/components/menubar/diffexpButtons.js | 176 ++++++++++++++++++ client/src/components/menubar/index.js | 89 +-------- server/common/app_config.py | 34 ++-- server/common/default_config.py | 4 + server/test/test_anndata_adaptor.py | 4 +- server/test/test_nan_anndata_adaptor.py | 4 +- 6 files changed, 200 insertions(+), 111 deletions(-) create mode 100644 client/src/components/menubar/diffexpButtons.js diff --git a/client/src/components/menubar/diffexpButtons.js b/client/src/components/menubar/diffexpButtons.js new file mode 100644 index 00000000..3fab8269 --- /dev/null +++ b/client/src/components/menubar/diffexpButtons.js @@ -0,0 +1,176 @@ +// jshint esversion: 6 +import React from "react"; +import { connect } from "react-redux"; +import { + Popover, + Button, + ButtonGroup, + AnchorButton, + Tooltip, + Position +} from "@blueprintjs/core"; +import * as globals from "../../globals"; +import actions from "../../actions"; +import CellSetButton from "./cellSetButtons"; + +@connect(state => ({ + config: state.config, + crossfilter: state.crossfilter, + differential: state.differential, + celllist1: state.differential?.celllist1, + celllist2: state.differential?.celllist2, + diffexpMayBeSlow: state.config?.parameters?.["diffexp-may-be-slow"] ?? false, + diffexpCellcountMax: state.config?.limits?.diffexp_cellcount_max +})) +class DiffexpButtons extends React.Component { + constructor(props) { + super(props); + this.state = { + userDismissedPopover: false + }; + } + + computeDiffExp = () => { + const { dispatch, differential } = this.props; + if (differential.celllist1 && differential.celllist2) { + dispatch( + actions.requestDifferentialExpression( + differential.celllist1, + differential.celllist2 + ) + ); + } + }; + + clearDifferentialExpression = () => { + const { dispatch, differential } = this.props; + dispatch({ + type: "clear differential expression", + diffExp: differential.diffExp + }); + dispatch({ + type: "clear scatterplot" + }); + }; + + handlePopoverDismiss = () => { + this.setState({ + userDismissedPopover: true + }); + }; + + render() { + /* diffexp-related buttons may be disabled */ + const { differential, diffexpMayBeSlow, diffexpCellcountMax } = this.props; + const { userDismissedPopover } = this.state; + + const haveBothCellSets = + !!differential.celllist1 && !!differential.celllist2; + + const haveEitherCellSet = + !!differential.celllist1 || !!differential.celllist2; + + const slowMsg = diffexpMayBeSlow + ? " (CAUTION: large dataset - may take longer or fail)" + : ""; + const tipMessage = `See top 10 differentially expressed genes${slowMsg}`; + const tipMessageWarn = `The total number of cells for differential expression computation + may not exceed ${diffexpCellcountMax}. Try reselecting new cell sets.`; + + const warnMaxSizeExceeded = + haveEitherCellSet && + !!diffexpCellcountMax && + (differential.celllist1?.length ?? 0) + + (differential.celllist2?.length ?? 0) > + diffexpCellcountMax; + + return ( + + + + {!differential.diffExp ? ( + + + + } + content={ +
+

+ {`The total number of cells for differential expression computation + may not exceed ${diffexpCellcountMax}`} +

+ + +
+ } + /> + ) : null} + + {differential.diffExp ? ( + + + + ) : null} +
+ ); + } +} + +export default DiffexpButtons; diff --git a/client/src/components/menubar/index.js b/client/src/components/menubar/index.js index 52951f7e..2e1e886b 100644 --- a/client/src/components/menubar/index.js +++ b/client/src/components/menubar/index.js @@ -4,12 +4,12 @@ import { connect } from "react-redux"; import { Button, ButtonGroup, AnchorButton, Tooltip } from "@blueprintjs/core"; import * as globals from "../../globals"; import actions from "../../actions"; -import CellSetButton from "./cellSetButtons"; import Clip from "./clip"; import Embedding from "./embedding"; import InformationMenu from "./infoMenu"; import Subset from "./subset"; import UndoRedoReset from "./undoRedo"; +import DiffexpButtons from "./diffexpButtons"; @connect(state => ({ universe: state.universe, @@ -164,29 +164,6 @@ class MenuBar extends React.Component { this.setState({ pendingClipPercentiles: null }); }; - computeDiffExp = () => { - const { dispatch, differential } = this.props; - if (differential.celllist1 && differential.celllist2) { - dispatch( - actions.requestDifferentialExpression( - differential.celllist1, - differential.celllist2 - ) - ); - } - }; - - clearDifferentialExpression = () => { - const { dispatch, differential } = this.props; - dispatch({ - type: "clear differential expression", - diffExp: differential.diffExp - }); - dispatch({ - type: "clear scatterplot" - }); - }; - handleCentroidChange = () => { const { dispatch, showCentroidLabels } = this.props; @@ -209,71 +186,11 @@ class MenuBar extends React.Component { return world.nObs !== universe.nObs; }; - renderDiffExp() { - /* diffexp-related buttons may be disabled */ - const { disableDiffexp, differential, diffexpMayBeSlow } = this.props; - if (disableDiffexp) return null; - - const haveBothCellSets = - !!differential.celllist1 && !!differential.celllist2; - - const slowMsg = diffexpMayBeSlow - ? " (CAUTION: large dataset - may take longer or fail)" - : ""; - const tipMessage = `See top 10 differentially expressed genes${slowMsg}`; - - return ( - - - - {!differential.diffExp ? ( - - - - ) : null} - - {differential.diffExp ? ( - - - - ) : null} - - ); - } - render() { const { dispatch, libraryVersions, + disableDiffexp, undoDisabled, redoDisabled, selectionTool, @@ -307,7 +224,7 @@ class MenuBar extends React.Component { display: "flex" }} > - {this.renderDiffExp()} + {disableDiffexp ? null : } limit_value diff --git a/server/common/default_config.py b/server/common/default_config.py index 635bbd0f..1b3c8683 100644 --- a/server/common/default_config.py +++ b/server/common/default_config.py @@ -80,6 +80,10 @@ adaptor: anndata_adaptor: backed: false +limits: + column_request_max: 32 + diffexp_cellcount_max: null + """ diff --git a/server/test/test_anndata_adaptor.py b/server/test/test_anndata_adaptor.py index 12d73e47..cc069853 100644 --- a/server/test/test_anndata_adaptor.py +++ b/server/test/test_anndata_adaptor.py @@ -41,11 +41,11 @@ class AdaptorTest(unittest.TestCase): "diffexp__lfc_cutoff": 0.01, "adaptor__anndata_adaptor__backed": self.backed, "single_dataset__datapath": self.data_locator, + "limits__diffexp_cellcount_max": None, + "limits__column_request_max": None } config = AppConfig() config.update(**args) - for k in config.limits.keys(): - config.limits[k] = None config.complete_config() self.data = AnndataAdaptor(DataLocator(self.data_locator), config) diff --git a/server/test/test_nan_anndata_adaptor.py b/server/test/test_nan_anndata_adaptor.py index 861c2776..8c37b7d6 100644 --- a/server/test/test_nan_anndata_adaptor.py +++ b/server/test/test_nan_anndata_adaptor.py @@ -19,13 +19,13 @@ class NaNTest(unittest.TestCase): "single_dataset__obs_names": None, "single_dataset__var_names": None, "diffexp__lfc_cutoff": 0.01, + "limits__diffexp_cellcount_max": None, + "limits__column_request_max": None, } config = AppConfig() config.update(**self.args) locator = DataLocator("test/test_datasets/nan.h5ad") config.update(single_dataset__datapath=locator.path) - for k in config.limits.keys(): - config.limits[k] = None config.complete_config() with warnings.catch_warnings():