Add a FastqName class to create and parse standardized fastq names.
[htsworkflow.git] / htsworkflow / submission / fastqname.py
1 """Standardize reading and writing fastq submission names.
2 """
3 import collections
4 import re
5 PAIRED_TEMPLATE = '{lib_id}_{flowcell}_c{cycle}_l{lane}_r{read}.fastq'
6 SINGLE_TEMPLATE = '{lib_id}_{flowcell}_c{cycle}_l{lane}.fastq'
7
8 FASTQ_RE = re.compile(
9     '(?P<lib_id>[^_]+)_(?P<flowcell>[^_]+)_'\
10     'c(?P<cycle>[\d]+)_l(?P<lane>[\d]+)(_r(?P<read>[\d]))?\.fastq')
11
12 class FastqName(collections.Mapping):
13     def __init__(self, is_paired=None, **kwargs):
14         self._attributes = ('flowcell', 'lib_id', 'lane', 'read', 'cycle')
15         self._is_paired = is_paired
16
17         if len(kwargs) == 0:
18             return
19         if 'filename' in kwargs:
20             self._init_by_filename(**kwargs)
21         else:
22             self._init_by_attributes(**kwargs)
23
24     def _init_by_attributes(self, **kwargs):
25         for k in self._attributes:
26             value = None
27             if k in kwargs:
28                 value = kwargs[k]
29             self[k] = value
30
31
32     def _init_by_filename(self, filename):
33         match = FASTQ_RE.match(filename)
34         if match is None:
35             raise ValueError('Is "{0}" a submission fastq?'.format(filename))
36
37         for k in self._attributes:
38             self[k] = match.group(k)
39
40     def _get_is_paired(self):
41         if self._is_paired is None:
42             return getattr(self, 'read', None) is not None
43         else:
44             return self._is_paired
45     def _set_is_paired(self, value):
46         self._is_paired = value
47     is_paired = property(_get_is_paired, _set_is_paired)
48
49     def _is_valid(self):
50         if self.is_paired and self['read'] is None:
51             return False
52
53         for k in self.keys():
54             if k == 'read':
55                 continue
56             if self[k] is None:
57                 return False
58         return True
59     is_valid = property(_is_valid)
60
61     def _get_filename(self):
62         if not self.is_valid:
63             raise ValueError(
64                 "Please set all needed variables before generating a filename")
65
66         T = PAIRED_TEMPLATE if self.is_paired else SINGLE_TEMPLATE
67         return T.format(**self)
68     filename = property(_get_filename)
69
70     def __iter__(self):
71         return iter(self._attributes)
72
73     def __getitem__(self, key):
74         return getattr(self, key, None)
75
76     def __setitem__(self, key, value):
77         if key in self._attributes:
78             setattr(self, key, value)
79         else:
80             raise ValueError("Unrecognized key {0}".format(key))
81
82     def __len__(self):
83         return len([k for k in self if self[k] is not None])