diff --git a/.github/workflows/test_and_deploy.yml b/.github/workflows/test_and_deploy.yml index ac0a507..271c9c2 100644 --- a/.github/workflows/test_and_deploy.yml +++ b/.github/workflows/test_and_deploy.yml @@ -21,46 +21,44 @@ jobs: runs-on: ${{ matrix.platform }} strategy: matrix: - platform: [ubuntu-latest, windows-latest, macos-latest] + platform: [ubuntu-latest] python-version: ['3.10'] - + defaults: + run: + shell: bash -el {0} steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - name: Set up conda ${{ matrix.python-version }} + uses: conda-incubator/setup-miniconda@v2 with: + mamba-version: "*" + activate-environment: napari-boxmanager + channel-priority: true python-version: ${{ matrix.python-version }} - - # these libraries enable testing on Qt on linux - - uses: tlambert03/setup-qt-libs@v1 - - # strategy borrowed from vispy for installing opengl libs on windows - - name: Install Windows OpenGL - if: runner.os == 'Windows' - run: | - git clone --depth 1 https://github.com/pyvista/gl-ci-helpers.git - powershell gl-ci-helpers/appveyor/install_opengl.ps1 - - # note: if you need dependencies from conda, considering using - # setup-miniconda: https://github.com/conda-incubator/setup-miniconda - # and - # tox-conda: https://github.com/tox-dev/tox-conda + channels: conda-forge, defaults + environment-file: conda_env.yml + - run: conda --version + - run: conda init bash + - run: | + which python - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install setuptools tox tox-gh-actions - - # this runs the platform-specific tests declared in tox.ini - - name: Test with tox - uses: GabrielBB/xvfb-action@v1 - with: - run: python -m tox + pip install setuptools setuptools_scm pylint tox tox-gh-actions pytest pytest-coverage twine build + pip install . + - name: Debug Info + run: | + which python + pip freeze + #- name: Analysing the code with pylint DEACTIVATED BECAUSE IT THROWS A LOT OF PYQT errors oO + # run: | + # pylint -E $(git ls-files '*.py') + - name: Tests + run: | + pytest -v --cov=./ --cov-report=xml --cov-config=.coveragerc env: PLATFORM: ${{ matrix.platform }} - - name: Coverage - uses: codecov/codecov-action@v2 - deploy: # this will run when you have tagged a commit, starting with "v*" # and requires that you have put your twine API key in your diff --git a/conda_env.yml b/conda_env.yml new file mode 100644 index 0000000..9f49be4 --- /dev/null +++ b/conda_env.yml @@ -0,0 +1,17 @@ +name: napari-boxmanager +channels: + - conda-forge + - defaults +dependencies: + - python=3.10 + - napari>=0.4.17 + - pyqt + - tifffile + - numpy + - matplotlib + - pip + - pip: + - pystardb>=0.4.2 + - scipy + - tqdm + - pandas[version='>=2'] diff --git a/reader_backup/_tests/test_box.py b/reader_backup/_tests/test_box.py deleted file mode 100644 index ec92e67..0000000 --- a/reader_backup/_tests/test_box.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -import pathlib - -import pandas -import pytest - -import box_manager.readers.box as brb - -TEST_DATA = pathlib.Path(os.path.dirname(__file__), "test_data") - - -@pytest.mark.parametrize("file_name", ["2d_center.box", "2d_center_float.box"]) -def test_read_box_file_center_correct_coord_values(file_name): - box_file = pathlib.Path(TEST_DATA, file_name) - box_data = brb.read(box_file) - - expected = pandas.DataFrame( - { - "x": [24, 36, 78], - "y": [40, 62, 51], - "box_x": [0, 0, 0], - "box_y": [0, 0, 0], - "filename": box_file, - } - ) - pandas.testing.assert_frame_equal(box_data, expected) - - -def test_read_box_file_left_correct_coord_values(): - file_name = "2d_left.box" - box_file = pathlib.Path(TEST_DATA, file_name) - box_data = brb.read(box_file) - - expected = pandas.DataFrame( - { - "x": [14, 26, 68], - "y": [35, 57, 46], - "box_x": [20, 20, 20], - "box_y": [10, 10, 10], - "filename": box_file, - } - ) - pandas.testing.assert_frame_equal(box_data, expected) - - -def test_read_box_file_empty_expect_empty_list(): - box_file = pathlib.Path(TEST_DATA, "2d_empty.box") - box_data = brb.read(box_file) - - expected = pandas.DataFrame( - columns=["x", "y", "box_x", "box_y", "filename"] - ) - pandas.testing.assert_frame_equal(box_data, expected, check_dtype=False) - - -def test_read_box_file_corrupt_column_expect_error(): - box_file = pathlib.Path(TEST_DATA, "2d_corrupt_unqual_columns.box") - with pytest.raises(brb.BoxFileNumberOfColumnsError): - brb.read(box_file) - - -def test_read_box_file_corrupt_string_expect_error(): - box_file = pathlib.Path(TEST_DATA, "2d_corrupt_has_string.box") - with pytest.raises(ValueError): - brb.read(box_file) - - -@pytest.mark.parametrize( - "params", [[[10, 10, 10], [0, 0, 0]], [[30, 35, 40]] * 2] -) -def test_prepare_napari_box_file_center_correct_coord_values(params): - expected_size, box_size = params - print - input_df = pandas.DataFrame( - { - "x": [ - 24 - box_size[0] // 2, - 36 - box_size[1] // 2, - 78 - box_size[2] // 2, - ], - "y": [ - 40 - box_size[0] // 2, - 62 - box_size[1] // 2, - 51 - box_size[2] // 2, - ], - "box_x": box_size, - "box_y": box_size, - "filename": "test", - } - ) - expected_df = pandas.DataFrame( - { - "x": [40, 62, 51], - "y": [24, 36, 78], - "boxsize": expected_size, - "sort_idx": "test", - "grp_idx": "test", - } - ) - data, coords_idx, metrics_idx, kwargs = brb.prepare_napari(input_df) - pandas.testing.assert_frame_equal(data, expected_df) - assert coords_idx == ["x", "y"] - assert metrics_idx == ["boxsize"] - assert kwargs == {"out_of_slice_display": False} diff --git a/reader_backup/_tests/test_data b/reader_backup/_tests/test_data deleted file mode 120000 index 077a737..0000000 --- a/reader_backup/_tests/test_data +++ /dev/null @@ -1 +0,0 @@ -../../../../test_data \ No newline at end of file diff --git a/reader_backup/_tests/test_init.py b/reader_backup/_tests/test_init.py deleted file mode 100644 index d927ca1..0000000 --- a/reader_backup/_tests/test_init.py +++ /dev/null @@ -1,15 +0,0 @@ -import glob -import os - -import box_manager.readers as bmr - - -def test_valid_functions_correct(): - - expected = [] - for module in glob.iglob(f"{os.path.dirname(__file__)}/../*.py"): - if os.path.basename(module) in bmr._ignore_list: - continue - expected.append(f".{os.path.splitext(os.path.basename(module))[0]}") - - assert sorted(expected) == list(sorted(bmr.valid_readers.keys())) diff --git a/reader_backup/_tests/test_interface.py b/reader_backup/_tests/test_interface.py deleted file mode 100644 index dd14993..0000000 --- a/reader_backup/_tests/test_interface.py +++ /dev/null @@ -1,118 +0,0 @@ -import os -import pathlib - -import numpy -import pandas - -import box_manager.readers as nrt - -TEST_DATA = pathlib.Path(os.path.dirname(__file__), "test_data") - - -def test_to_napari_file_correct_returns(): - tlpkl_file = pathlib.Path(TEST_DATA, "valid.tlpkl") - - points1, points2 = nrt.to_napari_coordinates(tlpkl_file, nrt.valid_readers[".tlpkl"]) - assert all([len(entry) == 3 for entry in (points1, points2)]) - - x = [86, 88, 6, 24, 78, 46, 30, 60, 76, 76, 70, 66, 30, 76] - y = [26, 2, 94, 40, 52, 58, 62, 86, 16, 60, 82, 44, 18, 94] - z = [98, 44, 92, 50, 48, 72, 64, 44, 92, 58, 48, 18, 28, 10] - points = pandas.DataFrame( - { - "x": z, - "y": y, - "z": x, - } - ) - pandas.testing.assert_frame_equal(points1[0], points.iloc[:8]) - pandas.testing.assert_frame_equal(points2[0], points.iloc[8:]) - - size = [18, 40, 90, 67, 89, 88, 82, 82, 76, 51, 11, 282, 248, 244] - metric_best = numpy.array( - [ - 0.639648, - 0.945801, - 0.947754, - 0.951172, - 0.953125, - 0.955566, - 0.956055, - 0.956543, - 0.534180, - 0.538574, - 0.550781, - 0.880371, - 0.885742, - 0.888184, - ], - dtype=numpy.float32, - ) - width = [37] * len(x) - height = [40] * len(x) - depth = [50] * len(x) - - boxsize = [numpy.array([width, height, depth]).mean().mean()] * len(x) - - metadata = { - "id": 1, - "size_min": min(size[:8]), - "size_max": max(size[:8]), - "metric_min": min(metric_best[:8]), - "metric_max": max(metric_best[:8]), - "boxsize_min": min(boxsize[:8]), - "boxsize_max": max(boxsize[:8]), - } - assert list(sorted(metadata.keys())) == list( - sorted(points1[1]["metadata"].keys()) - ) - for key in metadata.keys(): - val1 = metadata[key] - val2 = points1[1]["metadata"][key] - numpy.testing.assert_array_almost_equal(val1, val2) - - metadata = { - "id": 2, - "size_min": min(size[8:]), - "size_max": max(size[8:]), - "metric_min": min(metric_best[8:]), - "metric_max": max(metric_best[8:]), - "boxsize_min": min(boxsize[8:]), - "boxsize_max": max(boxsize[8:]), - } - assert list(sorted(metadata.keys())) == list( - sorted(points2[1]["metadata"].keys()) - ) - for key in metadata.keys(): - val1 = metadata[key] - val2 = points2[1]["metadata"][key] - numpy.testing.assert_array_almost_equal(val1, val2) - - features = { - "size": size[:8], - "metric": metric_best[:8], - "boxsize": boxsize[:8], - } - assert list(sorted(features.keys())) == list( - sorted(points1[1]["features"].keys()) - ) - for key in features.keys(): - val1 = features[key] - val2 = points1[1]["features"][key] - numpy.testing.assert_array_almost_equal(val1, val2) - - features = { - "size": size[8:], - "metric": metric_best[8:], - "boxsize": boxsize[8:], - } - assert list(sorted(features.keys())) == list( - sorted(points2[1]["features"].keys()) - ) - for key in features.keys(): - val1 = features[key] - val2 = points2[1]["features"][key] - numpy.testing.assert_array_almost_equal(val1, val2) - - numpy.testing.assert_array_equal(boxsize[:8], points1[1]["size"]) - numpy.testing.assert_array_equal(boxsize[8:], points2[1]["size"]) diff --git a/reader_backup/_tests/test_tlpkl.py b/reader_backup/_tests/test_tlpkl.py deleted file mode 100644 index c7a98e8..0000000 --- a/reader_backup/_tests/test_tlpkl.py +++ /dev/null @@ -1,104 +0,0 @@ -import os -import pathlib - -import numpy -import pandas - -import box_manager.readers.tlpkl as nrt - -TEST_DATA = pathlib.Path(os.path.dirname(__file__), "test_data") - - -def test_correct_read(): - tlpkl_file = pathlib.Path(TEST_DATA, "valid.tlpkl") - test_data = nrt.read(tlpkl_file) - - x = [86, 88, 6, 24, 78, 46, 30, 60, 76, 76, 70, 66, 30, 76] - y = [26, 2, 94, 40, 52, 58, 62, 86, 16, 60, 82, 44, 18, 94] - z = [98, 44, 92, 50, 48, 72, 64, 44, 92, 58, 48, 18, 28, 10] - size = [18, 40, 90, 67, 89, 88, 82, 82, 76, 51, 11, 282, 248, 244] - metric_best = numpy.array( - [ - 0.639648, - 0.945801, - 0.947754, - 0.951172, - 0.953125, - 0.955566, - 0.956055, - 0.956543, - 0.534180, - 0.538574, - 0.550781, - 0.880371, - 0.885742, - 0.888184, - ], - dtype=numpy.float32, - ) - width = [37] * len(x) - height = [40] * len(x) - depth = [50] * len(x) - predicted_class = [1] * 8 + [2] * 6 - predicted_class_name = ["cluster_2.pkl"] * 8 + ["cluster_3.pkl"] * 6 - expected = pandas.DataFrame( - { - "X": x, - "Y": y, - "Z": z, - "size": size, - "metric_best": metric_best, - "predicted_class": predicted_class, - "predicted_class_name": predicted_class_name, - "width": width, - "height": height, - "depth": depth, - } - ) - pandas.testing.assert_frame_equal(test_data[expected.columns], expected) - - -def test_correct_prepare_napari(): - x = [86, 88, 6] - y = [26, 2, 94] - z = [98, 44, 92] - size = [18, 40, 90] - metric_best = numpy.array( - [0.639648, 0.945801, 0.947754], dtype=numpy.float32 - ) - width = [37] * len(x) - height = [40] * len(x) - depth = [50] * len(x) - input_df = pandas.DataFrame( - { - "Z": z, - "Y": y, - "X": x, - "size": size, - "metric_best": metric_best, - "width": width, - "height": height, - "depth": depth, - "predicted_class_name": ["classx.pkl"] * 3, - "predicted_class": [0] * 3, - } - ) - expected = pandas.DataFrame( - { - "z": x, - "y": y, - "x": z, - "size": size, - "metric": metric_best, - "boxsize": numpy.mean(numpy.array([width, height, depth])), - "grp_idx": ["classx.pkl"] * 3, - "sort_idx": [0] * 3, - } - ) - test_df, test_coords, test_metadata, kwargs = nrt.prepare_napari(input_df) - assert test_coords == ["x", "y", "z"] - assert test_metadata == ["metric", "size", "boxsize"] - pandas.testing.assert_frame_equal( - test_df[expected.columns.values], expected - ) - assert kwargs == {} diff --git a/reader_backup/cbox.py b/reader_backup/cbox.py deleted file mode 100644 index 463c062..0000000 --- a/reader_backup/cbox.py +++ /dev/null @@ -1,18 +0,0 @@ -import typing - -import pandas as pd - -if typing.TYPE_CHECKING: - import pathlib - - import numpy as np - - -def read(path: "pathlib.Path") -> pd.DataFrame: - raise NotImplementedError - - -def prepare_napari( - path: "pathlib.Path", -) -> "tuple[np.ndarray, dict[str, typing.Any], dict[str, typing.Any], str]": - raise NotImplementedError diff --git a/reader_backup/star.py b/reader_backup/star.py deleted file mode 100644 index 463c062..0000000 --- a/reader_backup/star.py +++ /dev/null @@ -1,18 +0,0 @@ -import typing - -import pandas as pd - -if typing.TYPE_CHECKING: - import pathlib - - import numpy as np - - -def read(path: "pathlib.Path") -> pd.DataFrame: - raise NotImplementedError - - -def prepare_napari( - path: "pathlib.Path", -) -> "tuple[np.ndarray, dict[str, typing.Any], dict[str, typing.Any], str]": - raise NotImplementedError diff --git a/reader_backup/tepkl.py b/reader_backup/tepkl.py deleted file mode 100644 index 463c062..0000000 --- a/reader_backup/tepkl.py +++ /dev/null @@ -1,18 +0,0 @@ -import typing - -import pandas as pd - -if typing.TYPE_CHECKING: - import pathlib - - import numpy as np - - -def read(path: "pathlib.Path") -> pd.DataFrame: - raise NotImplementedError - - -def prepare_napari( - path: "pathlib.Path", -) -> "tuple[np.ndarray, dict[str, typing.Any], dict[str, typing.Any], str]": - raise NotImplementedError diff --git a/reader_backup/tlpkl.py b/reader_backup/tlpkl.py deleted file mode 100644 index e329a11..0000000 --- a/reader_backup/tlpkl.py +++ /dev/null @@ -1,55 +0,0 @@ -import os -import typing - -import pandas as pd - -if typing.TYPE_CHECKING: - pass - - -class DimZMissingWarning(Warning): - pass - - -def read(path: "os.PathLike") -> pd.DataFrame: - """ - Read a tlpkl conform file into memory. - tlpkl files need to have their coordinates for the Z and X dimension inverted. - - :param path: Path of the file to read information from. - :type path: Path object or str - - :return: DataFrame containing the coordinates, box size, metrics, and cluster size - :rtype: Pandas DataFrame - """ - pandas_data: pd.DataFrame = pd.read_pickle(path) - - pandas_data["X"] = pandas_data["X"].astype(int) - pandas_data["Y"] = pandas_data["Y"].astype(int) - pandas_data["Z"] = pandas_data["Z"].astype(int) - - return pandas_data - - -def prepare_napari( - input_df: pd.DataFrame, -) -> tuple[pd.DataFrame, list[str], list[str], typing.Any]: - coords_idx = ["x", "y", "z"] - metric_idx = ["metric", "size", "boxsize"] - util_idx = ["sort_idx", "grp_idx"] - output_data: pd.DataFrame = pd.DataFrame( - columns=coords_idx + metric_idx + util_idx - ) - - output_data["x"] = input_df["Z"] - output_data["y"] = input_df["Y"] - output_data["z"] = input_df["X"] - output_data["metric"] = input_df["metric_best"] - output_data["size"] = input_df["size"] - output_data["boxsize"] = input_df[["height", "width", "depth"]].mean( - axis=1 - ) - output_data["sort_idx"] = input_df["predicted_class"] - output_data["grp_idx"] = input_df["predicted_class_name"] - - return output_data, coords_idx, metric_idx, {} diff --git a/reader_backup/tmpkl.py b/reader_backup/tmpkl.py deleted file mode 100644 index 463c062..0000000 --- a/reader_backup/tmpkl.py +++ /dev/null @@ -1,18 +0,0 @@ -import typing - -import pandas as pd - -if typing.TYPE_CHECKING: - import pathlib - - import numpy as np - - -def read(path: "pathlib.Path") -> pd.DataFrame: - raise NotImplementedError - - -def prepare_napari( - path: "pathlib.Path", -) -> "tuple[np.ndarray, dict[str, typing.Any], dict[str, typing.Any], str]": - raise NotImplementedError diff --git a/reader_backup/to_napari_backup.txt b/reader_backup/to_napari_backup.txt deleted file mode 100644 index 28c8f88..0000000 --- a/reader_backup/to_napari_backup.txt +++ /dev/null @@ -1,58 +0,0 @@ - -#def to_napari( -# input_data: "os.PathLike | pd.DataFrame", -# reader: ReaderInterface | None = None, -#) -> "list[tuple[npt.ArrayLike, dict[str, typing.Any], str]]": -# """ -# Read a tlpkl conform file into memory to use within napari. -# -# :param path: Path of the file to read information from. -# :type path: pathlib.Path or str -# -# :return: Data to create a point, i.e., coords, point_kwargs, and type -# :rtype: list[tuple[npt.ArrayLike, dict[str, typing.Any], str]] -# """ -# if isinstance(input_data, (os.PathLike, str)): -# input_data = reader.read(input_data) -# data, coords_idx, metadata_idx, extra_kwargs = reader.prepare_napari( -# input_data -# ) -# -# colors = mcm.get_cmap("gist_rainbow") -# n_classes = np.unique(data["grp_idx"]).size -# n_classes = np.maximum(n_classes, 2) # Avoid zero division error -# -# output_list: "list[tuple[npt.ArrayLike, dict[str, typing.Any], str]]" = [] -# for idx, (cluster_name, cluster_df) in enumerate( -# data.sort_values(by=["sort_idx"]).groupby("grp_idx", sort=False) -# ): -# color = colors(idx / (n_classes - 1)) -# metadata = { -# f"{entry}_{func.__name__}": func(cluster_df[entry]) -# for func in [min, max] -# for entry in metadata_idx -# } -# metadata["id"] = cluster_df["sort_idx"].iloc[0] -# -# # to_numpy currently needed. Should be fixed in 0.4.16rc8 -# features = { -# entry: cluster_df[entry].to_numpy() for entry in metadata_idx -# } -# -# kwargs = { -# "edge_color": [color], -# "face_color": "transparent", -# "symbol": "disc", -# "edge_width": 2, -# "edge_width_is_relative": False, -# "size": cluster_df["boxsize"], -# "out_of_slice_display": True, -# "opacity": 0.5, -# "name": cluster_name, -# "metadata": metadata, -# "features": features, -# } -# kwargs.update(extra_kwargs) -# output_list.append((cluster_df[coords_idx], kwargs, "points")) -# -# return output_list diff --git a/setup.cfg b/setup.cfg index c04d386..51c5692 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,10 @@ install_requires = numpy <=1.23.5 pystardb>=0.4.2 pandas + pyqt5 + scipy + tifffile + tqdm python_requires = >=3.10 include_package_data = True package_dir = diff --git a/src/box_manager/_tests/_dummy_test.py b/src/box_manager/_tests/_dummy_test.py deleted file mode 100644 index 077934e..0000000 --- a/src/box_manager/_tests/_dummy_test.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_dumm(): - pass diff --git a/src/box_manager/io/interface.py b/src/box_manager/io/interface.py index 502695f..beb80f4 100644 --- a/src/box_manager/io/interface.py +++ b/src/box_manager/io/interface.py @@ -26,7 +26,7 @@ def to_napari( def get_valid_extensions(self) -> list[str]: ... - def has_shapes(path: os.PathLike) -> bool: + def has_shapes(self, path: os.PathLike) -> bool: ... def from_napari(