ef8d9457218a76ce53f0872f44efafd6d478dea1
[htsworkflow.git] / htsworkflow / submission / geo.py
1 import logging
2 import os
3
4 import RDF
5
6 from htsworkflow.submission.submission import Submission
7
8 from htsworkflow.util.rdfhelp import \
9      fromTypedNode, \
10      geoSoftNS, \
11      stripNamespace, \
12      submissionOntology
13
14 from django.conf import settings
15 from django.template import Context, loader
16
17 LOGGER = logging.getLogger(__name__)
18
19 class GEOSubmission(Submission):
20     def __init__(self, name, model, host):
21         super(GEOSubmission, self).__init__(name, model, host)
22
23     def make_soft(self, result_map):
24         samples = []
25         platform = self.get_platform_metadata()
26         platform_attribs = dict(platform)
27         platform_id = platform_attribs['^platform']
28         series = self.get_series_metadata()
29         series_attribs = dict(series)
30         series_id = series_attribs['^series']
31         for lib_id, result_dir in result_map.items():
32             an_analysis = self.get_submission_node(result_dir)
33             metadata = self.get_sample_metadata(an_analysis)
34             if len(metadata) == 0:
35                 errmsg = 'No metadata found for {0}'
36                 LOGGER.error(errmsg.format(str(an_analysis),))
37                 continue
38             elif len(metadata) > 1:
39                 errmsg = 'Confused there are more than one sample for %s'
40                 LOGGER.debug(errmsg % (str(an_analysis),))
41             metadata = metadata[0]
42             metadata['raw'] = self.get_raw_files(an_analysis)
43             metadata['supplimental'] = self.get_sample_files(an_analysis)
44             metadata['run'] = self.get_run_details(an_analysis)
45             samples.append(metadata)
46
47         soft_template = loader.get_template('geo_submission.soft')
48         context = Context({
49             'platform': platform,
50             'series': series,
51             'samples': samples,
52             'platform_id': platform_id,
53             'series_id': series_id,
54         })
55         print str(soft_template.render(context))
56
57     def check_for_name(self, analysis_node):
58         name = fromTypedNode(
59             self.model.get_target(analysis_node,
60                                   submissionOntology['name']))
61         if name is None:
62             logger.error("Need name for %s" % (str(analysis_node)))
63             return False
64         else:
65             return True
66
67     def get_platform_metadata(self):
68         """Gather information for filling out sample section of a SOFT file
69         """
70         query_template = loader.get_template('geo_platform.sparql')
71         submission = str(self.submissionSetNS[''].uri)
72         context = Context({
73             'submission': submission,
74             })
75
76         results = self.execute_query(query_template, context)
77         return self.query_to_soft_dictionary(results, 'platform')
78
79     def get_series_metadata(self):
80         """Gather information for filling out sample section of a SOFT file
81         """
82         query_template = loader.get_template('geo_series.sparql')
83         submission = str(self.submissionSetNS[''].uri)
84         context = Context({
85             'submission': submission,
86             })
87
88         results = self.execute_query(query_template, context)
89         return self.query_to_soft_dictionary(results, 'series')
90
91     def get_sample_metadata(self, analysis_node):
92         """Gather information for filling out sample section of a SOFT file
93         """
94         query_template = loader.get_template('geo_samples.sparql')
95
96         context = Context({
97             'submission': str(analysis_node.uri),
98             'submissionSet': str(self.submissionSetNS[''].uri),
99             })
100
101         results = self.execute_query(query_template, context)
102         for r in results:
103             r['dataProtocol'] = str(r['dataProtocol']).replace('\n', ' ')
104
105         return results
106
107     def get_sample_files(self, analysis_node):
108         """Gather derived files
109         """
110         query_template = loader.get_template('geo_files.sparql')
111
112         context = Context({
113             'submission': str(analysis_node.uri),
114             'file_class': str(geoSoftNS['supplemental'].uri)
115             })
116
117         return self.execute_query(query_template, context)
118
119     def get_raw_files(self, analysis_node):
120         """Gather raw data e.g. fastq files.
121         """
122         query_template = loader.get_template('geo_fastqs.sparql')
123
124         context = Context({
125             'submission': str(analysis_node.uri),
126             'file_class': str(geoSoftNS['raw'].uri),
127             })
128
129         lanes = {}
130         for row in self.execute_query(query_template, context):
131             data = {}
132             for k, v in row.items():
133                 data[k] = v
134             library = str(data['library'])
135             lanes.setdefault(library, []).append(data)
136         result = []
137         for library, files in lanes.items():
138             if len(files) > 2:
139                 errmsg = "Don't know what to do with more than 2 raw files"
140                 raise ValueError(errmsg)
141             elif len(files) == 2:
142                 is_paired = True
143             elif len(files) == 1:
144                 is_paired = False
145             elif len(files) == 0:
146                 raise RuntimeError("Empty library list discovered")
147             files = self._format_filename(files, is_paired)
148             files = self._format_flowcell_type(files, is_paired)
149             files = self._format_read_length(files, is_paired)
150             result.append(files[0])
151         return result
152
153     def _format_flowcell_type(self, files, is_paired):
154         """Used by get_raw_files to format value for single_or_paired-end
155         """
156         for f in files:
157             if 'flowcell_type' in f:
158                 flowcell_type = fromTypedNode(f['flowcell_type'])
159                 if flowcell_type is None:
160                     pass
161                 elif flowcell_type.lower() == "paired":
162                     f['flowcell_type'] = 'paired-end'
163                 else:
164                     f['flowcell_type'] = 'single'
165
166         return files
167
168     def _format_read_length(self, files, is_paired):
169         """Format
170         """
171         read_count = 2 if is_paired else 1
172         for f in files:
173             if 'read_length' in f:
174                 read_length = str(fromTypedNode(f['read_length']))
175                 f['read_length'] = ",".join([read_length] * read_count)
176         return files
177
178     def _format_filename(self, files, is_paired):
179         """Format file name for get_raw_files, also report if paired
180         """
181         if len(files) == 2:
182             # should be paired
183             f0 = files[0]
184             f1 = files[1]
185             f0['filename'] = "%s, %s" % (str(f0['filename']),
186                                          str(f1['filename']))
187             f0['md5sum'] = "%s, %s" % (str(f0['md5sum']),
188                                        str(f1['md5sum']))
189             del files[1]
190         else:
191             files[0]['filename'] = str(files[0]['filename'])
192             files[0]['md5sum'] = str(files[0]['md5sum'])
193         return files
194
195
196     def get_run_details(self, analysis_node):
197         """Get information about runs
198         """
199         query_template = loader.get_template('geo_run_details.sparql')
200
201         context = Context({
202             'submission': str(analysis_node.uri),
203             })
204
205         return self.execute_query(query_template, context)
206
207     def query_to_soft_dictionary(self, results, heading):
208         attributes = []
209         for r in results:
210             name = stripNamespace(geoSoftNS, r['name'])
211             if name is not None:
212                 if name.lower() == heading.lower():
213                     name = '^' + name
214                 else:
215                     name = '!' + name
216                 for v in fromTypedNode(r['value']).split(os.linesep):
217                     v = v.strip()
218                     if len(v) > 0:
219                         attributes.append((name, v))
220         return attributes