Provide a dummy eland result lane class if there's nothing appropriate in the xml
[htsworkflow.git] / htsworkflow / frontend / samples / views.py
1 # Create your views here.
2 from htsworkflow.frontend.experiments.models import FlowCell
3 from htsworkflow.frontend.samples.changelist import ChangeList
4 from htsworkflow.frontend.samples.models import Library
5 from htsworkflow.frontend.samples.results import get_flowcell_result_dict, parse_flowcell_id
6 from htsworkflow.frontend.bcmagic.forms import BarcodeMagicForm
7 from htsworkflow.pipelines.runfolder import load_pipeline_run_xml
8 from htsworkflow.pipelines import runfolder
9 from htsworkflow.pipelines.eland import ResultLane
10 from htsworkflow.frontend import settings
11 from htsworkflow.util import makebed
12 from htsworkflow.util import opener
13
14 from django.core.exceptions import ObjectDoesNotExist
15 from django.http import HttpResponse, HttpResponseRedirect
16 from django.shortcuts import render_to_response
17 from django.template import RequestContext
18 from django.template.loader import get_template
19 from django.contrib.auth.decorators import login_required
20
21 import StringIO
22 import logging
23 import os
24
25 LANE_LIST = [1,2,3,4,5,6,7,8]
26 SAMPLES_CONTEXT_DEFAULTS = {
27     'app_name': 'Flowcell/Library Tracker',
28     'bcmagic': BarcodeMagicForm()
29 }
30
31 def create_library_context(cl):
32     """
33     Create a list of libraries that includes how many lanes were run
34     """
35     records = []
36     #for lib in library_items.object_list:
37     for lib in cl.result_list:
38        summary = {}
39        summary['library_id'] = lib.library_id
40        summary['library_name'] = lib.library_name
41        summary['species_name' ] = lib.library_species.scientific_name
42        if lib.amplified_from_sample is not None:
43            summary['amplified_from'] = lib.amplified_from_sample.library_id
44        else:
45            summary['amplified_from'] = ''
46        lanes_run = 0
47        for lane_id in LANE_LIST:
48            lane = getattr(lib, 'lane_%d_library' % (lane_id,))
49            lanes_run += len( lane.all() )
50        summary['lanes_run'] = lanes_run
51        summary['is_archived'] = lib.is_archived()
52        records.append(summary)
53     cl.result_count = unicode(cl.paginator._count) + u" libraries"
54     return {'library_list': records }
55
56 def library(request):
57    # build changelist
58     fcl = ChangeList(request, Library,
59         list_filter=['affiliations', 'library_species'],
60         search_fields=['library_id', 'library_name', 'amplified_from_sample__library_id'],
61         list_per_page=200,
62         queryset=Library.objects.filter(hidden__exact=0)
63     )
64
65     context = { 'cl': fcl, 'title': 'Library Index'}
66     context.update(create_library_context(fcl))
67     t = get_template('samples/library_index.html')
68     c = RequestContext(request, context)
69     
70     app_context = {
71         'page_name': 'Library Index',
72         'east_region_config_div': 'changelist-filter',
73         'body': t.render(c)
74     }
75     app_context.update(SAMPLES_CONTEXT_DEFAULTS)
76     
77     app_t = get_template('flowcell_libraries_app.html')
78     app_c = RequestContext(request, app_context)
79     return HttpResponse( app_t.render(app_c) )
80
81 def library_to_flowcells(request, lib_id):
82     """
83     Display information about all the flowcells a library has been run on.
84     """
85     
86     try:
87       lib = Library.objects.get(library_id=lib_id)
88     except:
89       return HttpResponse("Library %s does not exist" % (lib_id))
90    
91     flowcell_list = []
92     interesting_flowcells = {} # aka flowcells we're looking at
93     for lane in LANE_LIST:
94         lane_library = getattr(lib, 'lane_%d_library' % (lane,))
95         for fc in lane_library.all():
96             flowcell_id, id = parse_flowcell_id(fc.flowcell_id)
97             if flowcell_id not in interesting_flowcells:
98                 interesting_flowcells[flowcell_id] = get_flowcell_result_dict(flowcell_id)
99             flowcell_list.append((fc.flowcell_id, lane))
100
101     flowcell_list.sort()
102     
103     lane_summary_list = []
104     eland_results = []
105     for fc, lane in flowcell_list:
106         lane_summary, err_list = _summary_stats(fc, lane)
107
108         eland_results.extend(_make_eland_results(fc, lane, interesting_flowcells))
109         lane_summary_list.extend(lane_summary)
110
111     context = {
112         'page_name': 'Library Details',
113         'lib': lib,
114         'eland_results': eland_results,
115         'lane_summary_list': lane_summary_list,
116     }
117     context.update(SAMPLES_CONTEXT_DEFAULTS)
118
119     return render_to_response(
120         'samples/library_detail.html',
121         context,
122         context_instance = RequestContext(request))
123
124 def summaryhtm_fc_cnm(request, flowcell_id, cnm):
125     """
126     returns a Summary.htm file if it exists.
127     """
128     fc_id, status = parse_flowcell_id(flowcell_id)
129     d = get_flowcell_result_dict(fc_id)
130     
131     if d is None:
132         return HttpResponse('<b>Results for Flowcell %s not found.</b>' % (fc_id))
133     
134     if cnm not in d:
135         return HttpResponse('<b>Results for Flowcell %s; %s not found.</b>' % (fc_id, cnm))
136     
137     summary_filepath = d[cnm]['summary']
138     
139     if summary_filepath is None:
140         return HttpResponse('<b>Summary.htm for Flowcell %s; %s not found.</b>' % (fc_id, cnm))
141     
142     f = open(summary_filepath, 'r')
143     
144     return HttpResponse(f)
145
146
147 def result_fc_cnm_eland_lane(request, flowcell_id, cnm, lane):
148     """
149     returns an eland_file upon calling.
150     """
151     fc_id, status = parse_flowcell_id(flowcell_id)
152     d = get_flowcell_result_dict(fc_id)
153     
154     if d is None:
155         return HttpResponse('<b>Results for Flowcell %s not found.</b>' % (fc_id))
156     
157     if cnm not in d:
158         return HttpResponse('<b>Results for Flowcell %s; %s not found.</b>' % (fc_id, cnm))
159     
160     erd = d[cnm]['eland_results']
161     lane = int(lane)
162     
163     if lane not in erd:
164         return HttpResponse('<b>Results for Flowcell %s; %s; lane %s not found.</b>' % (fc_id, cnm, lane))
165     
166     filepath = erd[lane]
167     
168     #f = opener.autoopen(filepath, 'r')
169     # return HttpResponse(f, mimetype="application/x-elandresult")
170
171     f = open(filepath, 'r')
172     return HttpResponse(f, mimetype='application/x-bzip2')
173     
174
175
176 def bedfile_fc_cnm_eland_lane_ucsc(request, fc_id, cnm, lane):
177     """
178     returns a bed file for a given flowcell, CN-M (i.e. C1-33), and lane (ucsc compatible)
179     """
180     return bedfile_fc_cnm_eland_lane(request, fc_id, cnm, lane, ucsc_compatible=True)
181
182
183 def bedfile_fc_cnm_eland_lane(request, flowcell_id, cnm, lane, ucsc_compatible=False):
184     """
185     returns a bed file for a given flowcell, CN-M (i.e. C1-33), and lane
186     """
187     fc_id, status = parse_flowcell_id(flowcell_id)
188     d = get_flowcell_result_dict(fc_id)
189     
190     if d is None:
191         return HttpResponse('<b>Results for Flowcell %s not found.</b>' % (fc_id))
192     
193     if cnm not in d:
194         return HttpResponse('<b>Results for Flowcell %s; %s not found.</b>' % (fc_id, cnm))
195     
196     erd = d[cnm]['eland_results']
197     lane = int(lane)
198     
199     if lane not in erd:
200         return HttpResponse('<b>Results for Flowcell %s; %s; lane %s not found.</b>' % (fc_id, cnm, lane))
201     
202     filepath = erd[lane]
203     
204     # Eland result file
205     fi = opener.autoopen(filepath, 'r')
206     # output memory file
207     
208     name, description = makebed.make_description( fc_id, lane )
209     
210     bedgen = makebed.make_bed_from_eland_generator(fi, name, description)
211     
212     if ucsc_compatible:
213         return HttpResponse(bedgen)
214     else:
215         return HttpResponse(bedgen, mimetype="application/x-bedfile")
216
217
218 def _summary_stats(flowcell_id, lane_id):
219     """
220     Return the summary statistics for a given flowcell, lane, and end.
221     """
222     fc_id, status = parse_flowcell_id(flowcell_id)
223     fc_result_dict = get_flowcell_result_dict(fc_id)
224
225     summary_list = []
226     err_list = []
227     
228     if fc_result_dict is None:
229         err_list.append('Results for Flowcell %s not found.' % (fc_id))
230         return (summary_list, err_list)
231
232     for cycle_width in fc_result_dict:
233         xmlpath = fc_result_dict[cycle_width]['run_xml']
234         
235         if xmlpath is None:
236             err_list.append('Run xml for Flowcell %s(%s) not found.' % (fc_id, cycle_width))
237             continue
238         
239         run = load_pipeline_run_xml(xmlpath)
240         gerald_summary = run.gerald.summary.lane_results
241         for end in range(len(gerald_summary)):
242             end_summary = run.gerald.eland_results.results[end]
243             if end_summary.has_key(lane_id):
244                 eland_summary = run.gerald.eland_results.results[end][lane_id]
245             else:
246                 eland_summary = ResultLane(lane_id=lane_id, end=end)
247             # add information to lane_summary
248             eland_summary.flowcell_id = flowcell_id
249             eland_summary.clusters = gerald_summary[end][lane_id].cluster
250             eland_summary.cycle_width = cycle_width
251             if hasattr(eland_summary, 'genome_map'):
252                 eland_summary.summarized_reads = runfolder.summarize_mapped_reads( 
253                                                    eland_summary.genome_map, 
254                                                    eland_summary.mapped_reads)
255
256             # grab some more information out of the flowcell db
257             flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
258             pm_field = 'lane_%d_pM' % (lane_id)
259             eland_summary.successful_pm = getattr(flowcell, pm_field)
260
261             summary_list.append(eland_summary)
262
263         #except Exception, e:
264         #    summary_list.append("Summary report needs to be updated.")
265         #    logging.error("Exception: " + str(e))
266     
267     return (summary_list, err_list)
268
269 def _summary_stats_old(flowcell_id, lane):
270     """
271     return a dictionary of summary stats for a given flowcell_id & lane.
272     """
273     fc_id, status = parse_flowcell_id(flowcell_id)
274     fc_result_dict = get_flowcell_result_dict(fc_id)
275     
276     dict_list = []
277     err_list = []
278     summary_list = []
279     
280     if fc_result_dict is None:
281         err_list.append('Results for Flowcell %s not found.' % (fc_id))
282         return (dict_list, err_list, summary_list)
283     
284     for cnm in fc_result_dict:
285     
286         xmlpath = fc_result_dict[cnm]['run_xml']
287         
288         if xmlpath is None:
289             err_list.append('Run xml for Flowcell %s(%s) not found.' % (fc_id, cnm))
290             continue
291         
292         tree = ElementTree.parse(xmlpath).getroot()
293         results = runfolder.PipelineRun(pathname='', xml=tree)
294         try:
295             lane_report = runfolder.summarize_lane(results.gerald, lane)
296             summary_list.append(os.linesep.join(lane_report))
297         except Exception, e:
298             summary_list.append("Summary report needs to be updated.")
299             logging.error("Exception: " + str(e))
300        
301         print "----------------------------------"
302         print "-- DOES NOT SUPPORT PAIRED END ---"
303         print "----------------------------------"
304         lane_results = results.gerald.summary[0][lane]
305         lrs = lane_results
306         
307         d = {}
308         
309         d['average_alignment_score'] = lrs.average_alignment_score
310         d['average_first_cycle_intensity'] = lrs.average_first_cycle_intensity
311         d['cluster'] = lrs.cluster
312         d['lane'] = lrs.lane
313         d['flowcell'] = flowcell_id
314         d['cnm'] = cnm
315         d['percent_error_rate'] = lrs.percent_error_rate
316         d['percent_intensity_after_20_cycles'] = lrs.percent_intensity_after_20_cycles
317         d['percent_pass_filter_align'] = lrs.percent_pass_filter_align
318         d['percent_pass_filter_clusters'] = lrs.percent_pass_filter_clusters
319         
320         #FIXME: function finished, but need to take advantage of
321         #   may need to take in a list of lanes so we only have to
322         #   load the xml file once per flowcell rather than once
323         #   per lane.
324         dict_list.append(d)
325     
326     return (dict_list, err_list, summary_list)
327     
328     
329 def get_eland_result_type(pathname):
330     """
331     Guess the eland result file type from the filename
332     """
333     path, filename = os.path.split(pathname)
334     if 'extended' in filename:
335         return 'extended'
336     elif 'multi' in filename:
337         return 'multi'
338     elif 'result' in filename:
339         return 'result'
340     else:
341         return 'unknown'
342
343 def _make_eland_results(flowcell_id, lane, interesting_flowcells):
344     fc_id, status = parse_flowcell_id(flowcell_id)
345     cur_fc = interesting_flowcells.get(fc_id, None)
346     if cur_fc is None:
347       return []
348
349     flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
350     # Loop throw storage devices if a result has been archived
351     storage_id_list = []
352     if cur_fc is not None:
353         for lts in flowcell.longtermstorage_set.all():
354             for sd in lts.storage_devices.all():
355                 # Use barcode_id if it exists
356                 if sd.barcode_id is not None and sd.barcode_id != '':
357                     storage_id_list.append(sd.barcode_id)
358                 # Otherwise use UUID
359                 else:
360                     storage_id_list.append(sd.uuid)
361         
362     # Formatting for template use
363     if len(storage_id_list) == 0:
364         storage_ids = None
365     else:
366         storage_ids = ', '.join(storage_id_list)
367
368     results = []
369     for cycle in cur_fc.keys():
370         result_path = cur_fc[cycle]['eland_results'].get(lane, None)
371         result_link = make_result_link(fc_id, cycle, lane, result_path)
372         results.append({'flowcell_id': fc_id,
373                         'cycle': cycle, 
374                         'lane': lane, 
375                         'summary_url': make_summary_url(flowcell_id, cycle),
376                         'result_url': result_link[0],
377                         'result_label': result_link[1],
378                         'bed_url': result_link[2],
379                         'storage_ids': storage_ids
380         })
381     return results
382
383 def make_summary_url(flowcell_id, cycle_name):
384     url = '/results/%s/%s/summary/' % (flowcell_id, cycle_name)
385     return url
386
387 def make_result_link(flowcell_id, cycle_name, lane, eland_result_path):
388     if eland_result_path is None:
389         return ("", "", "")
390
391     result_type = get_eland_result_type(eland_result_path)
392     result_url = '/results/%s/%s/eland_result/%s' % (flowcell_id, cycle_name, lane)
393     result_label = 'eland %s' % (result_type,)
394     bed_url = None
395     if result_type == 'result':
396        bed_url_pattern = '/results/%s/%s/bedfile/%s'
397        bed_url = bed_url_pattern % (flowcell_id, cycle_name, lane)
398     
399     return (result_url, result_label, bed_url)
400
401 def _files(flowcell_id, lane):
402     """
403     Sets up available files for download
404     """
405     lane = int(lane)
406
407     flowcell_id, id = parse_flowcell_id(flowcell_id)
408     d = get_flowcell_result_dict(flowcell_id)
409     
410     if d is None:
411         return ''
412     
413     output = []
414     
415     # c_name == 'CN-M' (i.e. C1-33)
416     for c_name in d:
417         
418         if d[c_name]['summary'] is not None:
419             output.append('<a href="/results/%s/%s/summary/">summary(%s)</a>' \
420                           % (flowcell_id, c_name, c_name))
421         
422         erd = d[c_name]['eland_results']
423         if lane in erd:
424             result_type = get_eland_result_type(erd[lane])
425             result_url_pattern = '<a href="/results/%s/%s/eland_result/%s">eland %s(%s)</a>'
426             output.append(result_url_pattern % (flowcell_id, c_name, lane, result_type, c_name))
427             if result_type == 'result':
428                 bed_url_pattern = '<a href="/results/%s/%s/bedfile/%s">bedfile(%s)</a>'
429                 output.append(bed_url_pattern % (flowcell_id, c_name, lane, c_name))
430     
431     if len(output) == 0:
432         return ''
433     
434     return '(' + '|'.join(output) + ')'
435
436 def library_id_to_admin_url(request, lib_id):
437     lib = Library.objects.get(library_id=lib_id)
438     return HttpResponseRedirect('/admin/samples/library/%s' % (lib.id,))
439
440 @login_required
441 def user_profile(request):
442     """
443     Information about the user
444     """
445     context = {
446                 'page_name': 'User Profile',
447                 'media': '',
448                 #'bcmagic': BarcodeMagicForm(),
449                 #'select': 'settings',
450             }
451     context.update(SAMPLES_CONTEXT_DEFAULTS)
452     return render_to_response('registration/profile.html', context,
453                               context_instance=RequestContext(request))