diff --git a/scripts/bmds-online-33-api.ipynb b/scripts/bmds-online-api.ipynb similarity index 97% rename from scripts/bmds-online-33-api.ipynb rename to scripts/bmds-online-api.ipynb index 2b436280..900d816c 100644 --- a/scripts/bmds-online-33-api.ipynb +++ b/scripts/bmds-online-api.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# BMDS online verison 3.3 - API\n", + "# BMDS Online API\n", "\n", "A work in progress to demonstrate how to use the BMDS Online API." ] @@ -16,9 +16,10 @@ "outputs": [], "source": [ "import json\n", - "import requests\n", + "import time\n", "from pathlib import Path\n", - "import time" + "\n", + "import requests" ] }, { @@ -62,8 +63,8 @@ " \"incidences\": [0, 0, 3, 2, 3],\n", " \"model_type\": \"DM\",\n", " \"column_names\": {\n", - " \"ns\": \"N\", \n", - " \"doses\": \"Dose\", \n", + " \"ns\": \"N\",\n", + " \"doses\": \"Dose\",\n", " \"incidences\": \"Incidence\"\n", " },\n", " \"dataset_name\": \"Dataset #1\",\n", @@ -124,8 +125,7 @@ " print(\"Polling...\")\n", " time.sleep(1)\n", " resp = requests.get(poll_url)\n", - " output = resp.json()\n", - " " + " output = resp.json()\n" ] }, { @@ -334,7 +334,7 @@ "source": [ "excel_url = root + output['word_url']\n", "resp = requests.get(excel_url)\n", - "Path('/Users/shapiromatron/Desktop/demo.docx').write_bytes(resp.content)" + "Path('~/Desktop/demo.docx').write_bytes(resp.content)" ] }, { @@ -356,7 +356,7 @@ "source": [ "word_url = root + output['excel_url']\n", "resp = requests.get(word_url)\n", - "Path('/Users/shapiromatron/Desktop/demo.xlsx').write_bytes(resp.content)" + "Path('~/Desktop/demo.xlsx').write_bytes(resp.content)" ] }, { @@ -393,7 +393,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.10" + "version": "3.11.9" } }, "nbformat": 4, diff --git a/tests/analysis/test_api.py b/tests/analysis/test_api.py index 1ff0f45e..25f17a62 100644 --- a/tests/analysis/test_api.py +++ b/tests/analysis/test_api.py @@ -20,7 +20,7 @@ def check_error(response: dict, type: str, loc: list, msg: str): @pytest.mark.django_db class TestAnalysisViewSet: - def test_auth(self, bmds3_complete_continuous): + def test_auth(self, complete_continuous): """ Check API auth cases. @@ -33,7 +33,7 @@ def test_auth(self, bmds3_complete_continuous): read_url = analysis.get_api_url() payload = { "editKey": analysis.password, - "data": bmds3_complete_continuous, + "data": complete_continuous, } # client; no CSRF @@ -142,43 +142,43 @@ def test_patch_partial(self): assert response.status_code == 200 assert response.json()["inputs"] == payload["data"] - def test_patch_complete_continuous(self, bmds3_complete_continuous): + def test_patch_complete_continuous(self, complete_continuous): client = APIClient() analysis = Analysis.objects.create() url = analysis.get_api_patch_inputs_url() - # complete bmds3 continuous - payload = {"editKey": analysis.password, "data": bmds3_complete_continuous} + # complete continuous + payload = {"editKey": analysis.password, "data": complete_continuous} response = client.patch(url, payload, format="json") assert response.status_code == 200 assert response.json()["inputs"] == payload["data"] - def test_optional_recommender(self, bmds3_complete_continuous): + def test_optional_recommender(self, complete_continuous): client = APIClient() analysis = Analysis.objects.create() url = analysis.get_api_patch_inputs_url() - # complete bmds3 continuous - payload = {"editKey": analysis.password, "data": bmds3_complete_continuous} + # complete continuous + payload = {"editKey": analysis.password, "data": complete_continuous} del payload["data"]["recommender"] response = client.patch(url, payload, format="json") assert response.status_code == 200 assert response.json()["inputs"] == payload["data"] - def test_patch_complete_dichotomous(self, bmds3_complete_dichotomous): + def test_patch_complete_dichotomous(self, complete_dichotomous): client = APIClient() analysis = Analysis.objects.create() url = analysis.get_api_patch_inputs_url() - # complete bmds3 dichotomous - payload = {"editKey": analysis.password, "data": bmds3_complete_dichotomous} + # complete dichotomous + payload = {"editKey": analysis.password, "data": complete_dichotomous} response = client.patch(url, payload, format="json") assert response.status_code == 200 assert response.json()["inputs"] == payload["data"] - def test_execute(self, bmds3_complete_dichotomous): + def test_execute(self, complete_dichotomous): client = APIClient() - analysis = Analysis.objects.create(inputs=bmds3_complete_dichotomous) + analysis = Analysis.objects.create(inputs=complete_dichotomous) assert analysis.started is None url = analysis.get_api_execute_url() @@ -196,9 +196,9 @@ def test_execute(self, bmds3_complete_dichotomous): assert response.data["has_errors"] is False assert bmd == pytest.approx(164.3, rel=0.05) - def test_reset_execute(self, bmds3_complete_dichotomous): + def test_reset_execute(self, complete_dichotomous): client = APIClient() - analysis = Analysis.objects.create(inputs=bmds3_complete_dichotomous) + analysis = Analysis.objects.create(inputs=complete_dichotomous) analysis.execute() url = analysis.get_api_execute_reset_url() @@ -215,9 +215,9 @@ def test_reset_execute(self, bmds3_complete_dichotomous): assert response.data["has_errors"] is False assert response.data["outputs"] == {} - def test_model_selection(self, bmds3_complete_dichotomous): + def test_model_selection(self, complete_dichotomous): client = APIClient() - analysis = Analysis.objects.create(inputs=bmds3_complete_dichotomous) + analysis = Analysis.objects.create(inputs=complete_dichotomous) analysis.execute() url = analysis.get_api_url() + "select-model/" diff --git a/tests/analysis/test_executor.py b/tests/analysis/test_executor.py index ff950713..edd8c7df 100644 --- a/tests/analysis/test_executor.py +++ b/tests/analysis/test_executor.py @@ -7,30 +7,30 @@ class TestAnalysisSession: - def test_default_dichotomous(self, bmds3_complete_dichotomous): + def test_default_dichotomous(self, complete_dichotomous): # assure a default dataset can be created - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) session = AnalysisSession.create(data, 0, 0) assert len(session.frequentist.models) == 1 assert len(session.bayesian.models) == 1 - def test_default_continuous(self, bmds3_complete_continuous): + def test_default_continuous(self, complete_continuous): # assure a default dataset can be created - data = deepcopy(bmds3_complete_continuous) + data = deepcopy(complete_continuous) session = AnalysisSession.create(data, 0, 0) assert len(session.frequentist.models) == 1 assert len(session.bayesian.models) == 1 - def test_default_continuous_individual(self, bmds3_complete_continuous_individual): + def test_default_continuous_individual(self, complete_continuous_individual): # assure a default dataset can be created - data = deepcopy(bmds3_complete_continuous_individual) + data = deepcopy(complete_continuous_individual) session = AnalysisSession.create(data, 0, 0) assert len(session.frequentist.models) == 1 assert len(session.bayesian.models) == 1 - def test_prior_classes(self, bmds3_complete_dichotomous): + def test_prior_classes(self, complete_dichotomous): # assure a default dataset can be created - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) data["models"] = { "frequentist_restricted": ["Gamma"], "frequentist_unrestricted": ["Gamma"], @@ -50,8 +50,8 @@ def test_prior_classes(self, bmds3_complete_dichotomous): ) assert session.bayesian.models[0].settings.priors.prior_class is PriorClass.bayesian - def test_exponential_unpacking(self, bmds3_complete_continuous): - data = deepcopy(bmds3_complete_continuous) + def test_exponential_unpacking(self, complete_continuous): + data = deepcopy(complete_continuous) data["models"] = { "frequentist_restricted": ["Exponential"], "bayesian": [{"model": "Exponential", "prior_weight": 1}], @@ -64,7 +64,7 @@ def test_exponential_unpacking(self, bmds3_complete_continuous): assert session.bayesian.models[0].bmd_model_class.id == ContinuousModelIds.c_exp_m3 assert session.bayesian.models[1].bmd_model_class.id == ContinuousModelIds.c_exp_m5 - def test_multistage_permutations(self, bmds3_complete_dichotomous): + def test_multistage_permutations(self, complete_dichotomous): def _expected_degree(session, n: int): assert session.bayesian is None assert len(session.frequentist.models) == n @@ -74,14 +74,14 @@ def _expected_degree(session, n: int): assert degrees == set(list(range(1, n + 1))) # degree = 1 - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) data["models"] = {"frequentist_restricted": ["Multistage"]} data["dataset_options"][0]["degree"] = 1 session = AnalysisSession.create(data, 0, 0) _expected_degree(session, 1) # degree = 2 - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) data["models"] = {"frequentist_restricted": ["Multistage"]} data["dataset_options"][0]["degree"] = 2 session = AnalysisSession.create(data, 0, 0) @@ -90,7 +90,7 @@ def _expected_degree(session, n: int): # 3 dose-groups; degree = N-1; expected 2 for num_doses in range(3, 8): expected_degree = min(max(num_doses - 1, 2), 8) - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) data["datasets"] = [ { "dtype": "D", @@ -107,7 +107,7 @@ def _expected_degree(session, n: int): _expected_degree(session, expected_degree) # degree = N -1, bayesian, fixed at degree == 2 - data = deepcopy(bmds3_complete_dichotomous) + data = deepcopy(complete_dichotomous) data["models"] = {"bayesian": [{"model": "Multistage", "prior_weight": 1}]} data["dataset_options"][0]["degree"] = 0 session = AnalysisSession.create(data, 0, 0) @@ -117,9 +117,9 @@ def _expected_degree(session, n: int): assert model.bmd_model_class.id == DichotomousModelIds.d_multistage assert model.settings.degree == 2 - def test_polynomial_unpacking(self, bmds3_complete_continuous): + def test_polynomial_unpacking(self, complete_continuous): # test linear; degree 0 - data = deepcopy(bmds3_complete_continuous) + data = deepcopy(complete_continuous) data["models"] = {"frequentist_unrestricted": ["Linear"]} data["dataset_options"][0]["degree"] = 0 session = AnalysisSession.create(data, 0, 0) @@ -128,7 +128,7 @@ def test_polynomial_unpacking(self, bmds3_complete_continuous): assert session.bayesian is None # test polynomial; degree 2 - data = deepcopy(bmds3_complete_continuous) + data = deepcopy(complete_continuous) data["models"] = {"frequentist_unrestricted": ["Polynomial"]} data["dataset_options"][0]["degree"] = 2 session = AnalysisSession.create(data, 0, 0) @@ -137,7 +137,7 @@ def test_polynomial_unpacking(self, bmds3_complete_continuous): assert session.bayesian is None # test polynomial; degree 3 - data = deepcopy(bmds3_complete_continuous) + data = deepcopy(complete_continuous) data["models"] = {"frequentist_unrestricted": ["Polynomial"]} data["dataset_options"][0]["degree"] = 3 session = AnalysisSession.create(data, 0, 0) @@ -147,7 +147,7 @@ def test_polynomial_unpacking(self, bmds3_complete_continuous): assert session.bayesian is None # test linear + polynomial; degree 3 - data = deepcopy(bmds3_complete_continuous) + data = deepcopy(complete_continuous) data["models"] = {"frequentist_unrestricted": ["Linear", "Polynomial"]} data["dataset_options"][0]["degree"] = 3 session = AnalysisSession.create(data, 0, 0) @@ -158,8 +158,8 @@ def test_polynomial_unpacking(self, bmds3_complete_continuous): assert session.bayesian is None # disttype 3 Linear and power are not added - def test_disttype(self, bmds3_complete_continuous): - data = deepcopy(bmds3_complete_continuous) + def test_disttype(self, complete_continuous): + data = deepcopy(complete_continuous) data["models"] = { "frequentist_restricted": ["Exponential", "Hill", "Linear", "Power"], } diff --git a/tests/analysis/test_models.py b/tests/analysis/test_models.py index 888dc177..2fb7c01d 100644 --- a/tests/analysis/test_models.py +++ b/tests/analysis/test_models.py @@ -14,9 +14,9 @@ def write_excel(data: dict, path: Path): @pytest.mark.django_db() -class TestBmds3Execution: - def test_continuous(self, bmds3_complete_continuous, data_path, rewrite_data_files): - analysis = Analysis.objects.create(inputs=bmds3_complete_continuous) +class TestExecution: + def test_continuous(self, complete_continuous, data_path, rewrite_data_files): + analysis = Analysis.objects.create(inputs=complete_continuous) assert analysis.is_finished is False assert analysis.has_errors is False @@ -38,13 +38,13 @@ def test_continuous(self, bmds3_complete_continuous, data_path, rewrite_data_fil assert len(df) == 3 if rewrite_data_files: - write_excel(df, data_path / "continuous.xlsx") - (data_path / "continuous.docx").write_bytes(docx.getvalue()) + write_excel(df, data_path / "reports/continuous.xlsx") + (data_path / "reports/continuous.docx").write_bytes(docx.getvalue()) def test_continuous_individual( - self, bmds3_complete_continuous_individual, data_path, rewrite_data_files + self, complete_continuous_individual, data_path, rewrite_data_files ): - analysis = Analysis.objects.create(inputs=bmds3_complete_continuous_individual) + analysis = Analysis.objects.create(inputs=complete_continuous_individual) assert analysis.is_finished is False assert analysis.has_errors is False @@ -66,11 +66,11 @@ def test_continuous_individual( assert len(df) == 3 if rewrite_data_files: - write_excel(df, data_path / "continuous_individual.xlsx") - (data_path / "continuous_individual.docx").write_bytes(docx.getvalue()) + write_excel(df, data_path / "reports/continuous_individual.xlsx") + (data_path / "reports/continuous_individual.docx").write_bytes(docx.getvalue()) - def test_dichotomous(self, bmds3_complete_dichotomous, data_path, rewrite_data_files): - analysis = Analysis.objects.create(inputs=bmds3_complete_dichotomous) + def test_dichotomous(self, complete_dichotomous, data_path, rewrite_data_files): + analysis = Analysis.objects.create(inputs=complete_dichotomous) assert analysis.is_finished is False assert analysis.has_errors is False @@ -92,8 +92,8 @@ def test_dichotomous(self, bmds3_complete_dichotomous, data_path, rewrite_data_f assert len(df) == 3 if rewrite_data_files: - write_excel(df, data_path / "dichotomous.xlsx") - (data_path / "dichotomous.docx").write_bytes(docx.getvalue()) + write_excel(df, data_path / "reports/dichotomous.xlsx") + (data_path / "reports/dichotomous.docx").write_bytes(docx.getvalue()) def test_nested_dichotomous(self, bmds_complete_nd, data_path, rewrite_data_files): analysis = Analysis.objects.create(inputs=bmds_complete_nd) @@ -118,8 +118,8 @@ def test_nested_dichotomous(self, bmds_complete_nd, data_path, rewrite_data_file assert len(df) == 3 if rewrite_data_files: - write_excel(df, data_path / "nested_dichotomous.xlsx") - (data_path / "nested_dichotomous.docx").write_bytes(docx.getvalue()) + write_excel(df, data_path / "reports/nested_dichotomous.xlsx") + (data_path / "reports/nested_dichotomous.docx").write_bytes(docx.getvalue()) def test_multitumor(self, bmds_complete_mt, data_path, rewrite_data_files): analysis = Analysis.objects.create(inputs=bmds_complete_mt) @@ -144,5 +144,5 @@ def test_multitumor(self, bmds_complete_mt, data_path, rewrite_data_files): assert len(df) == 3 if rewrite_data_files: - write_excel(df, data_path / "multitumor.xlsx") - (data_path / "multitumor.docx").write_bytes(docx.getvalue()) + write_excel(df, data_path / "reports/multitumor.xlsx") + (data_path / "reports/multitumor.docx").write_bytes(docx.getvalue()) diff --git a/tests/analysis/test_transforms.py b/tests/analysis/test_transforms.py index d138cc43..1456a8f9 100644 --- a/tests/analysis/test_transforms.py +++ b/tests/analysis/test_transforms.py @@ -8,7 +8,7 @@ class TestOptions: - def test_bmds3_options_c(self, bmds3_complete_continuous): + def test_options_continuous(self): options = { "bmr_type": 2, "bmr_value": 1.5, @@ -30,7 +30,7 @@ def test_bmds3_options_c(self, bmds3_complete_continuous): assert res.degree == 0 assert res.is_increasing is None - def test_bmds3_options_d(self, bmds3_complete_dichotomous): + def test_options_dichotomous(self): options = { "bmr_type": 0, "bmr_value": 0.15, @@ -49,8 +49,9 @@ def test_bmds3_options_d(self, bmds3_complete_dichotomous): assert res.degree == 1 -def test_remap_exponential(): - assert transforms.remap_exponential([]) == [] - assert transforms.remap_exponential([M_Exponential]) == [M_ExponentialM3, M_ExponentialM5] - expected = ["a", M_ExponentialM3, M_ExponentialM5, "b"] - assert transforms.remap_exponential(["a", M_Exponential, "b"]) == expected +class TestModels: + def test_remap_exponential(self): + assert transforms.remap_exponential([]) == [] + assert transforms.remap_exponential([M_Exponential]) == [M_ExponentialM3, M_ExponentialM5] + expected = ["a", M_ExponentialM3, M_ExponentialM5, "b"] + assert transforms.remap_exponential(["a", M_Exponential, "b"]) == expected diff --git a/tests/analysis/test_validators.py b/tests/analysis/test_validators.py index db94cfca..9229801f 100644 --- a/tests/analysis/test_validators.py +++ b/tests/analysis/test_validators.py @@ -19,7 +19,7 @@ def _missing_field(err, missing_field: str): class TestInputValidation: - def test_bmds3_partial(self): + def test_partial(self): data: dict[str, Any] = { "bmds_version": BmdsVersion.BMDS330.value, "dataset_type": bmds.constants.CONTINUOUS, @@ -74,19 +74,19 @@ def test_bmds3_partial(self): assert validators.validate_input(data, partial=True) is None assert validators.validate_input(data) is None - def test_recommender(self, bmds3_complete_dichotomous): + def test_recommender(self, complete_dichotomous): # ensure recommender is optional - recommender = bmds3_complete_dichotomous.pop("recommender") - assert validators.validate_input(bmds3_complete_dichotomous) is None + recommender = complete_dichotomous.pop("recommender") + assert validators.validate_input(complete_dichotomous) is None # but if it's included it validates - bmds3_complete_dichotomous["recommender"] = recommender - assert validators.validate_input(bmds3_complete_dichotomous) is None + complete_dichotomous["recommender"] = recommender + assert validators.validate_input(complete_dichotomous) is None # but it must be complete if included recommender.pop("rules") with pytest.raises(ValidationError) as err: - validators.validate_input(bmds3_complete_dichotomous) + validators.validate_input(complete_dichotomous) _missing_field(err, "rules") def test_nested_dichotomous(self, nested_dichotomous_datasets): @@ -193,7 +193,7 @@ def test_multi_tumor(self): class TestModelValidation: - def test_bmds3_dichotomous(self): + def test_dichotomous(self): dtype = bmds.constants.DICHOTOMOUS probit = bmds.constants.M_Probit logprobit = bmds.constants.M_LogProbit @@ -242,7 +242,7 @@ def test_bmds3_dichotomous(self): validators.validate_models(dtype, data) assert "Prior weight in bayesian does not sum to 1" in str(err.value) - def test_bmds3_continuous(self): + def test_continuous(self): dtype = bmds.constants.CONTINUOUS power = bmds.constants.M_Power linear = bmds.constants.M_Linear @@ -341,8 +341,8 @@ def test_continuous(self): class TestDatasetValidation: - def test_dichotomous(self, bmds3_complete_dichotomous): - dataset = bmds3_complete_dichotomous["datasets"][0] + def test_dichotomous(self, complete_dichotomous): + dataset = complete_dichotomous["datasets"][0] # check valid check = deepcopy(dataset) @@ -374,8 +374,8 @@ def test_dichotomous(self, bmds3_complete_dichotomous): with pytest.raises(PydanticValidationError, match="A maximum of 30 groups are allowed"): datasets.MaxDichotomousDatasetSchema(**check) - def test_continuous(self, bmds3_complete_continuous): - dataset = bmds3_complete_continuous["datasets"][0] + def test_continuous(self, complete_continuous): + dataset = complete_continuous["datasets"][0] # check valid check = deepcopy(dataset) @@ -401,8 +401,8 @@ def test_continuous(self, bmds3_complete_continuous): with pytest.raises(PydanticValidationError, match="A maximum of 30 groups are allowed"): datasets.MaxContinuousDatasetSchema(**check) - def test_continuous_individual(self, bmds3_complete_continuous_individual): - dataset = bmds3_complete_continuous_individual["datasets"][0] + def test_continuous_individual(self, complete_continuous_individual): + dataset = complete_continuous_individual["datasets"][0] # check valid check = deepcopy(dataset) diff --git a/tests/conftest.py b/tests/conftest.py index b409457b..a917b173 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,7 +45,7 @@ def vcr_cassette_dir(request): @pytest.fixture -def bmds3_complete_continuous(): +def complete_continuous(): return { "bmds_version": BmdsVersion.BMDS330.value, "dataset_type": "C", @@ -80,7 +80,7 @@ def bmds3_complete_continuous(): @pytest.fixture -def bmds3_complete_continuous_individual(): +def complete_continuous_individual(): # fmt: off return { "bmds_version": BmdsVersion.BMDS330.value, @@ -131,7 +131,7 @@ def bmds3_complete_continuous_individual(): @pytest.fixture -def bmds3_complete_dichotomous(): +def complete_dichotomous(): return { "bmds_version": BmdsVersion.BMDS330.value, "dataset_type": "D", diff --git a/tests/data/ci-webpack-stats.json b/tests/data/ci-webpack-stats.json index 33367b30..a0e2f957 100644 --- a/tests/data/ci-webpack-stats.json +++ b/tests/data/ci-webpack-stats.json @@ -9,7 +9,7 @@ "assets": { "main.97590b3096a638c665c7.js": { "name": "main.97590b3096a638c665c7.js", - "path": "/Users/shapiromatron/dev/bmds-server/bmds_server/static/bundles/main.97590b3096a638c665c7.js", + "path": "/path/to/bmds-server/bmds_server/static/bundles/main.97590b3096a638c665c7.js", "publicPath": "http://localhost:8110/dist/main.97590b3096a638c665c7.js" } }