14e3b02b1fc3fc3440db40878e2beda1b074661e
[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             eland_summary.successful_pm = getattr(flowcell, pm_field)
263
264             summary_list.append(eland_summary)
265
266         #except Exception, e:
267         #    summary_list.append("Summary report needs to be updated.")
268         #    logging.error("Exception: " + str(e))
269     
270     return (summary_list, err_list)
271
272 def _summary_stats_old(flowcell_id, lane):
273     """
274     return a dictionary of summary stats for a given flowcell_id & lane.
275     """
276     fc_id, status = parse_flowcell_id(flowcell_id)
277     fc_result_dict = get_flowcell_result_dict(fc_id)
278     
279     dict_list = []
280     err_list = []
281     summary_list = []
282     
283     if fc_result_dict is None:
284         err_list.append('Results for Flowcell %s not found.' % (fc_id))
285         return (dict_list, err_list, summary_list)
286     
287     for cnm in fc_result_dict:
288     
289         xmlpath = fc_result_dict[cnm]['run_xml']
290         
291         if xmlpath is None:
292             err_list.append('Run xml for Flowcell %s(%s) not found.' % (fc_id, cnm))
293             continue
294         
295         tree = ElementTree.parse(xmlpath).getroot()
296         results = runfolder.PipelineRun(pathname='', xml=tree)
297         try:
298             lane_report = runfolder.summarize_lane(results.gerald, lane)
299             summary_list.append(os.linesep.join(lane_report))
300         except Exception, e:
301             summary_list.append("Summary report needs to be updated.")
302             logging.error("Exception: " + str(e))
303        
304         print "----------------------------------"
305         print "-- DOES NOT SUPPORT PAIRED END ---"
306         print "----------------------------------"
307         lane_results = results.gerald.summary[0][lane]
308         lrs = lane_results
309         
310         d = {}
311         
312         d['average_alignment_score'] = lrs.average_alignment_score
313         d['average_first_cycle_intensity'] = lrs.average_first_cycle_intensity
314         d['cluster'] = lrs.cluster
315         d['lane'] = lrs.lane
316         d['flowcell'] = flowcell_id
317         d['cnm'] = cnm
318         d['percent_error_rate'] = lrs.percent_error_rate
319         d['percent_intensity_after_20_cycles'] = lrs.percent_intensity_after_20_cycles
320         d['percent_pass_filter_align'] = lrs.percent_pass_filter_align
321         d['percent_pass_filter_clusters'] = lrs.percent_pass_filter_clusters
322         
323         #FIXME: function finished, but need to take advantage of
324         #   may need to take in a list of lanes so we only have to
325         #   load the xml file once per flowcell rather than once
326         #   per lane.
327         dict_list.append(d)
328     
329     return (dict_list, err_list, summary_list)
330     
331     
332 def get_eland_result_type(pathname):
333     """
334     Guess the eland result file type from the filename
335     """
336     path, filename = os.path.split(pathname)
337     if 'extended' in filename:
338         return 'extended'
339     elif 'multi' in filename:
340         return 'multi'
341     elif 'result' in filename:
342         return 'result'
343     else:
344         return 'unknown'
345
346 def _make_eland_results(flowcell_id, lane, interesting_flowcells):
347     fc_id, status = parse_flowcell_id(flowcell_id)
348     cur_fc = interesting_flowcells.get(fc_id, None)
349     if cur_fc is None:
350       return []
351
352     flowcell = FlowCell.objects.get(flowcell_id=flowcell_id)
353     # Loop throw storage devices if a result has been archived
354     storage_id_list = []
355     if cur_fc is not None:
356         for lts in flowcell.longtermstorage_set.all():
357             for sd in lts.storage_devices.all():
358                 # Use barcode_id if it exists
359                 if sd.barcode_id is not None and sd.barcode_id != '':
360                     storage_id_list.append(sd.barcode_id)
361                 # Otherwise use UUID
362                 else:
363                     storage_id_list.append(sd.uuid)
364         
365     # Formatting for template use
366     if len(storage_id_list) == 0:
367         storage_ids = None
368     else:
369         storage_ids = ', '.join([ '<a href="/inventory/%s/">%s</a>' % (s,s) for s in storage_id_list ])
370
371     results = []
372     for cycle in cur_fc.keys():
373         result_path = cur_fc[cycle]['eland_results'].get(lane, None)
374         result_link = make_result_link(fc_id, cycle, lane, result_path)
375         results.append({'flowcell_id': fc_id,
376                         'cycle': cycle, 
377                         'lane': lane, 
378                         'summary_url': make_summary_url(flowcell_id, cycle),
379                         'result_url': result_link[0],
380                         'result_label': result_link[1],
381                         'bed_url': result_link[2],
382                         'storage_ids': storage_ids
383         })
384     return results
385
386 def make_summary_url(flowcell_id, cycle_name):
387     url = '/results/%s/%s/summary/' % (flowcell_id, cycle_name)
388     return url
389
390 def make_result_link(flowcell_id, cycle_name, lane, eland_result_path):
391     if eland_result_path is None:
392         return ("", "", "")
393
394     result_type = get_eland_result_type(eland_result_path)
395     result_url = '/results/%s/%s/eland_result/%s' % (flowcell_id, cycle_name, lane)
396     result_label = 'eland %s' % (result_type,)
397     bed_url = None
398     if result_type == 'result':
399        bed_url_pattern = '/results/%s/%s/bedfile/%s'
400        bed_url = bed_url_pattern % (flowcell_id, cycle_name, lane)
401     
402     return (result_url, result_label, bed_url)
403
404 def _files(flowcell_id, lane):
405     """
406     Sets up available files for download
407     """
408     lane = int(lane)
409
410     flowcell_id, id = parse_flowcell_id(flowcell_id)
411     d = get_flowcell_result_dict(flowcell_id)
412     
413     if d is None:
414         return ''
415     
416     output = []
417     
418     # c_name == 'CN-M' (i.e. C1-33)
419     for c_name in d:
420         
421         if d[c_name]['summary'] is not None:
422             output.append('<a href="/results/%s/%s/summary/">summary(%s)</a>' \
423                           % (flowcell_id, c_name, c_name))
424         
425         erd = d[c_name]['eland_results']
426         if lane in erd:
427             result_type = get_eland_result_type(erd[lane])
428             result_url_pattern = '<a href="/results/%s/%s/eland_result/%s">eland %s(%s)</a>'
429             output.append(result_url_pattern % (flowcell_id, c_name, lane, result_type, c_name))
430             if result_type == 'result':
431                 bed_url_pattern = '<a href="/results/%s/%s/bedfile/%s">bedfile(%s)</a>'
432                 output.append(bed_url_pattern % (flowcell_id, c_name, lane, c_name))
433     
434     if len(output) == 0:
435         return ''
436     
437     return '(' + '|'.join(output) + ')'
438
439 def library_id_to_admin_url(request, lib_id):
440     lib = Library.objects.get(library_id=lib_id)
441     return HttpResponseRedirect('/admin/samples/library/%s' % (lib.id,))
442
443 @login_required
444 def user_profile(request):
445     """
446     Information about the user
447     """
448     context = {
449                 'page_name': 'User Profile',
450                 'media': '',
451                 #'bcmagic': BarcodeMagicForm(),
452                 #'select': 'settings',
453             }
454     context.update(SAMPLES_CONTEXT_DEFAULTS)
455     return render_to_response('registration/profile.html', context,
456                               context_instance=RequestContext(request))