Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/minor_fixes' into minor_fixes
Browse files Browse the repository at this point in the history
# Conflicts:
#	peppy/_version.py
  • Loading branch information
khoroshevskyi committed Jul 16, 2024
2 parents b04eae4 + 24657d2 commit 395f12f
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 20 deletions.
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 37 additions & 18 deletions peppy/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
"""
Expand Down Expand Up @@ -302,6 +311,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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions tests/smoketests/test_Sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down
9 changes: 9 additions & 0 deletions tests/test_Project.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,15 @@ 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_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_path)
assert p.sample_name_colname == "NOT_SAMPLE_NAME"
assert p.samples[0].sample_name == "frog_1"

@pytest.mark.parametrize(
"example_pep_cfg_path",
["basic"],
Expand Down

0 comments on commit 395f12f

Please sign in to comment.