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