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