Skip to content

Commit

Permalink
qsip data subtypes
Browse files Browse the repository at this point in the history
  • Loading branch information
colinvwood committed Sep 13, 2024
1 parent a949c75 commit 17607bd
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 44 deletions.
14 changes: 7 additions & 7 deletions q2_qsip2/plugin_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from q2_qsip2 import __version__
from q2_qsip2.workflow import standard_workflow, create_qsip_data
from q2_qsip2.types import QSIP2Data
from q2_qsip2.types import QSIP2Data, Unfiltered, Filtered, EAF
from q2_qsip2.visualizers._visualizers import (
plot_weighted_average_densities, plot_sample_curves, plot_density_outliers,
show_comparison_groups
Expand All @@ -40,7 +40,7 @@
function=standard_workflow,
inputs={
'table': FeatureTable[Frequency],
'qsip_metadata': QSIP2Data,
'qsip_metadata': QSIP2Data[Unfiltered],
},
parameters={},
outputs=[
Expand Down Expand Up @@ -76,7 +76,7 @@
'gradient_pos_amt_column': Str,
},
outputs=[
('qsip_data', QSIP2Data)
('qsip_data', QSIP2Data[Unfiltered])
],
input_descriptions={},
parameter_descriptions={
Expand All @@ -101,7 +101,7 @@
plugin.visualizers.register_function(
function=plot_weighted_average_densities,
inputs={
'qsip_data': QSIP2Data
'qsip_data': QSIP2Data[Unfiltered]
},
parameters={
'group': Str
Expand All @@ -124,7 +124,7 @@
plugin.visualizers.register_function(
function=plot_sample_curves,
inputs={
'qsip_data': QSIP2Data
'qsip_data': QSIP2Data[Unfiltered]
},
parameters={},
input_descriptions={
Expand All @@ -141,7 +141,7 @@
plugin.visualizers.register_function(
function=plot_density_outliers,
inputs={
'qsip_data': QSIP2Data
'qsip_data': QSIP2Data[Unfiltered]
},
parameters={},
input_descriptions={
Expand All @@ -159,7 +159,7 @@
plugin.visualizers.register_function(
function=show_comparison_groups,
inputs={
'qsip_data': QSIP2Data
'qsip_data': QSIP2Data[Unfiltered]
},
parameters={
'groups': List[Str]
Expand Down
11 changes: 8 additions & 3 deletions q2_qsip2/types/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from q2_qsip2.types._formats import (
QSIP2DataFormat, QSIP2DataDirectoryFormat
QSIP2DataUnfilteredFormat, QSIP2DataUnfilteredDirectoryFormat,
QSIP2DataFilteredFormat, QSIP2DataFilteredDirectoryFormat,
QSIP2DataEAFFormat, QSIP2DataEAFDirectoryFormat
)
from q2_qsip2.types._types import QSIP2Data
from q2_qsip2.types._types import QSIP2Data, Unfiltered, Filtered, EAF

__all__ = [
'QSIP2DataFormat', 'QSIP2DataDirectoryFormat', 'QSIP2Data'
'QSIP2Data', 'Unfiltered', 'Filtered', 'EAF',
'QSIP2DataUnfilteredFormat', 'QSIP2DataUnfilteredDirectoryFormat',
'QSIP2DataFilteredFormat', 'QSIP2DataFilteredDirectoryFormat',
'QSIP2DataEAFFormat', 'QSIP2DataEAFDirectoryFormat'
]
42 changes: 35 additions & 7 deletions q2_qsip2/types/_deferred_setup/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,49 @@
import importlib

from q2_qsip2.plugin_setup import plugin
from q2_qsip2.types import (
QSIP2Data, QSIP2DataFormat, QSIP2DataDirectoryFormat
from q2_qsip2.types._types import (
QSIP2Data, Unfiltered, Filtered, EAF
)
from q2_qsip2.types._formats import (
QSIP2DataUnfilteredFormat, QSIP2DataUnfilteredDirectoryFormat,
QSIP2DataFilteredFormat, QSIP2DataFilteredDirectoryFormat,
QSIP2DataEAFFormat, QSIP2DataEAFDirectoryFormat
)


plugin.register_semantic_types(QSIP2Data)
plugin.register_semantic_types(QSIP2Data, Unfiltered, Filtered, EAF)

plugin.register_formats(
QSIP2DataFormat, QSIP2DataDirectoryFormat
QSIP2DataUnfilteredFormat, QSIP2DataUnfilteredDirectoryFormat,
QSIP2DataFilteredFormat, QSIP2DataFilteredDirectoryFormat,
QSIP2DataEAFFormat, QSIP2DataEAFDirectoryFormat
)

plugin.register_artifact_class(
QSIP2Data[Unfiltered],
directory_format=QSIP2DataUnfilteredDirectoryFormat,
description=(
'Represents initial imported qSIP2 data, containing source- and '
'sample-level metadata and a feature table.'
)
)

plugin.register_artifact_class(
QSIP2Data[Filtered],
directory_format=QSIP2DataFilteredDirectoryFormat,
description=(
'Represents filtered qSIP2 data, containg labeled and unlabeled '
'sources that constitute a desired comparison.'
)
)

plugin.register_artifact_class(
QSIP2Data,
directory_format=QSIP2DataDirectoryFormat,
description="A serialized qSIP2 data object."
QSIP2Data[EAF],
directory_format=QSIP2DataEAFDirectoryFormat,
description=(
'Represents fully process qSIP2 data that has been resampled and '
'had excess atom fractions (EAF) calculated.'
)
)

importlib.import_module('._transformers', __name__)
48 changes: 40 additions & 8 deletions q2_qsip2/types/_deferred_setup/_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,53 @@
import pickle

from q2_qsip2.plugin_setup import plugin
from q2_qsip2.types import QSIP2DataFormat
from q2_qsip2.types import (
QSIP2DataUnfilteredFormat, QSIP2DataFilteredFormat, QSIP2DataEAFFormat
)


@plugin.register_transformer
def _1(qsip_object: RS4) -> QSIP2DataFormat:
ff = QSIP2DataFormat()
def _format_to_qsip_object(ff):
with ff.open() as fh:
qsip_object = pickle.load(fh)

return qsip_object


def _qsip_object_to_format(qsip_object, ff):
with ff.open() as fh:
pickle.dump(qsip_object, fh)

return ff


@plugin.register_transformer
def _2(ff: QSIP2DataFormat) -> RS4:
with ff.open() as fh:
qsip_object = pickle.load(fh)
def _1(qsip_object: RS4) -> QSIP2DataUnfilteredFormat:
ff = QSIP2DataUnfilteredFormat()
return _qsip_object_to_format(qsip_object, ff)

return qsip_object

@plugin.register_transformer
def _2(ff: QSIP2DataUnfilteredFormat) -> RS4:
return _format_to_qsip_object(ff)


@plugin.register_transformer
def _3(qsip_object: RS4) -> QSIP2DataFilteredFormat:
ff = QSIP2DataFilteredFormat()
return _qsip_object_to_format(qsip_object, ff)


@plugin.register_transformer
def _4(ff: QSIP2DataFilteredFormat) -> RS4:
return _format_to_qsip_object(ff)


@plugin.register_transformer
def _5(qsip_object: RS4) -> QSIP2DataEAFFormat:
ff = QSIP2DataFilteredFormat()
return _qsip_object_to_format(qsip_object, ff)


@plugin.register_transformer
def _6(ff: QSIP2DataEAFFormat) -> RS4:
return _format_to_qsip_object(ff)
45 changes: 41 additions & 4 deletions q2_qsip2/types/_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@


# TODO: communicate warning about using pickled data
class QSIP2DataFormat(model.BinaryFileFormat):
class QSIP2DataFormatBase(model.BinaryFileFormat):
package = 'q2_qsip2.types.tests'

def stage_specific_validation_method(self, qsip_data_obj):
pass

def _validate_(self, level):
with self.open() as fh:
qsip_data_obj = pickle.load(fh)

try:
# TODO: why not implemented in R package?
ro.r['validate'](qsip_data_obj)
self.stage_specific_validation_method(qsip_data_obj)
except Exception as e:
msg = (
'There was a problem loading your qSIP2 data. See the below '
Expand All @@ -33,6 +36,40 @@ def _validate_(self, level):
raise ValidationError(msg)


QSIP2DataDirectoryFormat = model.SingleFileDirectoryFormat(
'QSIP2DataDirectoryFormat', 'qsip-data.pickle', QSIP2DataFormat
class QSIP2DataUnfilteredFormat(QSIP2DataFormatBase):
def stage_specific_validation_method(self, qsip_data_obj):
# TODO: update once implemented in R
pass


QSIP2DataUnfilteredDirectoryFormat = model.SingleFileDirectoryFormat(
'QSIP2DataUnfilteredDirectoryFormat',
'qsip-data.pickle',
QSIP2DataUnfilteredFormat
)


class QSIP2DataFilteredFormat(QSIP2DataFormatBase):
def stage_speicif_validation_method(self, qsip_data_obj):
# TODO: update once implemented in R
pass


QSIP2DataFilteredDirectoryFormat = model.SingleFileDirectoryFormat(
'QSIP2DataFilteredDirectoryFormat',
'qsip-data.pickle',
QSIP2DataFilteredFormat
)


class QSIP2DataEAFFormat(QSIP2DataFormatBase):
def stage_specific_validation_method(self, qsip_data_obj):
# TODO: update once implemented in R
pass


QSIP2DataEAFDirectoryFormat = model.SingleFileDirectoryFormat(
'QSIP2DataEAFDirectoryFormat',
'qsip-data.pickle',
QSIP2DataEAFFormat
)
8 changes: 7 additions & 1 deletion q2_qsip2/types/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@
from qiime2.plugin import SemanticType


QSIP2Data = SemanticType('QSIP2Data')
QSIP2Data = SemanticType('QSIP2Data', field_names='stage')

Unfiltered = SemanticType('Unfiltered', variant_of=QSIP2Data.field['stage'])

Filtered = SemanticType('Filtered', variant_of=QSIP2Data.field['stage'])

EAF = SemanticType('EAF', variant_of=QSIP2Data.field['stage'])
14 changes: 7 additions & 7 deletions q2_qsip2/types/tests/test_formats.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from qiime2.plugin import ValidationError
from qiime2.plugin.testing import TestPluginBase

from q2_qsip2.types import QSIP2DataFormat
from q2_qsip2.types import QSIP2DataUnfilteredFormat
from q2_qsip2.workflow import create_qsip_data


Expand All @@ -47,31 +47,31 @@ def get_feature_table(self):
df.values, observation_ids=df.index, sample_ids=df.columns
)

def test_valid_QSIP2DataFormat_from_files(self):
def test_valid_QSIP2DataUnfilteredFormat_from_files(self):
source_md = self.get_source_metadata()
sample_md = self.get_sample_metadata()
table = self.get_feature_table()

qsip_object = create_qsip_data(table, sample_md, source_md)

transformer = self.get_transformer(
RS4, QSIP2DataFormat
RS4, QSIP2DataUnfilteredFormat
)
format = transformer(qsip_object)

format.validate()

def test_valid_QSIP2DataFormat_from_pickle(self):
def test_valid_QSIP2DataUnfilteredFormat_from_pickle(self):
pickle_fp = (
importlib.resources.files(__package__) /
'data' / 'qsip-data.pickle'
)

format = QSIP2DataFormat(pickle_fp, mode='r')
format = QSIP2DataUnfilteredFormat(pickle_fp, mode='r')

format.validate()

def test_invalid_QSIP2DataFormat(self):
def test_invalid_QSIP2DataUnfilteredFormat(self):
# create useless rpy2 object to pickle
vector = ro.r("c('Q', 'I', 'I', 'M', 'E', '2')")

Expand All @@ -81,7 +81,7 @@ def test_invalid_QSIP2DataFormat(self):
with open(fp, 'wb') as fh:
pickle.dump(vector, fh)

format = QSIP2DataFormat(fp, mode='r')
format = QSIP2DataUnfilteredFormat(fp, mode='r')

msg = 'There was a problem loading your qSIP2 data.*'
with self.assertRaisesRegex(ValidationError, msg):
Expand Down
14 changes: 9 additions & 5 deletions q2_qsip2/types/tests/test_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from qiime2.plugin.testing import TestPluginBase

from q2_qsip2.types import QSIP2DataFormat
from q2_qsip2.types import QSIP2DataUnfilteredFormat


class TestTransformers(TestPluginBase):
Expand All @@ -26,13 +26,17 @@ def get_qsip_object(self):
'data' / 'qsip-data.pickle'
)

transformer = self.get_transformer(QSIP2DataFormat, RS4)
transformer = self.get_transformer(QSIP2DataUnfilteredFormat, RS4)

return transformer(QSIP2DataFormat(pickle_fp, mode='r'))
return transformer(QSIP2DataUnfilteredFormat(pickle_fp, mode='r'))

def test_object_to_pickle_file_and_back(self):
from_object_transformer = self.get_transformer(RS4, QSIP2DataFormat)
from_format_transformer = self.get_transformer(QSIP2DataFormat, RS4)
from_object_transformer = self.get_transformer(
RS4, QSIP2DataUnfilteredFormat
)
from_format_transformer = self.get_transformer(
QSIP2DataUnfilteredFormat, RS4
)

qsip_object = self.get_qsip_object()
ro.r['validate'](qsip_object)
Expand Down
7 changes: 5 additions & 2 deletions q2_qsip2/types/tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@

from qiime2.plugin.testing import TestPluginBase

from q2_qsip2.types import QSIP2Data
from q2_qsip2.types import QSIP2Data, Unfiltered, Filtered, EAF


class TestTypes(TestPluginBase):
package = 'q2_qsip2.types.tests'

def test_QSIP2Data_type_is_registered(self):
def test_QSIP2Data_types_registered(self):
self.assertRegisteredSemanticType(QSIP2Data)
self.assertRegisteredSemanticType(Unfiltered)
self.assertRegisteredSemanticType(Filtered)
self.assertRegisteredSemanticType(EAF)

0 comments on commit 17607bd

Please sign in to comment.