From 16c103015425abccb3bd02e2c0f44ef7b13de128 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 9 Apr 2026 21:37:36 -0700 Subject: [PATCH 1/5] Submitting example --- .github/workflows/submit_example.yaml | 38 +++++ requirements_example.txt | 0 tests/test_example.py | 205 ++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 .github/workflows/submit_example.yaml create mode 100644 requirements_example.txt create mode 100644 tests/test_example.py diff --git a/.github/workflows/submit_example.yaml b/.github/workflows/submit_example.yaml new file mode 100644 index 0000000..ab6b2cb --- /dev/null +++ b/.github/workflows/submit_example.yaml @@ -0,0 +1,38 @@ +--- +# This workflow will install Python dependencies and run tests + +name: Unit test and code coverage + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.13'] + submission: ['null', 'example'] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + sudo apt-get update + sudo apt install libbz2-dev + python -m pip install --upgrade pip + pip install wheel + pip install . + pip install .[dev] + if [ -f requirements_${{ matrix.submission }}.txt ]; then pip install -r requirements_${{ matrix.submission }}.txt; fi + - name: Run unit tests with pytest + run: | + python -m pytest tests/test_${{ matrix.submission }}.py diff --git a/requirements_example.txt b/requirements_example.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_example.py b/tests/test_example.py new file mode 100644 index 0000000..c96f48e --- /dev/null +++ b/tests/test_example.py @@ -0,0 +1,205 @@ +import os +from pathlib import Path +import pytest + +# Put needed import here + +# These are used by test scripts +from pz_data_challenge.taskset_1 import run_taskset_1 +from pz_data_challenge.taskset_2 import run_taskset_2 +from pz_data_challenge import submit_utils + +# Change these to match the name of the submission +# and a URL to download the sumission data files +# and needed model files +SUBMISSION_NAME: str = "example" +SUBMISSION_URL: str = "" + +# don't change these +SUBMIT_DIR: str = f"submissions/{SUBMISSION_NAME}" +PUBLIC_AREA: str = "tests/public" + + +@pytest.fixture(name="setup_submit_area", scope="module") +def setup_submit_area(request: pytest.FixtureRequest) -> int: + """ + A pytest fixture to download the submission data + + If all the submission data are in a tar file with the + proper structure you should not need to change this function. + """ + + if not os.path.exists(SUBMIT_DIR): + if not SUBMISSION_URL: + raise ValueError(f"SUBMISSION_URL in tests/test_{SUBMISSION_NAME}.py has not been set") + submit_utils.download_and_extract_tar(SUBMISSION_URL, SUBMIT_DIR) + + def teardown_submit_area() -> None: + if not os.environ.get("NO_TEARDOWN"): + os.system(f"\\rm -rf {SUBMIT_DIR}") + + try: + os.makedirs(os.path.join(SUBMIT_DIR, "outputs_2")) + except Exception: + pass + + try: + os.makedirs(os.path.join(SUBMIT_DIR, "outputs_3")) + except Exception: + pass + + request.addfinalizer(teardown_submit_area) + + return 0 + + +def run_taskset_1_estimation_only( + model_file: str | Path, + test_file: str | Path, + output_file: str | Path, +) -> None: + """ + User supplied function to run estimation for task set 1 + + This function should use a model stored in model_file, which + is downloaded as part of the submission tar file. + + This function should write output data to output_file in qp + format. + + Parameters + ---------- + model_file: + Path to the model. This should be part of the submission + tar file. + test_file: + Path to the test file contains the photometric test data on + which the PZ estimation will be run + output_file: + Path to write the output data to. The output data should + be written in qp format. + """ + return + + +def run_taskset_1_training_and_estimation( + train_file: str | Path, + test_file: str | Path, + output_file: str | Path, +) -> None: + """ + User supplied function to run training and estimation for task set 1 + + This function should train a model and use it. + + This function should write output data to output_file in qp + format. + + Parameters + ---------- + train_file: + Path to the test file contains the photometric test data on + which the PZ estimation will be trained + test_file: + Path to the test file contains the photometric test data on + which the PZ estimation will be run + output_file: + Path to write the output data to. The output data should + be written in qp format. + """ + return + + +def run_taskset_2_estimation_only( + model_file: str | Path, + test_file: str | Path, + output_file: str | Path, +) -> None: + """ + User supplied function to run estimation for task set 1 + + This function should use a model stored in model_file, which + is downloaded as part of the submission tar file. + + This function should write output data to output_file in qp + format. + + Parameters + ---------- + model_file: + Path to the model. This should be part of the submission + tar file. + test_file: + Path to the test file contains the photometric test data on + which the PZ estimation will be run + output_file: + Path to write the output data to. The output data should + be written in qp format. + """ + return + + +def run_taskset_2_training_and_estimation( + train_file: str | Path, + test_file: str | Path, + output_file: str | Path, +) -> None: + """ + User supplied function to run training and estimation for task set 1 + + This function should train a model and use it. + + This function should write output data to output_file in qp + format. + + Parameters + ---------- + test_file: + Path to the test file contains the photometric test data on + which the PZ estimation will be run + output_file: + Path to write the output data to. The output data should + be written in qp format. + """ + + +def test_example_taskset_1( + setup_public_area: int, + setup_submit_area: int, +) -> None: + """ + Test fuction to validate a submisson for Taskset 1 + + You should not need to change this function + """ + + assert setup_public_area == 0 + assert setup_submit_area == 0 + + run_taskset_1( + PUBLIC_AREA, + SUBMISSION_NAME, + run_taskset_1_estimation_only, + run_taskset_1_training_and_estimation, + ) + + +def test_example_taskset_2( + setup_public_area: int, + setup_submit_area: int, +) -> None: + """ + Test fuction to validate a submisson for Taskset 2 + + You should not need to change this function + """ + + assert setup_public_area == 0 + assert setup_submit_area == 0 + + run_taskset_2( + PUBLIC_AREA, + SUBMISSION_NAME, + run_taskset_2_estimation_only, + run_taskset_2_training_and_estimation, + ) From 9e55aa1816a927cb5941819adeca6c790af2ad49 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 9 Apr 2026 21:43:02 -0700 Subject: [PATCH 2/5] fix submission url --- tests/test_example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_example.py b/tests/test_example.py index c96f48e..06fc433 100644 --- a/tests/test_example.py +++ b/tests/test_example.py @@ -13,7 +13,7 @@ # and a URL to download the sumission data files # and needed model files SUBMISSION_NAME: str = "example" -SUBMISSION_URL: str = "" +SUBMISSION_URL: str = "https://s3df.slac.stanford.edu/people/echarles/submit_example.tgz" # don't change these SUBMIT_DIR: str = f"submissions/{SUBMISSION_NAME}" From 97171b01ffdf975fb67d06c7d62e95e65b0f7dcb Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 9 Apr 2026 21:55:47 -0700 Subject: [PATCH 3/5] fix workflow --- .github/workflows/submit_example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/submit_example.yaml b/.github/workflows/submit_example.yaml index ab6b2cb..fd2bcb2 100644 --- a/.github/workflows/submit_example.yaml +++ b/.github/workflows/submit_example.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: python-version: ['3.13'] - submission: ['null', 'example'] + submission: ['example'] steps: - uses: actions/checkout@v3 From 66c97c9499ac1e96bd6f3c0b976d2cffe4a62915 Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 9 Apr 2026 22:02:32 -0700 Subject: [PATCH 4/5] proper test --- tests/test_example.py | 82 +++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/tests/test_example.py b/tests/test_example.py index 06fc433..82c46cc 100644 --- a/tests/test_example.py +++ b/tests/test_example.py @@ -2,16 +2,15 @@ from pathlib import Path import pytest -# Put needed import here +from rail.core.data import TableHandle +from rail.estimation.algos import sklearn_neurnet +from rail.utils import catalog_utils -# These are used by test scripts from pz_data_challenge.taskset_1 import run_taskset_1 from pz_data_challenge.taskset_2 import run_taskset_2 + from pz_data_challenge import submit_utils -# Change these to match the name of the submission -# and a URL to download the sumission data files -# and needed model files SUBMISSION_NAME: str = "example" SUBMISSION_URL: str = "https://s3df.slac.stanford.edu/people/echarles/submit_example.tgz" @@ -22,16 +21,8 @@ @pytest.fixture(name="setup_submit_area", scope="module") def setup_submit_area(request: pytest.FixtureRequest) -> int: - """ - A pytest fixture to download the submission data - If all the submission data are in a tar file with the - proper structure you should not need to change this function. - """ - if not os.path.exists(SUBMIT_DIR): - if not SUBMISSION_URL: - raise ValueError(f"SUBMISSION_URL in tests/test_{SUBMISSION_NAME}.py has not been set") submit_utils.download_and_extract_tar(SUBMISSION_URL, SUBMIT_DIR) def teardown_submit_area() -> None: @@ -50,6 +41,9 @@ def teardown_submit_area() -> None: request.addfinalizer(teardown_submit_area) + catalog_utils.load_yaml("tests/catalogs.yaml") + catalog_utils.apply("cardinal_roman_rubin") + return 0 @@ -79,7 +73,16 @@ def run_taskset_1_estimation_only( Path to write the output data to. The output data should be written in qp format. """ - return + test_data = TableHandle("test", path=test_file) + estimator = sklearn_neurnet.SklNeurNetEstimator.make_stage( + name="estimate", + model=model_file, + output_mode="return", + ) + pz_out = estimator.estimate(test_data) + pz_out.data.ancil["object_id"] = test_data()["object_id"].astype(int) + pz_out.path = output_file + pz_out.write() def run_taskset_1_training_and_estimation( @@ -107,7 +110,23 @@ def run_taskset_1_training_and_estimation( Path to write the output data to. The output data should be written in qp format. """ - return + train_data = TableHandle("train", path=train_file) + test_data = TableHandle("test", path=test_file) + + informer = sklearn_neurnet.SklNeurNetInformer.make_stage( + name="inform", + ) + model = informer.inform(train_data) + + estimator = sklearn_neurnet.SklNeurNetEstimator.make_stage( + name="estimate", + model=model, + output_mode="return", + ) + pz_out = estimator.estimate(test_data) + pz_out.data.ancil["object_id"] = test_data()["object_id"].astype(int) + pz_out.path = output_file + pz_out.write() def run_taskset_2_estimation_only( @@ -136,7 +155,16 @@ def run_taskset_2_estimation_only( Path to write the output data to. The output data should be written in qp format. """ - return + test_data = TableHandle("test", path=test_file) + estimator = sklearn_neurnet.SklNeurNetEstimator.make_stage( + name="estimate", + model=model_file, + output_mode="return", + ) + pz_out = estimator.estimate(test_data) + pz_out.data.ancil["object_id"] = test_data()["object_id"].astype(int) + pz_out.path = output_file + pz_out.write() def run_taskset_2_training_and_estimation( @@ -161,6 +189,23 @@ def run_taskset_2_training_and_estimation( Path to write the output data to. The output data should be written in qp format. """ + train_data = TableHandle("train", path=train_file) + test_data = TableHandle("test", path=test_file) + + informer = sklearn_neurnet.SklNeurNetInformer.make_stage( + name="inform", + ) + model = informer.inform(train_data) + + estimator = sklearn_neurnet.SklNeurNetEstimator.make_stage( + name="estimate", + model=model, + output_mode="return", + ) + pz_out = estimator.estimate(test_data) + pz_out.data.ancil["object_id"] = test_data()["object_id"].astype(int) + pz_out.path = output_file + pz_out.write() def test_example_taskset_1( @@ -172,7 +217,7 @@ def test_example_taskset_1( You should not need to change this function """ - + assert setup_public_area == 0 assert setup_submit_area == 0 @@ -189,7 +234,7 @@ def test_example_taskset_2( setup_submit_area: int, ) -> None: """ - Test fuction to validate a submisson for Taskset 2 + Test fuction to validate a submisson for Taskset 1 You should not need to change this function """ @@ -203,3 +248,4 @@ def test_example_taskset_2( run_taskset_2_estimation_only, run_taskset_2_training_and_estimation, ) + From ffeb44798f6151b6d2bdf4f0b48382592b9e117e Mon Sep 17 00:00:00 2001 From: Eric Charles Date: Thu, 9 Apr 2026 22:16:52 -0700 Subject: [PATCH 5/5] fixed requirements file --- requirements_example.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements_example.txt b/requirements_example.txt index e69de29..2681716 100644 --- a/requirements_example.txt +++ b/requirements_example.txt @@ -0,0 +1,2 @@ +pz-rail-base +pz-rail-sklearn