[med-svn] [Git][med-team/q2-demux][upstream] New upstream version 2019.10.0
Liubov Chuprikova
gitlab at salsa.debian.org
Sun Dec 29 15:13:58 GMT 2019
Liubov Chuprikova pushed to branch upstream at Debian Med / q2-demux
Commits:
ff693221 by Liubov Chuprikova at 2019-12-29T15:11:37Z
New upstream version 2019.10.0
- - - - -
24 changed files:
- q2_demux/__init__.py
- + q2_demux/_filter.py
- q2_demux/_summarize/_visualizer.py
- q2_demux/_version.py
- q2_demux/plugin_setup.py
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/MANIFEST
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/metadata.yml
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R2_001.fastq.gz
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz
- + q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R2_001.fastq.gz
- + q2_demux/tests/data/filter_samples_paired_end/filter_all.tsv
- + q2_demux/tests/data/filter_samples_paired_end/filter_none.tsv
- + q2_demux/tests/data/filter_samples_paired_end/filter_subset.tsv
- + q2_demux/tests/data/filter_samples_single_end/dir_fmt/MANIFEST
- + q2_demux/tests/data/filter_samples_single_end/dir_fmt/metadata.yml
- + q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz
- + q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz
- + q2_demux/tests/data/filter_samples_single_end/filter_all.tsv
- + q2_demux/tests/data/filter_samples_single_end/filter_none.tsv
- + q2_demux/tests/data/filter_samples_single_end/filter_subset.tsv
- q2_demux/tests/test_demux.py
- + q2_demux/tests/test_filter.py
- setup.py
Changes:
=====================================
q2_demux/__init__.py
=====================================
@@ -9,6 +9,7 @@
from ._demux import emp_single, emp_paired
from ._subsample import subsample_single, subsample_paired
from ._summarize import summarize
+from ._filter import filter_samples
from ._version import get_versions
@@ -16,4 +17,4 @@ __version__ = get_versions()['version']
del get_versions
__all__ = ['emp_single', 'emp_paired', 'summarize',
- 'subsample_single', 'subsample_paired']
+ 'subsample_single', 'subsample_paired', 'filter_samples']
=====================================
q2_demux/_filter.py
=====================================
@@ -0,0 +1,50 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2016-2019, QIIME 2 development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file LICENSE, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import os
+
+import pandas as pd
+
+from qiime2 import Metadata
+from qiime2.util import duplicate
+from q2_types.per_sample_sequences import \
+ CasavaOneEightSingleLanePerSampleDirFmt
+
+from ._summarize import _PlotQualView
+
+
+def filter_samples(demux: _PlotQualView, metadata: Metadata,
+ where: str = None, exclude_ids: bool = False) \
+ -> CasavaOneEightSingleLanePerSampleDirFmt:
+ results = CasavaOneEightSingleLanePerSampleDirFmt()
+
+ paired = demux.paired
+ samples = demux.directory_format
+
+ ids_to_keep = metadata.get_ids(where=where)
+ if not ids_to_keep:
+ raise ValueError('No filtering requested.')
+ manifest = samples.manifest.view(pd.DataFrame)
+
+ if exclude_ids:
+ ids_to_keep = set(manifest.index) - set(ids_to_keep)
+
+ try:
+ for id in ids_to_keep:
+ forward = manifest.loc[id].forward
+ duplicate(forward, os.path.join(str(results),
+ os.path.split(forward)[1]))
+ if paired:
+ reverse = manifest.loc[id].reverse
+ duplicate(reverse, os.path.join(str(results),
+ os.path.split(reverse)[1]))
+ except KeyError:
+ raise ValueError(f'{id!r} is not a sample present in the '
+ 'demultiplexed data.')
+
+ return results
=====================================
q2_demux/_summarize/_visualizer.py
=====================================
@@ -25,7 +25,7 @@ TEMPLATES = pkg_resources.resource_filename('q2_demux', '_summarize')
def _decode_qual_to_phred33(qual_str):
# this function is adapted from scikit-bio
- qual = np.fromstring(qual_str, dtype=np.uint8) - 33
+ qual = np.frombuffer(qual_str.encode('ascii'), dtype=np.uint8) - 33
return qual
=====================================
q2_demux/_version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (tag: 2019.4.1)"
- git_full = "7d08f111ea34d99d7eb77a3583eaf839ecfde05b"
- git_date = "2019-05-08 16:42:34 -0700"
+ git_refnames = " (tag: 2019.10.0)"
+ git_full = "aec76a7d22d7126b57b99da259d752e72e99f148"
+ git_date = "2019-11-01 01:04:23 +0000"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
q2_demux/plugin_setup.py
=====================================
@@ -9,8 +9,10 @@
import importlib
from qiime2.plugin import (
- Plugin, MetadataColumn, Categorical, Bool, Int, Float, Range, Citations
+ Plugin, Metadata, MetadataColumn, Categorical, Bool, Str, Int, Float,
+ Range, Citations, TypeMatch
)
+
from q2_types.sample_data import SampleData
from q2_types.per_sample_sequences import (
SequencesWithQuality, PairedEndSequencesWithQuality,
@@ -228,4 +230,43 @@ plugin.methods.register_function(
'sequences after subsampling.')
)
+T = TypeMatch([SequencesWithQuality, PairedEndSequencesWithQuality,
+ JoinedSequencesWithQuality])
+plugin.methods.register_function(
+ function=q2_demux.filter_samples,
+ inputs={'demux': SampleData[T]},
+ parameters={'metadata': Metadata,
+ 'where': Str,
+ 'exclude_ids': Bool},
+ outputs=[
+ ('filtered_demux', SampleData[T])
+ ],
+ input_descriptions={
+ 'demux': 'The demultiplexed data from which samples should be '
+ 'filtered.'
+ },
+ parameter_descriptions={
+ 'metadata': 'Sample metadata indicating which sample ids to filter. '
+ 'The optional `where` parameter may be used to filter ids '
+ 'based on specified conditions in the metadata. The '
+ 'optional `exclude_ids` parameter may be used to exclude '
+ 'the ids specified in the metadata from the filter.',
+ 'where': 'Optional SQLite WHERE clause specifying sample metadata '
+ 'criteria that must be met to be included in the filtered '
+ 'data. If not provided, all samples in `metadata` that are '
+ 'also in the demultiplexed data will be retained.',
+ 'exclude_ids': 'Defaults to False. If True, the samples selected by '
+ 'the `metadata` and optional `where` parameter will be '
+ 'excluded from the filtered data.',
+ },
+ output_descriptions={
+ 'filtered_demux': 'Filtered demultiplexed data.'
+ },
+ name='Filter samples out of demultiplexed data.',
+ description='Filter samples indicated in given metadata out of '
+ 'demultiplexed data. Specific samples can be further selected '
+ 'with the WHERE clause, and the `exclude_ids` parameter '
+ 'allows for filtering of all samples not specified.',
+)
+
importlib.import_module('q2_demux._transformer')
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/MANIFEST
=====================================
@@ -0,0 +1,5 @@
+sample-id,filename,direction
+sample1,sample1_1_L001_R1_001.fastq.gz,forward
+sample1,sample1_1_L001_R2_001.fastq.gz,reverse
+sample2,sample2_2_L001_R1_001.fastq.gz,forward
+sample2,sample2_2_L001_R2_001.fastq.gz,reverse
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/metadata.yml
=====================================
@@ -0,0 +1 @@
+phred-offset: 33
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R2_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample1_1_L001_R2_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R2_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_paired_end/dir_fmt/sample2_2_L001_R2_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_paired_end/filter_all.tsv
=====================================
@@ -0,0 +1,3 @@
+#SampleID Study
+sample1 A
+sample2 B
=====================================
q2_demux/tests/data/filter_samples_paired_end/filter_none.tsv
=====================================
@@ -0,0 +1,2 @@
+#SampleID Study
+1 A
=====================================
q2_demux/tests/data/filter_samples_paired_end/filter_subset.tsv
=====================================
@@ -0,0 +1,2 @@
+#SampleID Study
+sample1 A
=====================================
q2_demux/tests/data/filter_samples_single_end/dir_fmt/MANIFEST
=====================================
@@ -0,0 +1,6 @@
+sample-id,filename,direction
+# direction is not meaningful in this file as these
+# data may be derived from forward, reverse, or
+# joined reads
+sample1,sample1_1_L001_R1_001.fastq.gz,forward
+sample2,sample2_2_L001_R1_001.fastq.gz,forward
=====================================
q2_demux/tests/data/filter_samples_single_end/dir_fmt/metadata.yml
=====================================
@@ -0,0 +1 @@
+phred-offset: 33
=====================================
q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample1_1_L001_R1_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz
=====================================
Binary files /dev/null and b/q2_demux/tests/data/filter_samples_single_end/dir_fmt/sample2_2_L001_R1_001.fastq.gz differ
=====================================
q2_demux/tests/data/filter_samples_single_end/filter_all.tsv
=====================================
@@ -0,0 +1,3 @@
+#SampleID Study
+sample1 A
+sample2 B
=====================================
q2_demux/tests/data/filter_samples_single_end/filter_none.tsv
=====================================
@@ -0,0 +1,2 @@
+#SampleID Study
+1 A
=====================================
q2_demux/tests/data/filter_samples_single_end/filter_subset.tsv
=====================================
@@ -0,0 +1,2 @@
+#SampleID Study
+sample1 A
=====================================
q2_demux/tests/test_demux.py
=====================================
@@ -18,7 +18,6 @@ import pandas as pd
import pandas.testing as pdt
import skbio
import qiime2
-import numpy as np
import numpy.testing as npt
from qiime2.plugin.testing import TestPluginBase
@@ -28,7 +27,8 @@ from q2_demux import emp_single, emp_paired, summarize
from q2_types.per_sample_sequences import (
FastqGzFormat, FastqManifestFormat,
SingleLanePerSampleSingleEndFastqDirFmt)
-from q2_demux._summarize._visualizer import _PlotQualView
+from q2_demux._summarize._visualizer import (_PlotQualView,
+ _decode_qual_to_phred33)
class BarcodeSequenceFastqIteratorTests(unittest.TestCase):
@@ -172,17 +172,12 @@ class BarcodeSequenceFastqIteratorTests(unittest.TestCase):
class EmpTestingUtils:
- def _decode_qual_to_phred(self, qual_str):
- # this function is adapted from scikit-bio
- qual = np.fromstring(qual_str, dtype=np.uint8) - 33
- return qual
-
def _compare_sequence_to_record(self, sequence, fields):
header_line = ' '.join([sequence.metadata['id'],
sequence.metadata['description']])
self.assertEqual(fields[0][1:], header_line)
self.assertEqual(fields[1], str(sequence))
- npt.assert_array_equal(self._decode_qual_to_phred(fields[3]),
+ npt.assert_array_equal(_decode_qual_to_phred33(fields[3]),
sequence.positional_metadata['quality'])
def _compare_manifests(self, act_manifest, exp_manifest):
=====================================
q2_demux/tests/test_filter.py
=====================================
@@ -0,0 +1,235 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2016-2019, QIIME 2 development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file LICENSE, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import unittest
+import os
+
+import pandas as pd
+
+from qiime2 import Metadata
+from qiime2.plugin.testing import TestPluginBase
+from q2_types.per_sample_sequences import (
+ SingleLanePerSampleSingleEndFastqDirFmt,
+ SingleLanePerSamplePairedEndFastqDirFmt)
+
+from q2_demux._filter import filter_samples
+from q2_demux._summarize import _PlotQualView
+
+
+class FilterSamplesTests(TestPluginBase):
+ package = 'q2_demux.tests'
+
+ def setUp(self):
+ super().setUp()
+
+ data_single = SingleLanePerSampleSingleEndFastqDirFmt(
+ self.get_data_path('filter_samples_single_end/dir_fmt'), mode='r')
+ self.sample_single = _PlotQualView(data_single, False)
+ self.manifest_single = data_single.manifest.view(pd.DataFrame)
+
+ self.md_single_all = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_all.tsv'))
+ self.md_single_subset = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_subset.tsv'))
+ self.md_single_none = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_none.tsv'))
+
+ data_paired = SingleLanePerSamplePairedEndFastqDirFmt(
+ self.get_data_path('filter_samples_paired_end/dir_fmt'), mode='r')
+ self.sample_paired = _PlotQualView(data_paired, True)
+ self.manifest_paired = data_paired.manifest.view(pd.DataFrame)
+
+ self.md_paired_all = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_all.tsv'))
+ self.md_paired_subset = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_subset.tsv'))
+ self.md_paired_none = Metadata.load(
+ self.get_data_path('filter_samples_single_end/filter_none.tsv'))
+
+ def _assert_single_contains(self, dir_fmt, exp_ids):
+ obs_ids = [file.split('_', 1)[0] for file in
+ os.listdir(str(dir_fmt))]
+ self.assertEqual(set(obs_ids), set(exp_ids))
+
+ def _assert_paired_contains(self, dir_fmt, exp_ids):
+ obs_ids = [(file.split('_')[0::3]) for file in
+ os.listdir(str(dir_fmt))]
+ obs_ids = [(id[0] + id[1]) for id in obs_ids]
+ self.assertEqual(set(obs_ids), set(exp_ids))
+
+ def test_filter_single_all(self):
+ exps = [(None, ['sample1', 'sample2']),
+ ("Study='A'", ['sample1']),
+ ("Study='B'", ['sample2']),
+ ("Study='A' OR Study='B'", ['sample1', 'sample2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_single, self.md_single_all,
+ where, False)
+ self._assert_single_contains(dir_fmt, exp)
+
+ def test_filter_single_all_exclude(self):
+ exps = [(None, []),
+ ("Study='A'", ['sample2']),
+ ("Study='B'", ['sample1']),
+ ("Study='A' OR Study='B'", [])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_single, self.md_single_all,
+ where, True)
+ self._assert_single_contains(dir_fmt, exp)
+
+ def test_filter_single_subset(self):
+ exps = [(None, ['sample1']),
+ ("Study='A'", ['sample1']),
+ ("Study='A' OR Study='B'", ['sample1'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_single,
+ self.md_single_subset, where, False)
+ self._assert_single_contains(dir_fmt, exp)
+
+ def test_filter_single_subset_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_single, self.md_single_subset,
+ where, False)
+
+ def test_filter_single_subset_exclude(self):
+ exps = [(None, ['sample2']),
+ ("Study='A'", ['sample2']),
+ ("Study='A' OR Study='B'", ['sample2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_single,
+ self.md_single_subset, where, True)
+ self._assert_single_contains(dir_fmt, exp)
+
+ def test_filter_sinlge_subset_exclude_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_single, self.md_single_subset,
+ where, True)
+
+ def test_filter_single_none_exclude(self):
+ exps = [(None, ['sample1', 'sample2']),
+ ("Study='A'", ['sample1', 'sample2']),
+ ("Study='A' OR Study='B'", ['sample1', 'sample2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_single, self.md_single_none,
+ where, True)
+ self._assert_single_contains(dir_fmt, exp)
+
+ def test_filter_single_id_not_present(self):
+ wheres = [None, "Study='A'", "Study='A' OR Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, '1'):
+ filter_samples(self.sample_single, self.md_single_none, where,
+ False)
+
+ def test_filter_single_none_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_single, self.md_single_none, where,
+ False)
+
+ def test_filter_single_none_exclude_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_single, self.md_single_none, where,
+ True)
+
+ def test_filter_paired_all(self):
+ exps = [(None, ['sample1R1', 'sample1R2', 'sample2R1',
+ 'sample2R2']),
+ ("Study='A'", ['sample1R1', 'sample1R2']),
+ ("Study='B'", ['sample2R1', 'sample2R2']),
+ ("Study='A' OR Study='B'", ['sample1R1', 'sample1R2',
+ 'sample2R1', 'sample2R2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_paired, self.md_paired_all,
+ where, False)
+ self._assert_paired_contains(dir_fmt, exp)
+
+ def test_filter_paired_all_exclude(self):
+ exps = [(None, []),
+ ("Study='A'", ['sample2R1', 'sample2R2']),
+ ("Study='B'", ['sample1R1', 'sample1R2']),
+ ("Study='A' OR Study='B'", [])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_paired, self.md_paired_all,
+ where, True)
+ self._assert_paired_contains(dir_fmt, exp)
+
+ def test_filter_paired_subset(self):
+ exps = [(None, ['sample1R1', 'sample1R2']),
+ ("Study='A'", ['sample1R1', 'sample1R2']),
+ ("Study='A' OR Study='B'", ['sample1R1', 'sample1R2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_paired,
+ self.md_paired_subset, where, False)
+ self._assert_paired_contains(dir_fmt, exp)
+
+ def test_filter_paired_subset_no_filtering(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_paired, self.md_paired_subset,
+ where, False)
+
+ def test_filter_paired_subset_exclude(self):
+ exps = [(None, ['sample2R1', 'sample2R2']),
+ ("Study='A'", ['sample2R1', 'sample2R2']),
+ ("Study='A' OR Study='B'", ['sample2R1', 'sample2R2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_paired,
+ self.md_paired_subset, where, True)
+ self._assert_paired_contains(dir_fmt, exp)
+
+ def test_filter_paired_subset_exclude_no_filtering(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_paired, self.md_paired_subset,
+ where, True)
+
+ def test_filter_paired_none_exclude(self):
+ exps = [(None, ['sample1R1', 'sample1R2', 'sample2R1', 'sample2R2']),
+ ("Study='A'", ['sample1R1', 'sample1R2', 'sample2R1',
+ 'sample2R2']),
+ ("Study='A' OR Study='B'", ['sample1R1', 'sample1R2',
+ 'sample2R1', 'sample2R2'])]
+ for (where, exp) in exps:
+ dir_fmt = filter_samples(self.sample_paired, self.md_paired_none,
+ where, True)
+ self._assert_paired_contains(dir_fmt, exp)
+
+ def test_filter_paired_id_not_present(self):
+ wheres = [None, "Study='A'", "Study='A' OR Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, '1'):
+ filter_samples(self.sample_paired, self.md_paired_none, where,
+ False)
+
+ def test_filter_paired_none_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_paired, self.md_paired_none, where,
+ False)
+
+ def test_filter_paired_none_exclude_no_filter(self):
+ wheres = ["Study='B'", "Study='A' AND Study='B'"]
+ for where in wheres:
+ with self.assertRaisesRegex(ValueError, 'No'):
+ filter_samples(self.sample_paired, self.md_paired_none, where,
+ True)
+
+
+if __name__ == '__main__':
+ unittest.main()
=====================================
setup.py
=====================================
@@ -33,6 +33,10 @@ setup(
'data/single_sample_multiple_files/*',
'data/subsample_single_end/*',
'data/subsample_paired_end/*',
+ 'data/filter_samples_single_end/*',
+ 'data/filter_samples_single_end/dir_fmt/*',
+ 'data/filter_samples_paired_end/*',
+ 'data/filter_samples_paired_end/dir_fmt/*'
],
'q2_demux': ['_summarize/assets/*.html',
'_summarize/assets/dist/*',
View it on GitLab: https://salsa.debian.org/med-team/q2-demux/commit/ff6932213e5dc598ad500abce0b1884e8e575cdb
--
View it on GitLab: https://salsa.debian.org/med-team/q2-demux/commit/ff6932213e5dc598ad500abce0b1884e8e575cdb
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20191229/f7ed5cc2/attachment-0001.html>
More information about the debian-med-commit
mailing list