From 5dc3d27b5f20dba88667167a1d11484ba8d3f514 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 12:28:11 +0000 Subject: [PATCH 01/14] remove variables from entry-point --- dafni/main_dafni.py | 73 ++++++++++++++------------------------------- 1 file changed, 23 insertions(+), 50 deletions(-) diff --git a/dafni/main_dafni.py b/dafni/main_dafni.py index 4cf75cb6..3987a6d1 100644 --- a/dafni/main_dafni.py +++ b/dafni/main_dafni.py @@ -13,6 +13,7 @@ from causal_testing.testing.causal_test_outcome import Positive, Negative, NoEffect, SomeEffect from causal_testing.estimation.linear_regression_estimator import LinearRegressionEstimator from causal_testing.json_front.json_class import JsonUtility +from causal_testing.specification.causal_dag import CausalDAG class ValidationError(Exception): @@ -40,12 +41,6 @@ def get_args(test_args=None) -> argparse.Namespace: "-i", "--ignore_cycles", action="store_true", help="Whether to ignore cycles in the DAG.", default=False ) - parser.add_argument( - "--variables_path", - required=True, - help="Input configuration file path " "containing the predefined variables (.json)", - ) - parser.add_argument( "--dag_path", required=True, @@ -71,8 +66,6 @@ def get_args(test_args=None) -> argparse.Namespace: # Convert these to Path objects for main() - args.variables_path = Path(args.variables_path) - args.tests_path = Path(args.tests_path) if args.dag_path is not None: @@ -91,53 +84,33 @@ def get_args(test_args=None) -> argparse.Namespace: return args -def read_variables(variables_path: Path) -> FileNotFoundError | dict: +def parse_variables(causal_dag: CausalDAG) -> tuple: """ - Function to read the variables.json file specified by the user - :param variables_path: A Path object of the user-specified file path + Function to validate the variables defined in the causal tests + :param causal_dag: an instancce of the CausalDAG class containing the relationships and variables (as attribtutes) :returns: - - dict - A valid dictionary consisting of the causal tests + - Tuple containing the inputs, outputs and constraints to pass into the modelling scenario """ - if not variables_path.exists() or variables_path.is_dir(): - print(f"JSON file not found at the specified location: {variables_path}") - raise FileNotFoundError + inputs = [ + Input(node, eval(eval(attributes["datatype"]))) + for node, attributes in causal_dag.graph.nodes(data=True) + if eval(attributes["typestring"]) == "input" + ] - with variables_path.open("r") as file: - inputs = json.load(file) + constraints = set() - return inputs + for (node, attributes), input_var in zip(causal_dag.graph.nodes(data=True), inputs): + if "constraint" in attributes: -def validate_variables(data_dict: dict) -> tuple: - """ - Function to validate the variables defined in the causal tests - :param data_dict: A dictionary consisting of the pre-defined variables for the causal tests - :returns: - - Tuple containing the inputs, outputs and constraints to pass into the modelling scenario - """ - if data_dict["variables"]: - variables = data_dict["variables"] - - inputs = [ - Input(variable["name"], eval(variable["datatype"])) - for variable in variables - if variable["typestring"] == "Input" - ] - - outputs = [ - Output(variable["name"], eval(variable["datatype"])) - for variable in variables - if variable["typestring"] == "Output" - ] - - constraints = set() - - for variable, input_var in zip(variables, inputs): - if "constraint" in variable: - constraints.add(input_var.z3 == variable["constraint"]) - else: - raise ValidationError("Cannot find the variables defined by the causal tests.") + constraints.add(input_var.z3 == attributes["constraint"]) + + outputs = [ + Output(node, eval(eval(attributes["datatype"]))) + for node, attributes in causal_dag.graph.nodes(data=True) + if eval(attributes["typestring"]) == "output" + ] return inputs, outputs, constraints @@ -153,11 +126,11 @@ def main(): data_frame = pd.concat([pd.read_csv(d) for d in args.data_path]) - # Step 1: Read in the JSON input/output variables and parse io arguments + # Step 1: Read in the dag and parse the variables from the .dot file - variables_dict = read_variables(args.variables_path) + causal_dag = CausalDAG(args.dag_path) - inputs, outputs, constraints = validate_variables(variables_dict) + inputs, outputs, constraints = parse_variables(causal_dag) # Step 2: Set up the modeling scenario and estimator From b929564e11f424b63b3f440b86f75fff7d6706c1 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 12:29:10 +0000 Subject: [PATCH 02/14] update sample dag file to include the variables as node attributes --- dafni/data/inputs/dag.dot | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dafni/data/inputs/dag.dot b/dafni/data/inputs/dag.dot index 43628817..67b9007b 100644 --- a/dafni/data/inputs/dag.dot +++ b/dafni/data/inputs/dag.dot @@ -1,7 +1,12 @@ digraph CausalDAG { rankdir=LR; - "vaccine" -> "cum_vaccinations"; - "vaccine" -> "cum_vaccinated"; - "vaccine" -> "cum_infections"; - "max_doses"; + vaccine [datatype="int", typestring="input"]; + cum_vaccinations [datatype="int", typestring="output"]; + cum_vaccinated [datatype="int", typestring="output"]; + cum_infections [datatype="int", typestring="output"]; + max_doses [datatype="int", typestring="output"]; + vaccine -> cum_vaccinations; + vaccine -> cum_vaccinated; + vaccine -> cum_infections; + max_doses; } \ No newline at end of file From 354fd3e0bf67ef4d1c642362b3242eb74de53011 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 12:29:35 +0000 Subject: [PATCH 03/14] update Dockerfile for dafni image --- dafni/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dafni/Dockerfile b/dafni/Dockerfile index 8969ad3f..a5caec83 100644 --- a/dafni/Dockerfile +++ b/dafni/Dockerfile @@ -24,10 +24,9 @@ COPY --chown=nobody ./dafni/data/inputs ./data/inputs RUN pip install causal-testing-framework --no-cache-dir #For local testing purposes -ENV VARIABLES_PATH=./data/inputs/variables.json \ - CAUSAL_TESTS=./data/inputs/causal_tests.json \ +ENV CAUSAL_TESTS=./data/inputs/causal_tests.json \ DATA_PATH=./data/inputs/runtime_data.csv \ DAG_PATH=./data/inputs/dag.dot # Define the entrypoint/commands -CMD python main_dafni.py --variables_path $VARIABLES_PATH --dag_path $DAG_PATH --data_path $DATA_PATH --tests_path $CAUSAL_TESTS +CMD python main_dafni.py --dag_path $DAG_PATH --data_path $DATA_PATH --tests_path $CAUSAL_TESTS From 0e53b47a0a639ee11e225ee715d1129770095b31 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 12:29:51 +0000 Subject: [PATCH 04/14] update dafni model definition --- dafni/model_definition.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dafni/model_definition.yaml b/dafni/model_definition.yaml index 901e2570..a44c0e3d 100644 --- a/dafni/model_definition.yaml +++ b/dafni/model_definition.yaml @@ -49,14 +49,6 @@ spec: path: inputs/ required: true - - name: Variables - description: > - A .JSON file containing the input variables to be used - default: - - 02e755c8-952b-461a-a914-4f4ffbe2edf1 - path: inputs/ - required: true - outputs: datasets: - name: causal_test_results.json From 4497da2f9305afbd4bc56508550dab116e1335ab Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 12:30:24 +0000 Subject: [PATCH 05/14] update README.md --- dafni/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dafni/README.md b/dafni/README.md index 29833d61..40e2ae42 100644 --- a/dafni/README.md +++ b/dafni/README.md @@ -10,8 +10,8 @@ to upload the framework onto [DAFNI](https://www.dafni.ac.uk). - `data` contains two sub-folders (the structure is important for DAFNI). - `inputs` is a folder that contains the input files that are (separately) uploaded to DAFNI. - `causal_tests.json` is a JSON file that contains the causal tests. - - `variables.json` is a JSON file that contains the variables and constraints to be used. - - `dag.dot` is a dot file that contains the directed acyclc graph (dag) file. + - `dag.dot` is a dot file that contains the directed acyclc graph (dag) file, + including the variables (and constraints if necessary) as node attributes. - `runtime_data.csv` is a csv file that contains the runtime data. - `outputs` is a folder where the `causal_tests_results.json` output file is created. From fb83d65afcdb00587eb7886ef5ad50d47260eef8 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 6 Dec 2024 16:18:37 +0000 Subject: [PATCH 06/14] turned main_dafni.py into a module --- dafni/Dockerfile | 16 +++++++--------- dafni/docker-compose.yaml | 4 ++-- dafni/src/__init__.py | 0 dafni/{ => src}/main_dafni.py | 0 4 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 dafni/src/__init__.py rename dafni/{ => src}/main_dafni.py (100%) diff --git a/dafni/Dockerfile b/dafni/Dockerfile index a5caec83..850417cf 100644 --- a/dafni/Dockerfile +++ b/dafni/Dockerfile @@ -8,25 +8,23 @@ ENV PYTHONDONTWRITEBYTECODE=1 ## from crashing without emitting any logs due to buffering ENV PYTHONUNBUFFERED=1 -#Label maintainer +# Label maintainer LABEL maintainer="Dr. Farhad Allian - The University of Sheffield" # Create a folder for the source code/outputs RUN mkdir -p ./causal_testing RUN mkdir -p ./data/outputs -# Copy the source code and test files from build into the container -COPY --chown=nobody ../causal_testing ./causal_testing -COPY --chown=nobody ./dafni/main_dafni.py ./ +# Copy the source code from local rootand test files from build into the container +COPY --chown=nobody ./causal_testing ./causal_testing +COPY --chown=nobody ./dafni/src/ ./src COPY --chown=nobody ./dafni/data/inputs ./data/inputs # Install core dependencies using PyPi RUN pip install causal-testing-framework --no-cache-dir -#For local testing purposes -ENV CAUSAL_TESTS=./data/inputs/causal_tests.json \ - DATA_PATH=./data/inputs/runtime_data.csv \ - DAG_PATH=./data/inputs/dag.dot +# Set the PYTHONPATH environment variable to include the /src directory +ENV PYTHONPATH="/src:${PYTHONPATH}" # Define the entrypoint/commands -CMD python main_dafni.py --dag_path $DAG_PATH --data_path $DATA_PATH --tests_path $CAUSAL_TESTS +CMD python -m main_dafni --dag_path $DAG_PATH --data_path $DATA_PATH --tests_path $TESTS_PATH diff --git a/dafni/docker-compose.yaml b/dafni/docker-compose.yaml index 9a38d1c4..a894fefb 100644 --- a/dafni/docker-compose.yaml +++ b/dafni/docker-compose.yaml @@ -3,8 +3,8 @@ services: causal-testing-framework: build: context: ../ - dockerfile: ./dafni/Dockerfile + dockerfile: dafni/Dockerfile env_file: - .env volumes: - - .:/usr/src/app + - ./dafni:/usr/src/ diff --git a/dafni/src/__init__.py b/dafni/src/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dafni/main_dafni.py b/dafni/src/main_dafni.py similarity index 100% rename from dafni/main_dafni.py rename to dafni/src/main_dafni.py From 5b736b8b3cbb043a04baf2ce0428d2d38bd55610 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 09:56:14 +0000 Subject: [PATCH 07/14] final touches to main_dafni.py --- dafni/src/main_dafni.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dafni/src/main_dafni.py b/dafni/src/main_dafni.py index 3987a6d1..245ac41a 100644 --- a/dafni/src/main_dafni.py +++ b/dafni/src/main_dafni.py @@ -72,7 +72,7 @@ def get_args(test_args=None) -> argparse.Namespace: args.dag_path = Path(args.dag_path) if args.output_path is None: - args.output_path = "./data/outputs/causal_tests_results.json" + args.output_path = "data/outputs/causal_tests_results.json" Path(args.output_path).parent.mkdir(exist_ok=True) @@ -86,7 +86,7 @@ def get_args(test_args=None) -> argparse.Namespace: def parse_variables(causal_dag: CausalDAG) -> tuple: """ - Function to validate the variables defined in the causal tests + Function to parse and validate the variables defined in the causal dag. :param causal_dag: an instancce of the CausalDAG class containing the relationships and variables (as attribtutes) :returns: - Tuple containing the inputs, outputs and constraints to pass into the modelling scenario From ea46bdc600972a5b8b6930f8e2d14c19be290548 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 09:57:35 +0000 Subject: [PATCH 08/14] add: tests for main_dafni.py --- dafni/tests/tests_main_dafni.py | 154 ++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 dafni/tests/tests_main_dafni.py diff --git a/dafni/tests/tests_main_dafni.py b/dafni/tests/tests_main_dafni.py new file mode 100644 index 00000000..19f0e89d --- /dev/null +++ b/dafni/tests/tests_main_dafni.py @@ -0,0 +1,154 @@ +import unittest +from unittest.mock import patch +import argparse +from pathlib import Path +from causal_testing.testing.causal_test_outcome import Positive, Negative, NoEffect, SomeEffect +from causal_testing.estimation.linear_regression_estimator import LinearRegressionEstimator +from causal_testing.specification.causal_dag import CausalDAG +from causal_testing.specification.variable import Input, Output +from main_dafni import get_args, parse_variables + +# Base directory (relative to the current test file location) +BASE_DIR = Path(__file__).resolve().parent.parent # Points to ./dafni +DATA_DIR = BASE_DIR / "data" # Points to ./dafni/data + + +class TestGetArgs(unittest.TestCase): + """Test the argparse functionality of the DAFNI entrypoint.""" + + @patch("argparse.ArgumentParser.parse_args") + def test_get_args_with_all_arguments(self, mock_parse_args): + """Ensure all arguments work as expected""" + + mock_parse_args.return_value = argparse.Namespace( + data_path=[str(DATA_DIR / "inputs/runtime_data.csv")], + tests_path=str(DATA_DIR / "inputs/causal_tests.json"), + ignore_cycles=True, + dag_path=str(DATA_DIR / "inputs/dag.dot"), + output_path=str(DATA_DIR / "outputs/causal_test_results.json"), + f=False, + w=False, + ) + + args = get_args() + + if args.output_path is None: + self.assertEqual(args.output_path, Path(DATA_DIR / "outputs/causal_test_results.json")) + else: + self.assertIsInstance(args.output_path, Path) + + self.assertEqual(args.data_path, [str(DATA_DIR / "inputs/runtime_data.csv")]) + self.assertEqual(args.tests_path, Path(DATA_DIR / "inputs/causal_tests.json")) + self.assertEqual(args.dag_path, Path(DATA_DIR / "inputs/dag.dot")) + self.assertIsInstance(args.f, bool) + self.assertIsInstance(args.w, bool) + self.assertIsInstance(args.ignore_cycles, bool) + + +class TestParseVariables(unittest.TestCase): + """Test the parse variables functionality of the DAFNI entrypoint.""" + + @classmethod + def setUpClass(cls): + """Set up class method for the Causal Dag.""" + + cls.causal_dag = CausalDAG(str(DATA_DIR / "inputs/dag.dot")) + + def test_causal_dag_instance(self): + """Test if the input is an instance of CausalDAG.""" + self.assertIsInstance(self.causal_dag, CausalDAG) + + def test_dag_attributes(self): + """Test if all attributes in the DAG are strings.""" + for _, attributes in self.causal_dag.graph.nodes(data=True): + for key, value in attributes.items(): + self.assertIsInstance(value, str) + self.assertIsInstance(key, str) + + def test_dag_inputs_outputs_constraints(self): + """Test the DAG inputs, outputs and constraints.""" + + try: + inputs, outputs, constraints = parse_variables(self.causal_dag) + + for _input, _output, _constraint in zip(inputs, outputs, constraints): + # Assert each input is an Input, Output or Set object + self.assertIsInstance(_input, Input) + self.assertIsInstance(_output, Output) + self.assertIsInstance(constraints, set) + + except ValueError as e: + self.fail(f"Unable to parse variables: {e}") + + +class TestMain(unittest.TestCase): + """Test the main entrypoint.""" + + def test_estimator(self): + """Test estimators currently supported on DAFNI.""" + + estimators = {"LinearRegressionEstimator": LinearRegressionEstimator} + + self.assertIsInstance(estimators, dict, "Estimators must be a dictionary.") + + self.assertIn( + "LinearRegressionEstimator", estimators, + "The key 'LinearRegressionEstimator' must exist in the dictionary." + ) + + self.assertEqual( + estimators["LinearRegressionEstimator"], + LinearRegressionEstimator, + "The estimator should be LinearRegression.", + ) + + self.assertNotIn("LogisticRegressionEstimator", estimators, + "The dictionary should not contain other estimators.") + + def test_expected_outcome_effects_keys(self): + """Check that the dictionary contains these keys only for DAFNI.""" + + expected_outcome_effects = { + "Positive": Positive(), + "Negative": Negative(), + "NoEffect": NoEffect(), + "SomeEffect": SomeEffect(), + } + + expected_keys = ["Positive", "Negative", "NoEffect", "SomeEffect"] + + actual_keys = list(expected_outcome_effects.keys()) + + self.assertEqual(sorted(actual_keys), sorted(expected_keys), + "The dictionary keys do not match the expected keys.") + + def test_expected_outcome_effects_values(self): + """Check that the dictionary contains the correct instances for DAFNI.""" + + expected_outcome_effects = { + "Positive": Positive(), + "Negative": Negative(), + "NoEffect": NoEffect(), + "SomeEffect": SomeEffect(), + } + + self.assertIsInstance( + expected_outcome_effects["Positive"], + Positive, + "The value for 'Positive' should be an instance of Positive.", + ) + self.assertIsInstance( + expected_outcome_effects["Negative"], + Negative, + "The value for 'Negative' should be an instance of Negative.", + ) + self.assertIsInstance( + expected_outcome_effects["NoEffect"], + NoEffect, + "The value for 'NoEffect' should be an instance of NoEffect.", + ) + self.assertIsInstance( + expected_outcome_effects["SomeEffect"], + SomeEffect, + "The value for 'SomeEffect' should be an instance of SomeEffect.", + ) From 005d2da97b25fd6e5fb62029d60c69e726489e24 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 11:00:53 +0000 Subject: [PATCH 09/14] update: tests to dafni workflow --- .github/workflows/publish-to-dafni.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-dafni.yaml b/.github/workflows/publish-to-dafni.yaml index 424a0fe6..f407cc37 100644 --- a/.github/workflows/publish-to-dafni.yaml +++ b/.github/workflows/publish-to-dafni.yaml @@ -38,14 +38,25 @@ jobs: with: python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install causal-testing-framework -e .[dev] + python -m pip install dafni-cli + + - name: Run tests before uploading + id: tests + run: | + python -m pytest ./dafni/tests + - name: Build the container + if: steps.tests.outcome == 'success' run: | docker build -t ctf:${{ env.VERSION }} -f ./dafni/Dockerfile . docker save ctf:${{ env.VERSION }} | gzip > ctf-dafni-${{ env.VERSION }}.tar.gz - - name: Install DAFNI-CLI and log in + - name: Log into DAFNI run: | - python -m pip install dafni-cli dafni login - name: Upload to DAFNI From 9a4539288ef2f3b84b41481fff108ee8b1519321 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 11:54:43 +0000 Subject: [PATCH 10/14] fix: typo in Dockerfile --- dafni/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dafni/Dockerfile b/dafni/Dockerfile index 850417cf..b94d045c 100644 --- a/dafni/Dockerfile +++ b/dafni/Dockerfile @@ -15,7 +15,7 @@ LABEL maintainer="Dr. Farhad Allian - The University of Sheffield" RUN mkdir -p ./causal_testing RUN mkdir -p ./data/outputs -# Copy the source code from local rootand test files from build into the container +# Copy the source code from local root and test files from build into the container COPY --chown=nobody ./causal_testing ./causal_testing COPY --chown=nobody ./dafni/src/ ./src COPY --chown=nobody ./dafni/data/inputs ./data/inputs From 456048cd919cb997435c8890c6bf24614f099cd9 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 13:02:16 +0000 Subject: [PATCH 11/14] fix: default in ignore_cycles tests_main_dafni.py --- dafni/tests/tests_main_dafni.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dafni/tests/tests_main_dafni.py b/dafni/tests/tests_main_dafni.py index 19f0e89d..821d9733 100644 --- a/dafni/tests/tests_main_dafni.py +++ b/dafni/tests/tests_main_dafni.py @@ -23,7 +23,7 @@ def test_get_args_with_all_arguments(self, mock_parse_args): mock_parse_args.return_value = argparse.Namespace( data_path=[str(DATA_DIR / "inputs/runtime_data.csv")], tests_path=str(DATA_DIR / "inputs/causal_tests.json"), - ignore_cycles=True, + ignore_cycles=False, dag_path=str(DATA_DIR / "inputs/dag.dot"), output_path=str(DATA_DIR / "outputs/causal_test_results.json"), f=False, From 029fdfcc9446bfae8733729867686bdc1b7de77f Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 13:03:56 +0000 Subject: [PATCH 12/14] updated causal_tests_results.json --- dafni/data/outputs/causal_tests_results.json | 108 ++++++++++++++----- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/dafni/data/outputs/causal_tests_results.json b/dafni/data/outputs/causal_tests_results.json index f5e503aa..59d651c3 100644 --- a/dafni/data/outputs/causal_tests_results.json +++ b/dafni/data/outputs/causal_tests_results.json @@ -18,9 +18,15 @@ "outcome": "cum_vaccinations", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 252628.1066666667, - "ci_low": 252271.33332001517, - "ci_high": 252984.8800133182 + "effect_estimate": { + "max_doses": 252628.1066666667 + }, + "ci_low": [ + 252271.33332001517 + ], + "ci_high": [ + 252984.8800133182 + ] } }, { @@ -42,9 +48,15 @@ "outcome": "cum_vaccinated", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 213111.93333333335, - "ci_low": 212755.15056812647, - "ci_high": 213468.71609854023 + "effect_estimate": { + "max_doses": 213111.93333333335 + }, + "ci_low": [ + 212755.15056812647 + ], + "ci_high": [ + 213468.71609854023 + ] } }, { @@ -66,9 +78,15 @@ "outcome": "cum_infections", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 2666.3066666666664, - "ci_low": 2619.972040648758, - "ci_high": 2712.6412926845746 + "effect_estimate": { + "max_doses": 2666.3066666666664 + }, + "ci_low": [ + 2619.972040648758 + ], + "ci_high": [ + 2712.6412926845746 + ] } }, { @@ -89,9 +107,15 @@ "outcome": "cum_vaccinations", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 315785.1333333332, - "ci_low": 315339.1666500188, - "ci_high": 316231.1000166476 + "effect_estimate": { + "vaccine": 315785.1333333332 + }, + "ci_low": [ + 315339.1666500188 + ], + "ci_high": [ + 316231.1000166476 + ] } }, { @@ -112,9 +136,15 @@ "outcome": "cum_vaccinated", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 266389.91666666657, - "ci_low": 265943.93821015797, - "ci_high": 266835.89512317517 + "effect_estimate": { + "vaccine": 266389.91666666657 + }, + "ci_low": [ + 265943.93821015797 + ], + "ci_high": [ + 266835.89512317517 + ] } }, { @@ -135,9 +165,15 @@ "outcome": "cum_infections", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 3332.883333333332, - "ci_low": 3274.9650508109467, - "ci_high": 3390.801615855717 + "effect_estimate": { + "vaccine": 3332.883333333332 + }, + "ci_low": [ + 3274.9650508109467 + ], + "ci_high": [ + 3390.801615855717 + ] } }, { @@ -159,9 +195,15 @@ "outcome": "cum_vaccinated", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": 0.9998656401531605, - "ci_low": 0.9929245394499968, - "ci_high": 1.0068067408563242 + "effect_estimate": { + "cum_vaccinations": 0.9998656401531605 + }, + "ci_low": [ + 0.9929245394499968 + ], + "ci_high": [ + 1.0068067408563242 + ] } }, { @@ -183,9 +225,15 @@ "outcome": "cum_infections", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": -0.006416682407515084, - "ci_low": -0.05663010083886572, - "ci_high": 0.043796736023835554 + "effect_estimate": { + "cum_vaccinations": -0.006416682407515084 + }, + "ci_low": [ + -0.05663010083886572 + ], + "ci_high": [ + 0.043796736023835554 + ] } }, { @@ -207,9 +255,15 @@ "outcome": "cum_infections", "adjustment_set": [], "effect_measure": "coefficient", - "effect_estimate": -0.006176900588291234, - "ci_low": -0.05639349612119588, - "ci_high": 0.04403969494461341 + "effect_estimate": { + "cum_vaccinated": -0.006176900588291234 + }, + "ci_low": [ + -0.05639349612119588 + ], + "ci_high": [ + 0.04403969494461341 + ] } } ] From 802e274c0cd8efc361f856f0b83c333d59c90404 Mon Sep 17 00:00:00 2001 From: f-allian Date: Tue, 10 Dec 2024 17:19:46 +0000 Subject: [PATCH 13/14] update: file names --- dafni/tests/__init__.py | 0 dafni/tests/{tests_main_dafni.py => test_main_dafni.py} | 3 +-- 2 files changed, 1 insertion(+), 2 deletions(-) create mode 100644 dafni/tests/__init__.py rename dafni/tests/{tests_main_dafni.py => test_main_dafni.py} (98%) diff --git a/dafni/tests/__init__.py b/dafni/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/dafni/tests/tests_main_dafni.py b/dafni/tests/test_main_dafni.py similarity index 98% rename from dafni/tests/tests_main_dafni.py rename to dafni/tests/test_main_dafni.py index 821d9733..ee135714 100644 --- a/dafni/tests/tests_main_dafni.py +++ b/dafni/tests/test_main_dafni.py @@ -6,13 +6,12 @@ from causal_testing.estimation.linear_regression_estimator import LinearRegressionEstimator from causal_testing.specification.causal_dag import CausalDAG from causal_testing.specification.variable import Input, Output -from main_dafni import get_args, parse_variables +from dafni.src.main_dafni import get_args, parse_variables # Base directory (relative to the current test file location) BASE_DIR = Path(__file__).resolve().parent.parent # Points to ./dafni DATA_DIR = BASE_DIR / "data" # Points to ./dafni/data - class TestGetArgs(unittest.TestCase): """Test the argparse functionality of the DAFNI entrypoint.""" From 05a85fd58a37ef1a0e09e7f34167264fea599a32 Mon Sep 17 00:00:00 2001 From: f-allian Date: Fri, 13 Dec 2024 16:48:00 +0000 Subject: [PATCH 14/14] update: README.md metadata --- dafni/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dafni/README.md b/dafni/README.md index 40e2ae42..059ce257 100644 --- a/dafni/README.md +++ b/dafni/README.md @@ -10,8 +10,11 @@ to upload the framework onto [DAFNI](https://www.dafni.ac.uk). - `data` contains two sub-folders (the structure is important for DAFNI). - `inputs` is a folder that contains the input files that are (separately) uploaded to DAFNI. - `causal_tests.json` is a JSON file that contains the causal tests. - - `dag.dot` is a dot file that contains the directed acyclc graph (dag) file, - including the variables (and constraints if necessary) as node attributes. + - `dag.dot` is a dot file that contains the directed acyclic graph (dag). Causal variables are defined as + node metadata attributes in this file as key-value pairs using the following syntax: + `node [datatype="int", typestring="input"]`. The `datatype` key specifies the datatype of the causal variable + as a string (e.g. `"int"`, `"str"`) and the `typestring` key specifies its typestring, which is also a string + representing the variable type (e.g. `"input"` or `"output"`). - `runtime_data.csv` is a csv file that contains the runtime data. - `outputs` is a folder where the `causal_tests_results.json` output file is created.