From 5bdacc6151bb16a27fc9ca3b8e96d59c3bd027fa Mon Sep 17 00:00:00 2001 From: Karen Hausman <74921039+hausman-gdit@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:24:59 -0400 Subject: [PATCH] 334 output refactoring (#14) * merge GOF * gof tweak, infotable merge * modal first pass * tweak imports * merge summary, fix import * remove lollipop * fix after merge w/ floating point hover * wip * updates * fix test failures and warnings * use CamelCase * version updates * remove multitumor analysis of deviance * add cdf plot to individual multitumor models * remove special multitumor components * use new component * add ruff check to windows batch file * make summary results consistent across types * remove duplicative parameter components * revert to psycopg2 --------- Co-authored-by: Andy Shapiro --- Makefile | 4 +- bmds_server/analysis/api.py | 3 + bmds_server/common/templatetags/bs4.py | 1 + frontend/package.json | 4 +- .../IndividualModel/ContinuousSummary.js | 75 -------- .../IndividualModel/DichotomousSummary.js | 78 --------- .../components/IndividualModel/GoodnessFit.js | 36 ++-- .../components/IndividualModel/InfoTable.js | 4 +- .../IndividualModel/ModelDetailModal.js | 165 ++++++++++++++++-- .../IndividualModel/ModelParameters.js | 114 ++++++------ .../src/components/IndividualModel/Summary.js | 66 +++++++ .../Output/Multitumor/AnalysisOfDeviance.js | 57 ------ .../Output/Multitumor/DatasetTable.js | 37 +--- .../Output/Multitumor/GoodnessFit.js | 98 ----------- .../components/Output/Multitumor/InfoTable.js | 26 --- .../components/Output/Multitumor/ModalBody.js | 79 --------- .../Output/Multitumor/ModelOptions.js | 30 ---- .../Output/Multitumor/MultitumorPlot.js | 10 +- .../Output/Multitumor/ParameterSettings.js | 44 ----- .../Output/Multitumor/ResultTable.js | 21 ++- .../components/Output/Multitumor/Summary.js | 36 ---- .../Output/NestedDichotomous/BootstrapRuns.js | 79 ++++----- .../Output/NestedDichotomous/LitterData.js | 73 +++----- .../Output/NestedDichotomous/ModalBody.js | 65 ------- .../NestedDichotomous/ModelParameters.js | 37 ---- .../Output/NestedDichotomous/Summary.js | 39 ----- frontend/src/components/Output/Output.js | 23 +-- .../components/common/FloatingPointHover.js | 2 +- frontend/src/components/common/Table.js | 63 +++++++ frontend/src/constants/modelConstants.js | 8 +- frontend/src/constants/plotting.js | 34 ---- frontend/src/stores/OutputStore.js | 54 +----- make.bat | 8 +- manage.py | 1 + pyproject.toml | 6 +- requirements/base.txt | 22 +-- requirements/dev.txt | 22 +-- setup.cfg | 23 +-- tests/analysis/test_api.py | 2 +- tests/analysis/test_models.py | 1 - 40 files changed, 498 insertions(+), 1052 deletions(-) delete mode 100644 frontend/src/components/IndividualModel/ContinuousSummary.js delete mode 100644 frontend/src/components/IndividualModel/DichotomousSummary.js create mode 100644 frontend/src/components/IndividualModel/Summary.js delete mode 100644 frontend/src/components/Output/Multitumor/AnalysisOfDeviance.js delete mode 100644 frontend/src/components/Output/Multitumor/GoodnessFit.js delete mode 100644 frontend/src/components/Output/Multitumor/InfoTable.js delete mode 100644 frontend/src/components/Output/Multitumor/ModalBody.js delete mode 100644 frontend/src/components/Output/Multitumor/ModelOptions.js delete mode 100644 frontend/src/components/Output/Multitumor/ParameterSettings.js delete mode 100644 frontend/src/components/Output/Multitumor/Summary.js delete mode 100644 frontend/src/components/Output/NestedDichotomous/ModalBody.js delete mode 100644 frontend/src/components/Output/NestedDichotomous/ModelParameters.js delete mode 100644 frontend/src/components/Output/NestedDichotomous/Summary.js create mode 100644 frontend/src/components/common/Table.js diff --git a/Makefile b/Makefile index cd051965..7335f24b 100644 --- a/Makefile +++ b/Makefile @@ -80,10 +80,10 @@ lint: lint-py lint-js ## Check formatting issues format: format-py format-js ## Fix formatting issues where possible lint-py: ## Check python formatting issues - @ruff format . --check && ruff . + @ruff format . --check && ruff check . format-py: ## Fix python formatting issues where possible - @ruff format . && ruff . --fix --show-fixes + @ruff format . && ruff check . --fix --show-fixes lint-js: ## Check javascript formatting issues @npm --prefix ./frontend run lint diff --git a/bmds_server/analysis/api.py b/bmds_server/analysis/api.py index b919e895..9f0012f8 100644 --- a/bmds_server/analysis/api.py +++ b/bmds_server/analysis/api.py @@ -4,6 +4,7 @@ from rest_framework import exceptions, mixins, viewsets from rest_framework.decorators import action from rest_framework.response import Response +from rest_framework.schemas.openapi import AutoSchema from ..common import renderers from ..common.renderers import BinaryFile @@ -19,6 +20,7 @@ class AnalysisViewset(mixins.RetrieveModelMixin, viewsets.GenericViewSet): serializer_class = serializers.AnalysisSerializer queryset = models.Analysis.objects.prefetch_related("collections").all() + schema = AutoSchema(operation_id_base="Analysis") @action(detail=False, url_path="default") def default(self, request, *args, **kwargs): @@ -198,6 +200,7 @@ def collections(self, request, *args, **kwargs): class PolyKViewset(viewsets.GenericViewSet): queryset = models.Analysis.objects.none() serializer_class = UnusedSerializer + schema = AutoSchema(operation_id_base="PolyK") def _run_analysis(self, request) -> PolyKAdjustment: try: diff --git a/bmds_server/common/templatetags/bs4.py b/bmds_server/common/templatetags/bs4.py index 8e63219f..8b12c5bd 100644 --- a/bmds_server/common/templatetags/bs4.py +++ b/bmds_server/common/templatetags/bs4.py @@ -1,6 +1,7 @@ """ Twitter Bootstrap 4 - helper methods """ + from datetime import datetime from textwrap import dedent from uuid import uuid4 diff --git a/frontend/package.json b/frontend/package.json index 5869324f..621499ed 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,7 +46,7 @@ "rimraf": "^2.4.3", "style-loader": "^0.19.0", "webpack": "^5.88.1", - "webpack-bundle-tracker": "^2.0.1", + "webpack-bundle-tracker": "^3.1.0", "webpack-cli": "^5.1.4", "webpack-dev-middleware": "^6.1.1" }, @@ -58,7 +58,7 @@ "lodash": "^4.17.21", "mobx": "^5.15.7", "mobx-react": "^6.3.1", - "plotly.js": "2.27.0", + "plotly.js": "2.30.0", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^17.0.2", diff --git a/frontend/src/components/IndividualModel/ContinuousSummary.js b/frontend/src/components/IndividualModel/ContinuousSummary.js deleted file mode 100644 index c2ca67b2..00000000 --- a/frontend/src/components/IndividualModel/ContinuousSummary.js +++ /dev/null @@ -1,75 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import FloatingPointHover from "@/components/common/FloatingPointHover"; -import {ff, fractionalFormatter} from "@/utils/formatters"; - -@observer -class ContinuousSummary extends Component { - render() { - const {store} = this.props, - results = store.modalModel.results, - p_value = results.tests.p_values[3]; - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modeling Summary
BMD - -
BMDL - -
BMDU - -
AIC - -
-2* Log(Likelihood Ratio){ff(results.fit.loglikelihood)}
- P-Value - {fractionalFormatter(p_value)}
Model d.f.{ff(results.tests.dfs[3])}
- ); - } -} - -ContinuousSummary.propTypes = { - store: PropTypes.object, -}; - -export default ContinuousSummary; diff --git a/frontend/src/components/IndividualModel/DichotomousSummary.js b/frontend/src/components/IndividualModel/DichotomousSummary.js deleted file mode 100644 index 0a9946c5..00000000 --- a/frontend/src/components/IndividualModel/DichotomousSummary.js +++ /dev/null @@ -1,78 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import FloatingPointHover from "@/components/common/FloatingPointHover"; -import {ff, fourDecimalFormatter} from "@/utils/formatters"; - -@observer -class DichotomousSummary extends Component { - render() { - const {store} = this.props, - results = store.modalModel.results; - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Modeling Summary
BMD - -
BMDL - -
BMDU - -
AIC - -
-2* Log(Likelihood Ratio){ff(results.fit.loglikelihood)}
- P-Value - {fourDecimalFormatter(results.gof.p_value)}
Overall d.f.{ff(results.gof.df)}
ChiĀ²{ff(results.fit.chisq)}
- ); - } -} - -DichotomousSummary.propTypes = { - store: PropTypes.object, -}; - -export default DichotomousSummary; diff --git a/frontend/src/components/IndividualModel/GoodnessFit.js b/frontend/src/components/IndividualModel/GoodnessFit.js index b2413c18..ad85b21a 100644 --- a/frontend/src/components/IndividualModel/GoodnessFit.js +++ b/frontend/src/components/IndividualModel/GoodnessFit.js @@ -8,10 +8,8 @@ import {ff} from "@/utils/formatters"; @observer class GoodnessFit extends Component { - getDichotomousData() { - const {store} = this.props, - gof = store.modalModel.results.gof, - dataset = store.selectedDataset; + getDichotomousData(dataset, model) { + const gof = model.results.gof; return { headers: [ "Dose", @@ -35,10 +33,8 @@ class GoodnessFit extends Component { }; } - getContinuousNormalData(dtype) { - const {store} = this.props, - gof = store.modalModel.results.gof, - dataset = store.selectedDataset, + getContinuousNormalData(dataset, dtype, model) { + const gof = model.results.gof, useFF = dtype === Dtype.CONTINUOUS_INDIVIDUAL; return { headers: [ @@ -65,10 +61,8 @@ class GoodnessFit extends Component { }; } - getContinuousLognormalData(dtype) { - const {store} = this.props, - gof = store.modalModel.results.gof, - dataset = store.selectedDataset, + getContinuousLognormalData(dataset, dtype, model) { + const gof = model.results.gof, useFF = dtype === Dtype.CONTINUOUS_INDIVIDUAL; return { headers: [ @@ -98,22 +92,22 @@ class GoodnessFit extends Component { }), }; } + render() { const {store} = this.props, settings = store.modalModel.settings, - dataset = store.selectedDataset, + model = store.modalModel, + dataset = store.isMultiTumor ? store.modalDataset : store.selectedDataset, {dtype} = dataset; - let data; - if (dtype == Dtype.DICHOTOMOUS) { - data = this.getDichotomousData(); + if (store.isMultiTumor || dtype == Dtype.DICHOTOMOUS) { + data = this.getDichotomousData(dataset, model); + } else if (isLognormal(settings.disttype)) { + data = this.getContinuousLognormalData(dataset, dtype, model); } else { - if (isLognormal(settings.disttype)) { - data = this.getContinuousLognormalData(dtype); - } else { - data = this.getContinuousNormalData(dtype); - } + data = this.getContinuousNormalData(dataset, dtype, model); } + return ( diff --git a/frontend/src/components/IndividualModel/InfoTable.js b/frontend/src/components/IndividualModel/InfoTable.js index f6b12af4..fe4ed78a 100644 --- a/frontend/src/components/IndividualModel/InfoTable.js +++ b/frontend/src/components/IndividualModel/InfoTable.js @@ -10,7 +10,9 @@ class InfoTable extends Component { render() { const {outputStore} = this.props, model = outputStore.modalModel, - dataset = outputStore.selectedDataset, + dataset = outputStore.isMultiTumor + ? outputStore.modalDataset + : outputStore.selectedDataset, data = [ ["Dataset", dataset.metadata.name], ["Model", model.name], diff --git a/frontend/src/components/IndividualModel/ModelDetailModal.js b/frontend/src/components/IndividualModel/ModelDetailModal.js index e58e2e78..7936cc34 100644 --- a/frontend/src/components/IndividualModel/ModelDetailModal.js +++ b/frontend/src/components/IndividualModel/ModelDetailModal.js @@ -7,15 +7,17 @@ import * as dc from "@/constants/dataConstants"; import Button from "../common/Button"; import DoseResponsePlot from "../common/DoseResponsePlot"; -import MultitumorModalBody from "../Output/Multitumor/ModalBody"; -import NestedDichotomousModalBody from "../Output/NestedDichotomous/ModalBody"; +import {MsComboInfo, MsComboSummary} from "../Output/Multitumor/MsCombo"; +import MultitumorPlot from "../Output/Multitumor/MultitumorPlot"; +import BootstrapResults from "../Output/NestedDichotomous/BootstrapResults"; +import BootstrapRuns from "../Output/NestedDichotomous/BootstrapRuns"; +import LitterData from "../Output/NestedDichotomous/LitterData"; +import ScaledResidual from "../Output/NestedDichotomous/ScaledResidual"; import CDFPlot from "./CDFPlot"; import CDFTable from "./CDFTable"; import ContinuousDeviance from "./ContinuousDeviance"; -import ContinuousSummary from "./ContinuousSummary"; import ContinuousTestOfInterest from "./ContinuousTestOfInterest"; import DichotomousDeviance from "./DichotomousDeviance"; -import DichotomousSummary from "./DichotomousSummary"; import GoodnessFit from "./GoodnessFit"; import InfoTable from "./InfoTable"; import MaBenchmarkDose from "./MaBenchmarkDose"; @@ -23,7 +25,7 @@ import MaIndividualModels from "./MaIndividualModels"; import ModelOptionsTable from "./ModelOptionsTable"; import ModelParameters from "./ModelParameters"; import ParameterPriorTable from "./ParameterPriorTable"; - +import Summary from "./Summary"; @observer class ModelBody extends Component { render() { @@ -53,8 +55,7 @@ class ModelBody extends Component { - {isDichotomous ? : null} - {isContinuous ? : null} + - + @@ -145,6 +146,150 @@ ModelAverageBody.propTypes = { outputStore: PropTypes.object, }; +@observer +class MtModalBody extends Component { + render() { + const {outputStore} = this.props, + model = outputStore.modalModel, + isSummary = outputStore.drModelModalIsMA, + dataset = outputStore.modalDataset; + + if (isSummary) { + return ( + + + + + + + + + + + + + + ); + } + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } +} +MtModalBody.propTypes = { + outputStore: PropTypes.object, +}; + +@observer +class NdModalBody extends Component { + render() { + const {outputStore} = this.props, + dataset = outputStore.selectedDataset, + dtype = dataset.dtype, + model = outputStore.modalModel, + isSummary = outputStore.drModelModalIsMA; + + if (isSummary) { + return ( + + + + + + + + + + + + + + ); + } + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); + } +} +NdModalBody.propTypes = { + outputStore: PropTypes.object, +}; + @inject("outputStore") @observer class ModelDetailModal extends Component { @@ -155,11 +300,11 @@ class ModelDetailModal extends Component { getBody() { const {outputStore} = this.props; if (outputStore.isMultiTumor) { - return MultitumorModalBody; + return MtModalBody; } else if (outputStore.drModelModalIsMA) { return ModelAverageBody; } else if (outputStore.isNestedDichotomous) { - return NestedDichotomousModalBody; + return NdModalBody; } else { return ModelBody; } diff --git a/frontend/src/components/IndividualModel/ModelParameters.js b/frontend/src/components/IndividualModel/ModelParameters.js index 49dcf060..80d14d7a 100644 --- a/frontend/src/components/IndividualModel/ModelParameters.js +++ b/frontend/src/components/IndividualModel/ModelParameters.js @@ -4,75 +4,65 @@ import PropTypes from "prop-types"; import React, {Component} from "react"; import HelpTextPopover from "@/components/common/HelpTextPopover"; +import Table from "@/components/common/Table"; import {parameterFormatter} from "@/utils/formatters"; -@observer -class ModelParameters extends Component { - render() { - const {parameters} = this.props, +const getData = function(model) { + const parameters = model.results.parameters, indexes = _.range(parameters.names.length), anyBounded = _.sum(parameters.bounded) > 0; - return ( - <> -
- - - - - - - - - - - - - - - - - {indexes.map(i => { - const bounded = parameters.bounded[i]; - return ( - - - - - - ); - })} - -
Model Parameters
VariableEstimateStandard Error
{parameters.names[i]} - {bounded ? ( - <> - On Bound - - - ) : ( - parameterFormatter(parameters.values[i]) - )} - - {bounded - ? "Not Reported" - : parameterFormatter(parameters.se[i])} -
- {anyBounded ? ( -

- Standard errors estimates are not generated for parameters estimated on - corresponding bounds, although sampling error is present for all parameters, - as a rule. Standard error estimates may not be reliable as a basis for - confidence intervals or tests when one or more parameters are on bounds. -

- ) : null} - - ); + return { + headers: ["Variable", "Estimate", "Standard Error"], + subheader: "Model Parameters", + rows: indexes.map(i => { + const bounded = parameters.bounded[i]; + return [ + parameters.names[i], + bounded ? ( + <> + On Bound + + + ) : ( + parameterFormatter(parameters.values[i]) + ), + bounded ? "Not Reported" : parameterFormatter(parameters.se[i]), + ]; + }), + footnotes: anyBounded ? ( +

+ Standard errors estimates are not generated for parameters estimated on + corresponding bounds, although sampling error is present for all parameters, as + a rule. Standard error estimates may not be reliable as a basis for confidence + intervals or tests when one or more parameters are on bounds. +

+ ) : null, + }; + }, + getNestedData = function(model) { + const names = model.results.parameter_names, + values = model.results.parameters; + return { + headers: ["Variable", "Estimate"], + rows: _.range(_.size(names)).map(i => [names[i], values[i]]), + subheader: "Model Parameters", + }; + }; + +@observer +class ModelParameters extends Component { + render() { + const {model, isNestedDichotomous} = this.props, + fn = isNestedDichotomous ? getNestedData : getData; + return ; } } ModelParameters.propTypes = { - parameters: PropTypes.object.isRequired, + isNestedDichotomous: PropTypes.bool.isRequired, + model: PropTypes.object.isRequired, }; export default ModelParameters; diff --git a/frontend/src/components/IndividualModel/Summary.js b/frontend/src/components/IndividualModel/Summary.js new file mode 100644 index 00000000..e57d1f82 --- /dev/null +++ b/frontend/src/components/IndividualModel/Summary.js @@ -0,0 +1,66 @@ +import {inject, observer} from "mobx-react"; +import PropTypes from "prop-types"; +import React, {Component} from "react"; + +import TwoColumnTable from "@/components/common/TwoColumnTable"; +import * as mc from "@/constants/mainConstants"; +import {ff, fractionalFormatter} from "@/utils/formatters"; + +@inject("outputStore") +@observer +class Summary extends Component { + render() { + const {outputStore} = this.props, + model = outputStore.modalModel; + let data; + if (outputStore.isNestedDichotomous) { + data = [ + ["BMD", ff(model.results.bmd), model.results.bmd], + ["BMDL", ff(model.results.summary.bmdl), model.results.summary.bmdl], + ["BMDU", ff(model.results.summary.bmdu), model.results.summary.bmdu], + ["AIC", ff(model.results.summary.aic), model.results.summary.aic], + [ + + P-value + , + fractionalFormatter(model.results.combined_pvalue), + ], + ["Model d.f.", ff(model.results.dof)], + [ + + Chi2 + , + ff(model.results.summary.chi_squared), + ], + ]; + } else { + const isContinuous = outputStore.getModelType === mc.MODEL_CONTINUOUS, + results = model.bmd ? model : model.results, + p_value = isContinuous ? results.tests.p_values[3] : results.gof.p_value, + df = isContinuous ? results.tests.p_values[3] : results.gof.df; + data = [ + ["BMD", ff(results.bmd), results.bmd], + ["BMDL", ff(results.bmdl), results.bmdl], + ["BMDU", ff(results.bmdu), results.bmdu], + ["AIC", ff(results.fit.aic), results.fit.aic], + ["-2* Log(Likelihood Ratio)", ff(results.fit.loglikelihood)], + [ + + P-value + , + fractionalFormatter(p_value), + ], + ["Model d.f.", ff(df)], + ]; + if (outputStore.isMultiTumor) { + data.splice(3, 0, ["Slope Factor", ff(results.slope_factor), results.slope_factor]); + } + } + + return ; + } +} +Summary.propTypes = { + outputStore: PropTypes.object, +}; +export default Summary; diff --git a/frontend/src/components/Output/Multitumor/AnalysisOfDeviance.js b/frontend/src/components/Output/Multitumor/AnalysisOfDeviance.js deleted file mode 100644 index 90435817..00000000 --- a/frontend/src/components/Output/Multitumor/AnalysisOfDeviance.js +++ /dev/null @@ -1,57 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import {ff, fractionalFormatter} from "@/utils/formatters"; - -@observer -class AnalysisOfDeviance extends Component { - render() { - const deviances = this.props.model.deviance; - return ( -
- - - - - - - - - - - - - - - - - - - - - - - {deviances.names.map((name, i) => { - return ( - - - - - - - - - ); - })} - -
Analysis of Deviance
Model-2* Log(Likelihood Ratio)# ParametersDevianceTest d.f. - P-Value -
{name}{ff(deviances.ll[i])}{deviances.params[i]}{ff(deviances.deviance[i])}{ff(deviances.df[i])}{fractionalFormatter(deviances.p_value[i])}
- ); - } -} -AnalysisOfDeviance.propTypes = { - model: PropTypes.object, -}; -export default AnalysisOfDeviance; diff --git a/frontend/src/components/Output/Multitumor/DatasetTable.js b/frontend/src/components/Output/Multitumor/DatasetTable.js index d6da764e..193ae638 100644 --- a/frontend/src/components/Output/Multitumor/DatasetTable.js +++ b/frontend/src/components/Output/Multitumor/DatasetTable.js @@ -3,6 +3,8 @@ import {inject, observer} from "mobx-react"; import PropTypes from "prop-types"; import React, {Component} from "react"; +import Table from "@/components/common/Table"; + const getData = datasets => { const headers = ["Dose"], rows = [], @@ -26,11 +28,7 @@ const getData = datasets => { } }); }); - return { - headers, - colWidths: _.fill(Array(headers.length), `${Math.round(100 / headers.length)}%`), - rows, - }; + return {headers, rows}; }; @inject("outputStore") @@ -43,34 +41,7 @@ class DatasetTable extends Component { if (!selectedFrequentist) { return null; } - const {multitumorDatasets} = store, - data = getData(multitumorDatasets); - - return ( - - - {_.map(data.colWidths).map((value, idx) => ( - - ))} - - - - {data.headers.map((text, idx) => ( - - ))} - - - - {data.rows.map((rows, idx) => ( - - {rows.map((text, idx) => ( - - ))} - - ))} - -
{text}
{text}
- ); + return ; } } DatasetTable.propTypes = { diff --git a/frontend/src/components/Output/Multitumor/GoodnessFit.js b/frontend/src/components/Output/Multitumor/GoodnessFit.js deleted file mode 100644 index d896efcb..00000000 --- a/frontend/src/components/Output/Multitumor/GoodnessFit.js +++ /dev/null @@ -1,98 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import {Dtype} from "@/constants/dataConstants"; -import {isLognormal} from "@/constants/modelConstants"; -import {ff} from "@/utils/formatters"; - -/* eslint-disable */ -const hdr_c_normal = [ - "Dose", "Size", "Observed Mean", "Calculated Mean", "Estimated Mean", - "Observed SD", "Calculated SD", "Estimated SD", "Scaled Residual", - ], - hdr_c_lognormal = [ - "Dose", "Size", "Observed Mean", "Calculated Median", "Estimated Median", - "Observed SD", "Calculated GSD", "Estimated GSD", "Scaled Residual", - ], - hdr_d = [ "Dose", "Size", "Observed", "Expected", "Estimated Probability", "Scaled Residual"]; -/* eslint-enable */ - -@observer -class GoodnessFit extends Component { - getHeaders(dtype, settings) { - if (dtype == Dtype.CONTINUOUS || dtype == Dtype.CONTINUOUS_INDIVIDUAL) { - const headers = isLognormal(settings.disttype) ? hdr_c_lognormal : hdr_c_normal; - return [headers, [10, 10, 10, 12, 12, 12, 10, 12, 12]]; - } - if (dtype == Dtype.DICHOTOMOUS) { - return [hdr_d, [17, 16, 16, 17, 17, 17]]; - } - throw Error("Unknown dtype"); - } - render() { - const {store} = this.props, - settings = store.modalModel.settings, - gof = store.modalModel.gof, - dataset = store.modalDataset, - {dtype} = dataset, - headers = this.getHeaders(dtype, settings); - return ( -
- - {headers[1].map((d, i) => ( - - ))} - - - - - - - {headers[0].map((d, i) => ( - - ))} - - - - {dtype == Dtype.CONTINUOUS || dtype == Dtype.CONTINUOUS_INDIVIDUAL - ? gof.dose.map((item, i) => { - const useFF = dtype === Dtype.CONTINUOUS_INDIVIDUAL; - return ( - - - - - - - - - - - - ); - }) - : null} - {dtype == Dtype.DICHOTOMOUS - ? dataset.doses.map((dose, i) => { - return ( - - - - - - - - - ); - }) - : null} - -
Goodness of Fit
{d}
{item}{gof.size[i]}{useFF ? ff(gof.obs_mean[i]) : gof.obs_mean[i]}{ff(gof.calc_mean[i])}{ff(gof.est_mean[i])}{useFF ? ff(gof.obs_sd[i]) : gof.obs_sd[i]}{ff(gof.calc_sd[i])}{ff(gof.est_sd[i])}{ff(gof.residual[i])}
{dose}{dataset.ns[i]}{dataset.incidences[i]}{ff(gof.expected[i])}{ff(gof.expected[i] / dataset.ns[i])}{ff(gof.residual[i])}
- ); - } -} -GoodnessFit.propTypes = { - store: PropTypes.object, -}; -export default GoodnessFit; diff --git a/frontend/src/components/Output/Multitumor/InfoTable.js b/frontend/src/components/Output/Multitumor/InfoTable.js deleted file mode 100644 index 2e205aad..00000000 --- a/frontend/src/components/Output/Multitumor/InfoTable.js +++ /dev/null @@ -1,26 +0,0 @@ -import {inject, observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import TwoColumnTable from "@/components/common/TwoColumnTable"; -import {getNameFromDegrees} from "@/constants/modelConstants"; - -@inject("outputStore") -@observer -class InfoTable extends Component { - render() { - const {outputStore} = this.props, - model = outputStore.modalModel, - dataset = outputStore.modalDataset, - data = [ - ["Dataset", dataset.metadata.name], - ["Model", getNameFromDegrees(model)], - ["Equation", "P[dose] = g + (1 - g) * (1 - exp(-b1 * dose^1 - b2 * dose^2 - ...))"], - ]; - return ; - } -} -InfoTable.propTypes = { - outputStore: PropTypes.object, -}; -export default InfoTable; diff --git a/frontend/src/components/Output/Multitumor/ModalBody.js b/frontend/src/components/Output/Multitumor/ModalBody.js deleted file mode 100644 index 50dd9484..00000000 --- a/frontend/src/components/Output/Multitumor/ModalBody.js +++ /dev/null @@ -1,79 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; -import {Col, Modal, Row} from "react-bootstrap"; - -import DoseResponsePlot from "../../common/DoseResponsePlot"; -import ModelParameters from "../../IndividualModel/ModelParameters"; -import AnalysisOfDeviance from "./AnalysisOfDeviance"; -import GoodnessFit from "./GoodnessFit"; -import InfoTable from "./InfoTable"; -import ModelOptions from "./ModelOptions"; -import {MsComboInfo, MsComboSummary} from "./MsCombo"; -import MultitumorPlot from "./MultitumorPlot"; -import ParameterSettings from "./ParameterSettings"; -import Summary from "./Summary"; - -@observer -class ModalBody extends Component { - render() { - const {outputStore} = this.props, - model = outputStore.modalModel, - isSummary = outputStore.drModelModalIsMA; - - if (isSummary) { - return ( - - - - - - - - - - - - - - ); - } - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } -} -ModalBody.propTypes = { - outputStore: PropTypes.object, -}; -export default ModalBody; diff --git a/frontend/src/components/Output/Multitumor/ModelOptions.js b/frontend/src/components/Output/Multitumor/ModelOptions.js deleted file mode 100644 index 85a34715..00000000 --- a/frontend/src/components/Output/Multitumor/ModelOptions.js +++ /dev/null @@ -1,30 +0,0 @@ -import {inject, observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import {getLabel} from "@/common"; -import TwoColumnTable from "@/components/common/TwoColumnTable"; -import {dichotomousBmrOptions} from "@/constants/optionsConstants"; -import {ff} from "@/utils/formatters"; - -@inject("outputStore") -@observer -class ModelOptions extends Component { - render() { - const {outputStore} = this.props, - options = outputStore.selectedModelOptions, - {modalOptionSet} = outputStore, - degree = modalOptionSet.degree == 0 ? "auto" : modalOptionSet.degree, - data = [ - ["Risk Type", getLabel(options.bmr_type, dichotomousBmrOptions)], - ["BMR", ff(options.bmr_value)], - ["Confidence Level (one sided)", ff(options.confidence_level)], - ["Degree", degree], - ]; - return ; - } -} -ModelOptions.propTypes = { - outputStore: PropTypes.object, -}; -export default ModelOptions; diff --git a/frontend/src/components/Output/Multitumor/MultitumorPlot.js b/frontend/src/components/Output/Multitumor/MultitumorPlot.js index 00533270..6948e24f 100644 --- a/frontend/src/components/Output/Multitumor/MultitumorPlot.js +++ b/frontend/src/components/Output/Multitumor/MultitumorPlot.js @@ -3,7 +3,6 @@ import PropTypes from "prop-types"; import React, {Component} from "react"; import DoseResponsePlot from "@/components/common/DoseResponsePlot"; -import {getNameFromDegrees} from "@/constants/modelConstants"; import {colorCodes, getBmdDiamond, getDrLayout, getResponse} from "@/constants/plotting"; const getLayout = function(datasets) { @@ -49,16 +48,17 @@ const getData = function(ma, datasets, models) { // add selected models models.forEach((model, index) => { - let dataset = datasets[index]; + const dataset = datasets[index], + {results} = model; data.push({ - x: model.plotting.dr_x, - y: model.plotting.dr_y, + x: results.plotting.dr_x, + y: results.plotting.dr_y, line: { width: 3, color: colorCodes[index], }, legendgroup: index, - name: `${dataset.metadata.name} ${getNameFromDegrees(model)}`, + name: `${dataset.metadata.name} ${model.name}`, }); }); diff --git a/frontend/src/components/Output/Multitumor/ParameterSettings.js b/frontend/src/components/Output/Multitumor/ParameterSettings.js deleted file mode 100644 index 8082ab2c..00000000 --- a/frontend/src/components/Output/Multitumor/ParameterSettings.js +++ /dev/null @@ -1,44 +0,0 @@ -import _ from "lodash"; -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -@observer -class ParameterSettings extends Component { - render() { - const model = this.props.model.parameters, - temp_rslt_len = _.size(model.names); - return ( - - - - - - - - - - - - - - {_.range(temp_rslt_len).map(i => { - return ( - - - - - - - ); - })} - -
Parameter Settings
ParameterInitialMinMax
{model.names[i]}{model.prior_initial_value[i]}{model.prior_min_value[i]}{model.prior_max_value[i]}
- ); - } -} -ParameterSettings.propTypes = { - model: PropTypes.object, -}; - -export default ParameterSettings; diff --git a/frontend/src/components/Output/Multitumor/ResultTable.js b/frontend/src/components/Output/Multitumor/ResultTable.js index fadb0e85..474d1ad7 100644 --- a/frontend/src/components/Output/Multitumor/ResultTable.js +++ b/frontend/src/components/Output/Multitumor/ResultTable.js @@ -4,7 +4,6 @@ import PropTypes from "prop-types"; import React, {Component} from "react"; import FloatingPointHover from "@/components/common/FloatingPointHover"; -import {getNameFromDegrees} from "@/constants/modelConstants"; import {ff} from "@/utils/formatters"; @inject("outputStore") @@ -84,8 +83,8 @@ class ResultTable extends Component { rows = _.flatten( dataset_models.map((model, model_index) => { const key = `${dataset_index}-${model_index}`, - name = getNameFromDegrees(model), - selected = indexes[dataset_index] === model_index; + selected = indexes[dataset_index] === model_index, + {results} = model; return ( @@ -99,7 +98,7 @@ class ResultTable extends Component { model_index ); }}> - {name} + {model.name} {selected ? "*" : ""} @@ -108,20 +107,20 @@ class ResultTable extends Component { - + - + - + - {ff(model.gof.p_value)} + {ff(results.gof.p_value)} - + - {ff(model.gof.residual[0])} - {ff(model.gof.roi)} + {ff(results.gof.residual[0])} + {ff(results.gof.roi)} ); }) diff --git a/frontend/src/components/Output/Multitumor/Summary.js b/frontend/src/components/Output/Multitumor/Summary.js deleted file mode 100644 index 0dabc918..00000000 --- a/frontend/src/components/Output/Multitumor/Summary.js +++ /dev/null @@ -1,36 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import TwoColumnTable from "@/components/common/TwoColumnTable"; -import {ff, fourDecimalFormatter} from "@/utils/formatters"; - -@observer -class Summary extends Component { - render() { - const {model} = this.props, - data = [ - ["BMD", ff(model.bmd), model.bmd], - ["BMDL", ff(model.bmdl), model.bmdl], - ["BMDU", ff(model.bmdu), model.bmdu], - ["Slope Factor", ff(model.slope_factor), model.slope_factor], - ["AIC", ff(model.fit.aic), model.fit.aic], - [ - - P-Value - , - fourDecimalFormatter(model.gof.p_value), - ], - ["Overall d.f.", ff(model.gof.df)], - ["ChiĀ²", ff(model.fit.chisq)], - ["-2* Log(Likelihood Ratio)", ff(model.fit.loglikelihood)], - ]; - return ; - } -} - -Summary.propTypes = { - model: PropTypes.object, -}; - -export default Summary; diff --git a/frontend/src/components/Output/NestedDichotomous/BootstrapRuns.js b/frontend/src/components/Output/NestedDichotomous/BootstrapRuns.js index 807a2c14..cecd54ab 100644 --- a/frontend/src/components/Output/NestedDichotomous/BootstrapRuns.js +++ b/frontend/src/components/Output/NestedDichotomous/BootstrapRuns.js @@ -3,53 +3,48 @@ import {observer} from "mobx-react"; import PropTypes from "prop-types"; import React, {Component} from "react"; +import Table from "@/components/common/Table"; import {ff} from "@/utils/formatters"; +const getData = function(model) { + const results = model.results.bootstrap, + rows = _.range(results.n_runs).map(run => [ + run + 1, + results.p_value[run], + ff(results.p50[run]), + ff(results.p90[run]), + ff(results.p95[run]), + ff(results.p99[run]), + ]); + rows.push([ + "Combined", + results.p_value[3], + ff(results.p50[3]), + ff(results.p90[3]), + ff(results.p95[3]), + ff(results.p99[3]), + ]); + + return { + headers: [ + "Run", + + P-Value + , + "50th", + "90th", + "95th", + "99th", + ], + subheader: "Bootstrap Runs", + rows, + }; +}; + @observer class BootstrapRuns extends Component { render() { - const results = this.props.model.results.bootstrap; - return ( - - - - - - - - - - - - - - - - {_.range(results.n_runs).map(run => { - return ( - - - - - - - - - ); - })} - - - - - - - - - -
Bootstrap Runs
Run - P-Value - 50th90th95th99th
{run + 1}{results.p_value[run]}{ff(results.p50[run])}{ff(results.p90[run])}{ff(results.p95[run])}{ff(results.p99[run])}
Combined{results.p_value[3]}{ff(results.p50[3])}{ff(results.p90[3])}{ff(results.p95[3])}{ff(results.p99[3])}
- ); + return ; } } BootstrapRuns.propTypes = { diff --git a/frontend/src/components/Output/NestedDichotomous/LitterData.js b/frontend/src/components/Output/NestedDichotomous/LitterData.js index fdc5ebc5..767720a1 100644 --- a/frontend/src/components/Output/NestedDichotomous/LitterData.js +++ b/frontend/src/components/Output/NestedDichotomous/LitterData.js @@ -3,55 +3,40 @@ import {observer} from "mobx-react"; import PropTypes from "prop-types"; import React, {Component} from "react"; +import Table from "@/components/common/Table"; import {ff} from "@/utils/formatters"; +const getData = function(model) { + const litter = model.results.litter, + n = _.size(litter.lsc); + return { + headers: [ + "Dose", + "Litter Specific Covariance", + "Estimated Probability", + "Liter Size", + "Expected", + "Observed", + "Scaled Residual", + ], + colWidths: [14, 15, 14, 14, 14, 14, 15], + subheader: "Litter Data", + rows: _.range(n).map(i => [ + litter.dose[i], + litter.lsc[i], + ff(litter.estimated_probabilities[i]), + litter.litter_size[i], + ff(litter.expected[i]), + litter.observed[i], + ff(litter.scaled_residuals[i]), + ]), + }; +}; + @observer class LitterData extends Component { render() { - const litter = this.props.model.results.litter, - n = _.size(litter.lsc); - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - {_.range(n).map(i => { - return ( - - - - - - - - - - ); - })} - -
Litter Data
DoseLitter Specific CovarianceEstimated ProbabilityLiter SizeExpectedObservedScaled Residual
{litter.dose[i]}{litter.lsc[i]}{ff(litter.estimated_probabilities[i])}{litter.litter_size[i]}{ff(litter.expected[i])}{litter.observed[i]}{ff(litter.scaled_residuals[i])}
- ); + return ; } } LitterData.propTypes = { diff --git a/frontend/src/components/Output/NestedDichotomous/ModalBody.js b/frontend/src/components/Output/NestedDichotomous/ModalBody.js deleted file mode 100644 index d778150b..00000000 --- a/frontend/src/components/Output/NestedDichotomous/ModalBody.js +++ /dev/null @@ -1,65 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; -import {Col, Modal, Row} from "react-bootstrap"; - -import DoseResponsePlot from "../../common/DoseResponsePlot"; -import InfoTable from "../../IndividualModel/InfoTable"; -import ModelOptionsTable from "../../IndividualModel/ModelOptionsTable"; -import BootstrapResults from "./BootstrapResults"; -import BootstrapRuns from "./BootstrapRuns"; -import LitterData from "./LitterData"; -import ModelParameters from "./ModelParameters"; -import ScaledResidual from "./ScaledResidual"; -import Summary from "./Summary"; - -@observer -class ModalBody extends Component { - render() { - const {outputStore} = this.props, - dataset = outputStore.selectedDataset, - dtype = dataset.dtype, - model = outputStore.modalModel; - - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); - } -} -ModalBody.propTypes = { - outputStore: PropTypes.object, -}; -export default ModalBody; diff --git a/frontend/src/components/Output/NestedDichotomous/ModelParameters.js b/frontend/src/components/Output/NestedDichotomous/ModelParameters.js deleted file mode 100644 index c5c31a6a..00000000 --- a/frontend/src/components/Output/NestedDichotomous/ModelParameters.js +++ /dev/null @@ -1,37 +0,0 @@ -import _ from "lodash"; -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -@observer -class ModelParameters extends Component { - render() { - const names = this.props.model.results.parameter_names, - values = this.props.model.results.parameters; - - return ( -
- - - - - - - {_.range(_.size(names)).map(i => { - return ( - - - - - ); - })} - -
Model Parameters
{names[i]}{values[i]}
- ); - } -} -ModelParameters.propTypes = { - model: PropTypes.object, -}; - -export default ModelParameters; diff --git a/frontend/src/components/Output/NestedDichotomous/Summary.js b/frontend/src/components/Output/NestedDichotomous/Summary.js deleted file mode 100644 index 0d36f7b4..00000000 --- a/frontend/src/components/Output/NestedDichotomous/Summary.js +++ /dev/null @@ -1,39 +0,0 @@ -import {observer} from "mobx-react"; -import PropTypes from "prop-types"; -import React, {Component} from "react"; - -import TwoColumnTable from "@/components/common/TwoColumnTable"; -import {ff} from "@/utils/formatters"; - -@observer -class Summary extends Component { - render() { - const {results} = this.props, - data = [ - ["BMD", ff(results.summary.bmd), results.summary.bmd], - ["BMDL", ff(results.summary.bmdl), results.summary.bmdl], - ["BMDU", ff(results.summary.bmdu), results.summary.bmdu], - ["AIC", ff(results.summary.aic), results.summary.aic], - [ - - P-value - , - ff(results.combined_pvalue), - ], - ["d.f.", ff(results.dof)], - [ - - Chi2 - , - ff(results.summary.chi_squared), - ], - ]; - return ; - } -} -Summary.propTypes = { - model: PropTypes.object, - results: PropTypes.object.isRequired, -}; - -export default Summary; diff --git a/frontend/src/components/Output/Output.js b/frontend/src/components/Output/Output.js index f87c1085..52e443e8 100644 --- a/frontend/src/components/Output/Output.js +++ b/frontend/src/components/Output/Output.js @@ -46,7 +46,7 @@ class Output extends Component { selectedFrequentist, selectedBayesian, } = outputStore, - {isFuture, analysisSavedAndValidated} = outputStore.rootStore.mainStore; + {analysisSavedAndValidated} = outputStore.rootStore.mainStore; if (hasAnyError) { return ( @@ -149,27 +149,6 @@ class Output extends Component { ) : null} - {isFuture && !outputStore.isMultiTumor ? ( -
- {selectedFrequentist ? ( -
- -
- ) : null} - {selectedBayesian ? ( -
- -
- ) : null} -
- ) : null} -
{outputStore.showModelModal ? : null}
); diff --git a/frontend/src/components/common/FloatingPointHover.js b/frontend/src/components/common/FloatingPointHover.js index 25bb89a6..e07feedc 100644 --- a/frontend/src/components/common/FloatingPointHover.js +++ b/frontend/src/components/common/FloatingPointHover.js @@ -9,7 +9,7 @@ class FloatingPointHover extends Component { render() { const {value} = this.props; return ( - + {ff(value)} ); diff --git a/frontend/src/components/common/Table.js b/frontend/src/components/common/Table.js new file mode 100644 index 00000000..3bbccee1 --- /dev/null +++ b/frontend/src/components/common/Table.js @@ -0,0 +1,63 @@ +import _ from "lodash"; +import {observer} from "mobx-react"; +import PropTypes from "prop-types"; +import React, {Component} from "react"; + +@observer +class Table extends Component { + render() { + const {data} = this.props, + nCols = data.headers.length; + if (_.isUndefined(data.colWidths)) { + data.colWidths = _.fill(Array(nCols), Math.round(100 / nCols)); + } + return ( + + + {_.map(data.colWidths).map((value, idx) => ( + + ))} + + + {data.subheader ? ( + + + + ) : null} + + {data.headers.map((text, idx) => ( + + ))} + + + + {data.rows.map((rows, idx) => ( + + {rows.map((text, idx) => ( + + ))} + + ))} + + {data.footnotes ? ( + + + + + + ) : null} +
{data.subheader}
{text}
{text}
{data.footnotes}
+ ); + } +} +Table.propTypes = { + data: PropTypes.shape({ + headers: PropTypes.array.isRequired, + rows: PropTypes.arrayOf(PropTypes.array).isRequired, + subheader: PropTypes.string, + colWidths: PropTypes.arrayOf(PropTypes.number), + footnotes: PropTypes.node, + }), +}; + +export default Table; diff --git a/frontend/src/constants/modelConstants.js b/frontend/src/constants/modelConstants.js index 4fda10c1..9934d300 100644 --- a/frontend/src/constants/modelConstants.js +++ b/frontend/src/constants/modelConstants.js @@ -75,10 +75,6 @@ const modelsList = { isLognormal = function(disttype) { return disttype == 3; }, - hasDegrees = new Set(["Multistage", "Polynomial"]), - getNameFromDegrees = function(model) { - const degree = model.parameters.names.length - 1; - return `Multistage ${degree}`; - }; + hasDegrees = new Set(["Multistage", "Polynomial"]); -export {allModelOptions, getNameFromDegrees, hasDegrees, isLognormal, models, modelsList}; +export {allModelOptions, hasDegrees, isLognormal, models, modelsList}; diff --git a/frontend/src/constants/plotting.js b/frontend/src/constants/plotting.js index 014695e0..745f972c 100644 --- a/frontend/src/constants/plotting.js +++ b/frontend/src/constants/plotting.js @@ -238,40 +238,6 @@ export const getResponse = dataset => { return data; }, - getLollipopDataset = function(dataArray, modelArray, modelName) { - return { - x: dataArray, - y: modelArray, - mode: "line", - type: "scatter", - line: { - width: 5, - color: "#696969", - }, - marker: { - size: 10, - color: ["#FFFFFF", "#0000FF", "#FFFFFF"], - }, - name: modelName, - }; - }, - getLollipopPlotLayout = function(title, dataset) { - const layout = { - title: { - text: title, - }, - margin: {l: 100, r: 5, t: 35, b: 65}, - showlegend: false, - xaxis: { - showline: true, - title: getDoseLabel(dataset), - }, - yaxis: { - showline: true, - }, - }; - return layout; - }, getConfig = function() { return { modeBarButtonsToRemove: [ diff --git a/frontend/src/stores/OutputStore.js b/frontend/src/stores/OutputStore.js index 303cb9e7..f0cdc964 100644 --- a/frontend/src/stores/OutputStore.js +++ b/frontend/src/stores/OutputStore.js @@ -3,7 +3,6 @@ import {action, computed, observable, toJS} from "mobx"; import {getHeaders} from "@/common"; import {MODEL_MULTI_TUMOR, MODEL_NESTED_DICHOTOMOUS} from "@/constants/mainConstants"; -import {getNameFromDegrees} from "@/constants/modelConstants"; import {maIndex, modelClasses} from "@/constants/outputConstants"; import { bmaColor, @@ -12,8 +11,6 @@ import { getDrBmdLine, getDrDatasetPlotData, getDrLayout, - getLollipopDataset, - getLollipopPlotLayout, hoverColor, selectedColor, } from "@/constants/plotting"; @@ -194,7 +191,7 @@ class OutputStore { return "MS Combo"; } else { const dataset = this.modalDataset; - return `${dataset.metadata.name} - ${getNameFromDegrees(model)}`; + return `${dataset.metadata.name} - ${model.name}`; } } else { return this.drModelModalIsMA ? "Model Average" : model.name; @@ -360,13 +357,8 @@ class OutputStore { @computed get drIndividualMultitumorPlotData() { // a single model, shown in the modal - const model = this.modalModel, - modelMock = {name: getNameFromDegrees(model), results: _.cloneDeep(model)}, - data = [ - getDrDatasetPlotData(this.modalDataset), - ...getDrBmdLine(modelMock, hoverColor), - ]; - return data; + const model = this.modalModel; + return [getDrDatasetPlotData(this.modalDataset), ...getDrBmdLine(model, hoverColor)]; } @action.bound drPlotAddHover(model) { @@ -379,46 +371,6 @@ class OutputStore { @action.bound drPlotRemoveHover() { this.drModelHover = null; } - - @computed get drFrequentistLollipopPlotDataset() { - let plotData = []; - let models = this.selectedOutput.frequentist.models; - models.map(model => { - let dataArray = []; - let modelArray = new Array(3); - modelArray.fill(model.name); - dataArray.push(model.results.bmdl); - dataArray.push(model.results.bmd); - dataArray.push(model.results.bmdu); - let data = getLollipopDataset(dataArray, modelArray, model.name); - plotData.push(data); - }); - return plotData; - } - - @computed get drBayesianLollipopPlotDataset() { - let plotData = []; - let models = this.selectedOutput.bayesian.models; - models.map(model => { - let dataArray = []; - let modelArray = new Array(3); - modelArray.fill(model.name); - dataArray.push(model.results.bmdl); - dataArray.push(model.results.bmd); - dataArray.push(model.results.bmdu); - let data = getLollipopDataset(dataArray, modelArray, model.name); - plotData.push(data); - }); - return plotData; - } - - @computed get drFrequentistLollipopPlotLayout() { - return getLollipopPlotLayout("Frequentist bmd/bmdl/bmdu Plot", this.selectedDataset); - } - - @computed get drBayesianLollipopPlotLayout() { - return getLollipopPlotLayout("Bayesian bmd/bmdl/bmdu Plot", this.selectedDataset); - } // end dose-response plotting data methods // start model selection methods diff --git a/make.bat b/make.bat index 01c1c3e2..5b750c68 100644 --- a/make.bat +++ b/make.bat @@ -48,21 +48,21 @@ mkdocs serve -f docs/mkdocs.yml -a localhost:8050 goto :eof :lint -ruff format . --check && ruff . +ruff format . --check && ruff check . npm --prefix .\frontend run lint goto :eof :format -ruff format . && ruff . --fix --show-fixes +ruff format . && ruff check . --fix --show-fixes npm --prefix .\frontend run format goto :eof :lint-py -ruff format . --check && ruff . +ruff format . --check && ruff check . goto :eof :format-py -ruff format . && ruff . --fix --show-fixes +ruff format . && ruff check . --fix --show-fixes goto :eof :lint-js diff --git a/manage.py b/manage.py index c80a8616..0aedd338 100644 --- a/manage.py +++ b/manage.py @@ -12,6 +12,7 @@ To fix, update our build/packaging system to flit or poetry and call console_scripts as a module instead of a file, which may fix this issue, based on work on other (private) projects? """ + import os import sys diff --git a/pyproject.toml b/pyproject.toml index c13a9230..72d00f19 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,15 +9,17 @@ omit = [ [tool.ruff] line-length = 100 target-version = "py311" + +[tool.ruff.lint] select = ["F", "E", "W", "I", "UP", "S", "B", "T20", "RUF"] ignore = ["E501", "B904", "B007", "S308", "S113", "S314"] unfixable = ["F401", "F841"] -[tool.ruff.isort] +[tool.ruff.lint.isort] known-first-party = ["bmds_server"] known-third-party = ["bmds"] -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "test_*.py" = ["S101", "S106"] "scripts/*.py" = ["T201"] "**/management/**.py" = ["T201"] diff --git a/requirements/base.txt b/requirements/base.txt index 816a224e..e2e7756f 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,24 +1,24 @@ # website -Django~=4.2.7 -django-anymail==10.2 +Django~=5.0.4 +django-anymail==10.3 django-redis==5.4.0 -django-webpack-loader==2.0.1 -djangorestframework==3.14.0 +django-webpack-loader==3.1.0 +djangorestframework==3.15.1 uritemplate==4.1.1 # schema dependencies PyYAML==6.0.1 -django-reversion==5.0.6 +django-reversion==5.0.12 # db psycopg2-binary==2.9.9 # tasks -celery==5.3.4 - billiard==4.1.0 - kombu==5.3.2 +celery==5.3.6 + billiard==4.2.0 + kombu==5.3.7 -plotly==5.18.0 -redis==5.0.1 +plotly==5.20.0 +redis==5.0.3 # desktop -textual==0.45.1 +textual==0.56.4 whitenoise==6.6.0 diff --git a/requirements/dev.txt b/requirements/dev.txt index b675f4fd..fd92ec4a 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,27 +1,27 @@ -r base.txt # debug -django-debug-toolbar==4.2.0 +django-debug-toolbar==4.3.0 django_extensions==3.2.3 -django-browser-reload==1.12.0 +django-browser-reload==1.12.1 # docs -mkdocs-material==9.4.7 +mkdocs-material==9.5.17 # tests -pytest==7.4.3 -pytest-django==4.6.0 -vcrpy==5.1.0 - urllib3==2.0.6 +pytest==8.1.1 +pytest-django==4.8.0 +vcrpy==6.0.1 + urllib3==2.2.1 pytest-vcr==1.0.2 -coverage==7.3.2 +coverage==7.4.4 # integration tests -playwright==1.39.0 -pytest-playwright==0.4.3 +playwright==1.43.0 +pytest-playwright==0.4.4 # lint and formatting tools -ruff~=0.1.3 +ruff~=0.3.7 # install in editable mode -e . diff --git a/setup.cfg b/setup.cfg index 6d7eafde..cfb793da 100644 --- a/setup.cfg +++ b/setup.cfg @@ -9,6 +9,7 @@ license = MIT classifiers = Programming Language :: Python Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 [options] zip_safe = False @@ -16,20 +17,20 @@ include_package_data = True packages = find: requires_python = ">=3.11" install_requires = - Django~=4.2.7 - django-webpack-loader~=2.0.1 - djangorestframework~=3.14.0 + Django~=5.0.4 + django-webpack-loader~=3.1.0 + djangorestframework~=3.15.1 uritemplate~=4.1.1 PyYAML~=6.0.1 - django-reversion~=5.0.6 - celery~=5.3.4 - billiard~=4.1.0 - kombu~=5.3.2 - plotly~=5.18.0 - redis~=5.0.1 - django-anymail~=10.2 + django-reversion~=5.0.12 + celery~=5.3.6 + billiard~=4.2.0 + kombu~=5.3.7 + plotly~=5.20.0 + redis~=5.0.3 + django-anymail~=10.3 django-redis~=5.4.0 - textual~=0.45.1 + textual~=0.56.4 whitenoise~=6.6.0 [options.package_data] diff --git a/tests/analysis/test_api.py b/tests/analysis/test_api.py index 1b03aed5..ab053ad3 100644 --- a/tests/analysis/test_api.py +++ b/tests/analysis/test_api.py @@ -263,7 +263,7 @@ def test_excel(self, polyk_dataset): response = client.post(url, polyk_dataset, format="json") assert response.status_code == 200 # ensure that response is a valid workbook with two worksheets - data = pd.read_excel(response.content, sheet_name=["adjusted", "summary"]) + data = pd.read_excel(BytesIO(response.content), sheet_name=["adjusted", "summary"]) assert isinstance(data["adjusted"], pd.DataFrame) assert isinstance(data["summary"], pd.DataFrame) diff --git a/tests/analysis/test_models.py b/tests/analysis/test_models.py index c6b28c68..888dc177 100644 --- a/tests/analysis/test_models.py +++ b/tests/analysis/test_models.py @@ -121,7 +121,6 @@ def test_nested_dichotomous(self, bmds_complete_nd, data_path, rewrite_data_file write_excel(df, data_path / "nested_dichotomous.xlsx") (data_path / "nested_dichotomous.docx").write_bytes(docx.getvalue()) - @pytest.mark.xfail(reason="multitumor plotting error") # TODO - remove xfail once fixed def test_multitumor(self, bmds_complete_mt, data_path, rewrite_data_files): analysis = Analysis.objects.create(inputs=bmds_complete_mt)