From 4965dd473be87150a05eefe6da70da984bf79341 Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 20 May 2024 11:37:25 -0400 Subject: [PATCH 01/10] fix for #459 --- peppy/project.py | 4 ++++ tests/conftest.py | 5 +++++ .../example_custom_index/project_config.yaml | 2 +- .../example_custom_index/sample_table.csv | 2 +- tests/test_Project.py | 11 +++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/peppy/project.py b/peppy/project.py index 1e0ebb68..5c4524bd 100644 --- a/peppy/project.py +++ b/peppy/project.py @@ -659,6 +659,10 @@ def _assert_samples_have_names(self): ) else: raise InvalidSampleTableFileException(message) + else: + if self.st_index != SAMPLE_NAME_ATTR: + sample[SAMPLE_NAME_ATTR] = self.st_index + _LOGGER.warning(f"Setting sample.sample_name: {self.st_index}") def _auto_merge_duplicated_names(self): """ diff --git a/tests/conftest.py b/tests/conftest.py index 50c7ffd5..a689ffc1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -51,6 +51,11 @@ def example_pep_cfg_noname_path(request): return get_path_to_example_file(EPB, "noname", request.param) +@pytest.fixture +def example_pep_cfg_custom_index(request): + return get_path_to_example_file(EPB, "custom_index", request.param) + + @pytest.fixture def example_peps_cfg_paths(request): """ diff --git a/tests/data/example_peps-master/example_custom_index/project_config.yaml b/tests/data/example_peps-master/example_custom_index/project_config.yaml index 83f85a88..a9a777be 100644 --- a/tests/data/example_peps-master/example_custom_index/project_config.yaml +++ b/tests/data/example_peps-master/example_custom_index/project_config.yaml @@ -1,3 +1,3 @@ pep_version: "2.0.0" sample_table: sample_table.csv -sample_table_index: sample_id +sample_table_index: NOT_SAMPLE_NAME diff --git a/tests/data/example_peps-master/example_custom_index/sample_table.csv b/tests/data/example_peps-master/example_custom_index/sample_table.csv index 30228147..c9965421 100644 --- a/tests/data/example_peps-master/example_custom_index/sample_table.csv +++ b/tests/data/example_peps-master/example_custom_index/sample_table.csv @@ -1,3 +1,3 @@ -sample_id,protocol,file +NOT_SAMPLE_NAME,protocol,file frog_1,anySampleType,data/frog1_data.txt frog_2,anySampleType,data/frog2_data.txt diff --git a/tests/test_Project.py b/tests/test_Project.py index c77439ec..3c3c975f 100644 --- a/tests/test_Project.py +++ b/tests/test_Project.py @@ -322,6 +322,17 @@ def test_missing_sample_name_custom_index(self, example_pep_cfg_noname_path): p = Project(cfg=example_pep_cfg_noname_path, sample_table_index="id") assert p.sample_name_colname == "id" + @pytest.mark.parametrize( + "example_pep_cfg_custom_index", ["project_config.yaml"], indirect=True + ) + def test_sample_name_custom_index(self, example_pep_cfg_custom_index): + """ + Verify that sample_name attribute becomes st_index from cfg + """ + p = Project(cfg=example_pep_cfg_custom_index) + assert p.sample_name_colname == "NOT_SAMPLE_NAME" + assert p.samples[0].sample_name == "NOT_SAMPLE_NAME" + @pytest.mark.parametrize( "example_pep_cfg_path", ["basic"], From a012d156845aae3d512153ec038b7a4bf0d768ba Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 20 May 2024 11:40:23 -0400 Subject: [PATCH 02/10] remove logger_warning, its too verbose --- peppy/project.py | 1 - 1 file changed, 1 deletion(-) diff --git a/peppy/project.py b/peppy/project.py index 5c4524bd..1a368070 100644 --- a/peppy/project.py +++ b/peppy/project.py @@ -662,7 +662,6 @@ def _assert_samples_have_names(self): else: if self.st_index != SAMPLE_NAME_ATTR: sample[SAMPLE_NAME_ATTR] = self.st_index - _LOGGER.warning(f"Setting sample.sample_name: {self.st_index}") def _auto_merge_duplicated_names(self): """ From e97d3a9600b2ba88d97e6ccb123953a64cf6f2dd Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 20 May 2024 11:49:46 -0400 Subject: [PATCH 03/10] update lint gha --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 8b48ddf1..9705deab 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -8,4 +8,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: psf/black@stable + - uses: psf/black@20.8b1 From 9426553ca67f87d8889a797d62e7fae5d98f669a Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 20 May 2024 12:58:35 -0400 Subject: [PATCH 04/10] Revert "update lint gha" This reverts commit e97d3a9600b2ba88d97e6ccb123953a64cf6f2dd. --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 9705deab..8b48ddf1 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -8,4 +8,4 @@ jobs: steps: - uses: actions/checkout@v2 - uses: actions/setup-python@v2 - - uses: psf/black@20.8b1 + - uses: psf/black@stable From d68d31ecf8951e7a39125379bacca405dedfb73f Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 20 May 2024 13:04:02 -0400 Subject: [PATCH 05/10] re-lint with black 24.4.2 --- peppy/project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/peppy/project.py b/peppy/project.py index 1a368070..f4d6df25 100644 --- a/peppy/project.py +++ b/peppy/project.py @@ -1,6 +1,7 @@ """ Build a Project object. """ + import os import sys from collections.abc import Mapping, MutableMapping From f1bd23fac8235f64baf7429f06db09c075476858 Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Tue, 21 May 2024 14:39:36 -0400 Subject: [PATCH 06/10] make sample_name a property of sample instead --- peppy/project.py | 3 --- peppy/sample.py | 10 ++++++++++ tests/test_Project.py | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/peppy/project.py b/peppy/project.py index f4d6df25..2590ce71 100644 --- a/peppy/project.py +++ b/peppy/project.py @@ -660,9 +660,6 @@ def _assert_samples_have_names(self): ) else: raise InvalidSampleTableFileException(message) - else: - if self.st_index != SAMPLE_NAME_ATTR: - sample[SAMPLE_NAME_ATTR] = self.st_index def _auto_merge_duplicated_names(self): """ diff --git a/peppy/sample.py b/peppy/sample.py index 9f92a6ac..d28fdaff 100644 --- a/peppy/sample.py +++ b/peppy/sample.py @@ -302,6 +302,16 @@ def project(self): """ return self[PRJ_REF] + @property + def sample_name(self): + """ + Get the sample's name + + :return str: current sample name derived from project's st_index + """ + + return self[self[PRJ_REF].st_index] + # The __reduce__ function provides an interface for # correct object serialization with the pickle module. def __reduce__(self): diff --git a/tests/test_Project.py b/tests/test_Project.py index 3c3c975f..f5ce2883 100644 --- a/tests/test_Project.py +++ b/tests/test_Project.py @@ -331,7 +331,7 @@ def test_sample_name_custom_index(self, example_pep_cfg_custom_index): """ p = Project(cfg=example_pep_cfg_custom_index) assert p.sample_name_colname == "NOT_SAMPLE_NAME" - assert p.samples[0].sample_name == "NOT_SAMPLE_NAME" + assert p.samples[0].sample_name == "frog_1" @pytest.mark.parametrize( "example_pep_cfg_path", From b7a4aca05b437278d0c9b9d761b76ea053fe09ca Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Tue, 28 May 2024 12:49:19 -0400 Subject: [PATCH 07/10] update changelog and version for 0.40.2 point release --- docs/changelog.md | 4 ++++ peppy/_version.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index 4a96c87f..4db79aac 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. +## [0.40.2] -- 2024-05-28 +### Added +- added `sample_name` property to samples object. + ## [0.40.1] -- 2024-01-11 ### Fixed - Initializing Project with `NaN` value instead of `None` in `from_pandas` method diff --git a/peppy/_version.py b/peppy/_version.py index 01b570aa..47a53c14 100644 --- a/peppy/_version.py +++ b/peppy/_version.py @@ -1 +1 @@ -__version__ = "0.40.1" +__version__ = "0.40.2" From d42929419995cf24582cd997d6aae83a134ee29f Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:45:20 -0400 Subject: [PATCH 08/10] allow string representation of conveted yaml #369 --- peppy/sample.py | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/peppy/sample.py b/peppy/sample.py index d28fdaff..60d14677 100644 --- a/peppy/sample.py +++ b/peppy/sample.py @@ -4,6 +4,7 @@ from copy import copy as cp from logging import getLogger from string import Formatter +from typing import Optional, Union import pandas as pd import yaml @@ -134,31 +135,39 @@ def _obj2dict(obj, name=None): serial.update({"prj": grab_project_data(self[PRJ_REF])}) return serial - def to_yaml(self, path, add_prj_ref=False): + def to_yaml( + self, path: Optional[str] = None, add_prj_ref=False + ) -> Union[str, None]: """ - Serializes itself in YAML format. + Serializes itself in YAML format. Writes to file if path is provided, else returns string representation. :param str path: A file path to write yaml to; provide this or - the subs_folder_path + the subs_folder_path, defaults to None :param bool add_prj_ref: whether the project reference bound do the Sample object should be included in the YAML representation + :return str | None: returns string representation of sample yaml or None """ serial = self.to_dict(add_prj_ref=add_prj_ref) - path = os.path.expandvars(path) - if not os.path.exists(os.path.dirname(path)): - _LOGGER.warning( - "Could not write sample data to: {}. " - "Directory does not exist".format(path) - ) - return - with open(path, "w") as outfile: - try: - yaml_data = yaml.safe_dump(serial, default_flow_style=False) - except yaml.representer.RepresenterError: - _LOGGER.error("Serialized sample data: {}".format(serial)) - raise - outfile.write(yaml_data) - _LOGGER.debug("Sample data written to: {}".format(path)) + if path: + path = os.path.expandvars(path) + if os.path.exists(os.path.dirname(path)): + with open(path, "w") as outfile: + try: + yaml_data = yaml.safe_dump(serial, default_flow_style=False) + except yaml.representer.RepresenterError: + _LOGGER.error("Serialized sample data: {}".format(serial)) + raise + outfile.write(yaml_data) + _LOGGER.debug("Sample data written to: {}".format(path)) + else: + _LOGGER.warning( + "Could not write sample data to: {}. " + "Directory does not exist".format(path) + ) + return + else: + yaml_data = yaml.safe_dump(serial, stream=None, default_flow_style=False) + return yaml_data def derive_attribute(self, data_sources, attr_name): """ From b828d9f65675a98f077761bdd1451fa4866c3dd2 Mon Sep 17 00:00:00 2001 From: Donald Campbell <125581724+donaldcampbelljr@users.noreply.github.com> Date: Mon, 15 Jul 2024 11:06:58 -0400 Subject: [PATCH 09/10] add pytest coverage #369 --- tests/smoketests/test_Sample.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/smoketests/test_Sample.py b/tests/smoketests/test_Sample.py index a9a933bd..77e42fbf 100644 --- a/tests/smoketests/test_Sample.py +++ b/tests/smoketests/test_Sample.py @@ -58,6 +58,16 @@ def test_str_repr_correctness(self, example_pep_cfg_path): assert example_pep_cfg_path in str_repr assert "Sample '{}'".format(sample["sample_name"]) in str_repr + @pytest.mark.parametrize("example_pep_cfg_path", EXAMPLE_TYPES, indirect=True) + def test_sample_to_yaml_no_path(self, example_pep_cfg_path): + """ + Verify that to_yaml returns representation without requiring a path. + """ + p = Project(cfg=example_pep_cfg_path) + for sample in p.samples: + yaml_repr = sample.to_yaml() + assert "sample_name" in yaml_repr + @pytest.mark.parametrize("example_pep_cfg_path", ["basic"], indirect=True) def test_sheet_dict_excludes_private_attrs(self, example_pep_cfg_path): """ From 4027bed8a8e20b338ba8e19a1cee4f320bcdf326 Mon Sep 17 00:00:00 2001 From: Khoroshevskyi Date: Mon, 15 Jul 2024 14:44:43 -0400 Subject: [PATCH 10/10] updated tests --- tests/conftest.py | 5 ----- tests/test_Project.py | 8 +++----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a689ffc1..50c7ffd5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -51,11 +51,6 @@ def example_pep_cfg_noname_path(request): return get_path_to_example_file(EPB, "noname", request.param) -@pytest.fixture -def example_pep_cfg_custom_index(request): - return get_path_to_example_file(EPB, "custom_index", request.param) - - @pytest.fixture def example_peps_cfg_paths(request): """ diff --git a/tests/test_Project.py b/tests/test_Project.py index f165fb53..959ae082 100644 --- a/tests/test_Project.py +++ b/tests/test_Project.py @@ -330,14 +330,12 @@ def test_missing_sample_name_custom_index(self, example_pep_cfg_noname_path): p = Project(cfg=example_pep_cfg_noname_path, sample_table_index="id") assert p.sample_name_colname == "id" - @pytest.mark.parametrize( - "example_pep_cfg_custom_index", ["project_config.yaml"], indirect=True - ) - def test_sample_name_custom_index(self, example_pep_cfg_custom_index): + @pytest.mark.parametrize("example_pep_cfg_path", ["custom_index"], indirect=True) + def test_sample_name_custom_index(self, example_pep_cfg_path): """ Verify that sample_name attribute becomes st_index from cfg """ - p = Project(cfg=example_pep_cfg_custom_index) + p = Project(cfg=example_pep_cfg_path) assert p.sample_name_colname == "NOT_SAMPLE_NAME" assert p.samples[0].sample_name == "frog_1"