Added ability to print container labels.
[htsworkflow.git] / samplebc / samples / views.py
1 from django.http import HttpResponse, HttpResponseRedirect
2 from samplebc.samples import models
3 from django.core.exceptions import ObjectDoesNotExist
4 from django.template import Context, Template, RequestContext
5 from django.template.loader import get_template
6 from django.shortcuts import render_to_response
7
8 from django.utils.safestring import mark_safe
9
10 from samplebc.samples.forms import FreezerForm, ContainerForm, SampleForm
11 from samplebc.samples.models import Freezer, Container, Sample
12 from samplebc.samples.util import get_uuid, get_sampleid, assign_to_container
13 from samplebc.samples.errors import NoSpaceAvaliable
14 from samplebc import settings
15
16 # Barcode Magic!
17 from samplebc.bcmagic.forms import BarcodeMagicForm
18
19
20 import random
21 import ftplib
22 import StringIO
23
24
25 ################################################
26 # Util functions
27 ################################################
28
29
30 def print_zpl(zpl_text):
31     """
32     Sends zpl_text to printer
33     """
34     ftp = ftplib.FTP(host='131.215.54.194', user='blank', passwd='')
35     ftp.login()
36     ftp.storlines("STOR printme.txt", StringIO.StringIO(zpl_text))
37
38
39 def __center_line(line, width):
40     spaces_to_add = width - len(line)
41     
42     front_half = spaces_to_add / 2
43     back_half = spaces_to_add - front_half
44     
45     return (' ' * front_half) + line + (' ' * back_half)    
46
47
48   
49   
50 ################################################
51 # Container
52 ################################################
53
54 def container_index(request):
55     """
56     Returns an index of available to containers
57     """
58     container_list = Container.objects.all()
59     c = Context({'title': 'Container Index',
60                  'container_list': container_list})
61     t = get_template('container_index.html')
62     
63     return render_to_response('app.html', {
64         'app_name': "HTSW-SampleTracker",
65         'page_name': 'Container Index',
66         'bcmagic': BarcodeMagicForm(),
67         'select': 'container',
68         'body': t.render(c)
69     })
70     
71
72
73     
74 def container_summary(request, container_id):
75     """
76     Returns details of a container
77     """
78     # Retrieve container by UUID
79     try:
80         container = models.Container.objects.get(uuid=container_id)
81     except ObjectDoesNotExist, e:
82         msg = "Container (%s) does not exist." % (container_id)
83         return render_to_response('app.html', {
84             'app_name': settings.HTSW_ST_APPNAME,
85             'page_name': 'Container Summary',
86             'media': '',
87             'bcmagic': BarcodeMagicForm(),
88             'select': 'container',
89             'body': msg
90         })
91     
92     # Retrieve samples from container
93     sample_list = container.sample_set.all()
94     
95     # Prepare a Sample Index of Contained Samples
96     cs = Context({'title': 'Contained Samples',
97                  'sample_list': sample_list})
98     ts = get_template('sample_index.html')
99     
100     # Render prepared samples into container summary
101     cc = Context({'container': container,
102                  'rendered_samples': ts.render(cs) })
103     tc = get_template('container_summary.html')
104     
105     # Render container summary to app html
106     return render_to_response('app.html', {
107         'app_name': settings.HTSW_ST_APPNAME,
108         'page_name': 'Container Summary',
109         'media': '',
110         'bcmagic': BarcodeMagicForm(),
111         'select': 'container',
112         'body': tc.render(cc)
113     })
114
115
116 def container_add(request):
117     """
118     Form for adding a container.
119     """
120     # If user submitted the Container form.
121     if request.method == 'POST':
122         # Fill in ContainerForm with POSTed data.
123         form = ContainerForm(request.POST)
124         
125         # If the form is valid, process it.
126         if form.is_valid():
127             # Create new Container object from form data
128             c_obj = form.save(commit=False)
129             # Added UUID
130             c_obj.uuid = get_uuid()
131             # Save to DB.
132             c_obj.save()
133             # Save the many to many link data.
134             form.save_m2m()
135             return container_summary(request, c_obj.uuid)
136     else:
137         # If users first visit, create empty form.
138         form = ContainerForm()
139         
140     # Load form template
141     c = Context({'form': form,
142                  'action_url': '/samples/container/add/'})
143     t = get_template('generic_form.html')
144
145     return render_to_response('app.html', {
146         'app_name': settings.HTSW_ST_APPNAME,
147         'page_name': 'Container Add',
148         'media': form.media,
149         'bcmagic': BarcodeMagicForm(),
150         'select': 'container',
151         'body': t.render(c)
152     })
153     
154 def container_edit(request, container_id):
155     """
156     Allow editing of a container
157     """
158     try:
159         container = models.Container.objects.get(uuid=container_id)
160     except ObjectDoesNotExist, e:
161         msg = "Container (%s) cannot be edited as it does not exist." % (container_id)
162         return render_to_response('app.html', {
163             'app_name': settings.HTSW_ST_APPNAME,
164             'page_name': 'Container Edit',
165             'media': '',
166             'bcmagic': BarcodeMagicForm(),
167             'select': 'container',
168             'body': msg
169         })
170     
171     # If user submitted the container form.
172     if request.method == 'POST':
173         # Fill in ContainerForm with POSTed data.
174         form = ContainerForm(request.POST, instance=container)
175         
176         # If the form is valid, process it.
177         if form.is_valid():
178             # Save Container object from form data
179             c_obj = form.save(commit=True)
180             # Save many2many changes as well
181             #form.save_m2m() #Not needed when form.save(commit=True)
182             
183             return container_summary(request, c_obj.uuid)
184     else:
185         # If users first visit, create form from Container instance.
186         form = ContainerForm(instance=container)
187
188     # Load form template
189     c = Context({'form': form,
190                  'action_url': '%sedit/' % (container.get_absolute_url())})
191     t = get_template('generic_form.html')
192
193     return render_to_response('app.html', {
194         'app_name': settings.HTSW_ST_APPNAME,
195         'page_name': 'Container Edit',
196         'media': form.media,
197         'bcmagic': BarcodeMagicForm(),
198         'select': 'container',
199         'body': t.render(c)
200     })
201
202
203 def container_print(request, container_id):
204     """
205     prints a container label
206     """
207     
208     try:
209         container = models.Container.objects.get(uuid=container_id)
210     except ObjectDoesNotExist:
211         return HttpResponse('Container (%s) does not exist!' % (container_id))
212     
213     params = {}
214     params['line1'] = __center_line('', 10)
215     params['line2'] = __center_line('', 14)
216     params['line3'] = __center_line('%s' % (container.name[16:16+15]), 15)
217     params['container_name'] = __center_line(container.name[0:16], 16)
218     params['barcode'] = 'cntr|%s' % (container.uuid)
219     params['symbol'] = ''
220     
221     c = Context(params)
222     t = get_template('zpl_container_label.txt')
223     print_zpl(t.render(c))
224     
225     return HttpResponse('print command for container %s sent.' % (container.uuid))
226
227
228 ################################################
229 # Freezer
230 ################################################
231
232 def freezer_index(request):
233     """
234     Returns an index of available freezers
235     """
236     freezer_list = Freezer.objects.all()
237     c = Context({'freezer_list': freezer_list})
238     t = get_template('freezer_index.html')
239     
240     return render_to_response('app.html', {
241         'app_name': "HTSW-SampleTracker",
242         'page_name': 'Freezer Index',
243         'media': '',
244         'bcmagic': BarcodeMagicForm(),
245         'select': 'freezer',
246         'body': t.render(c)
247     })
248     
249     
250 def freezer_summary(request, freezer_id):
251     """
252     Returns summary of freezer_id
253     """
254     # Retrieve freezer by UUID
255     try:
256         freezer = models.Freezer.objects.get(uuid=freezer_id)
257     except ObjectDoesNotExist, e:
258         msg = "Freezer (%s) does not exist." % (freezer_id)
259         return render_to_response('app.html', {
260                 'app_name': settings.HTSW_ST_APPNAME,
261                 'page_name': 'Freezer Summary',
262                 'media': '',
263                 'bcmagic': BarcodeMagicForm(),
264                 'select': 'freezer',
265                 'body': msg
266                 })
267     
268     # List of contained containers
269     container_list = freezer.container_set.all()
270     
271     
272     # Render prepared container index into freezer summary
273     cf = Context({'freezer': freezer })
274     tf = get_template('freezer_summary.html')
275     
276     # Render Freezer summary to app html
277     return render_to_response('app.html', {
278         'app_name': settings.HTSW_ST_APPNAME,
279         'page_name': 'Freezer Summary',
280         'media': '',
281         'bcmagic': BarcodeMagicForm(),
282         'select': 'freezer',
283         'body': tf.render(cf)
284     })
285
286
287 def freezer_add(request):
288     """
289     Allows you to add a new freezer.
290     """
291     # If user submitted the freezer form.
292     if request.method == 'POST':
293         # Fill in FreezerForm with POSTed data.
294         form = FreezerForm(request.POST)
295         
296         # If the form is valid, process it.
297         if form.is_valid():
298             # Create new Freezer object from form data
299             f_obj = form.save(commit=False)
300             # Added UUID
301             f_obj.uuid = get_uuid()
302             # Save to DB.
303             f_obj.save()
304             return freezer_summary(request, f_obj.uuid)
305     else:
306         # If users first visit, create empty form.
307         form = FreezerForm()
308
309     # Load form template
310     c = Context({'form': form,
311                  'action_url': '/samples/freezer/add/'})
312     t = get_template('generic_form.html')
313
314     return render_to_response('app.html', {
315         'app_name': settings.HTSW_ST_APPNAME,
316         'page_name': 'Freezer Add',
317         'media': form.media,
318         'bcmagic': BarcodeMagicForm(),
319         'select': 'freezer',
320         'body': t.render(c)
321     })
322
323
324 def freezer_edit(request, freezer_id):
325     """
326     Allow editing of a freezer
327     """
328     try:
329         freezer = models.Freezer.objects.get(uuid=freezer_id)
330     except ObjectDoesNotExist, e:
331         msg = "Freezer (%s) cannot be edited as it does not exist." % (freezer_id)
332         return render_to_response('app.html', {
333                 'app_name': settings.HTSW_ST_APPNAME,
334                 'page_name': 'Freezer Summary',
335                 'media': '',
336                 'bcmagic': BarcodeMagicForm(),
337                 'select': 'freezer',
338                 'body': msg
339                 })
340     
341     # If user submitted the freezer form.
342     if request.method == 'POST':
343         # Fill in FreezerForm with POSTed data.
344         form = FreezerForm(request.POST, instance=freezer)
345         
346         # If the form is valid, process it.
347         if form.is_valid():
348             # Save Freezer object from form data
349             f_obj = form.save(commit=True)
350             
351             return freezer_summary(request, f_obj.uuid)
352     else:
353         # If users first visit, create form from freezer instance.
354         form = FreezerForm(instance=freezer)
355
356     # Load form template
357     c = Context({'form': form,
358                  'action_url': '%sedit/' % (freezer.get_absolute_url())})
359     t = get_template('generic_form.html')
360
361     return render_to_response('app.html', {
362         'app_name': settings.HTSW_ST_APPNAME,
363         'page_name': 'Freezer Edit',
364         'media': form.media,
365         'bcmagic': BarcodeMagicForm(),
366         'select': 'freezer',
367         'body': t.render(c)
368     })
369
370
371 def freezer_print(request, freezer_id):
372     """
373     prints a freezer label
374     """
375     
376     try:
377         freezer = models.Freezer.objects.get(uuid=freezer_id)
378     except ObjectDoesNotExist:
379         return HttpResponse('Freezer (%s) does not exist!' % (freezer_id))
380     
381     params = {}
382     params['line1'] = __center_line('', 10)
383     params['line2'] = __center_line('', 14)
384     params['line3'] = __center_line('Temp: %s C' % (freezer.temperature), 15)
385     params['freezer_name'] = __center_line(freezer.name, 16)
386     params['barcode'] = 'frzr|%s' % (freezer.uuid)
387     params['symbol'] = ''
388     
389     c = Context(params)
390     t = get_template('zpl_freezer_label.txt')
391     print_zpl(t.render(c))
392     
393     return HttpResponse('print command for freezer %s sent.' % (freezer.uuid))
394
395 ################################################
396 # Samples
397 ################################################
398
399 def sample_index(request):
400     """
401     return a list of samples and what we can do with them.
402     """
403     sample_list = models.Sample.objects.all()
404     
405     # Load form template
406     c = Context({'title': 'Sample Index',
407                  'sample_list': sample_list})
408     t = get_template('sample_index.html')
409     #html.append('%s <a href="/samples/sample/%s/print">(print)</a>' \
410     #            % (sample, sample.sampleid))
411         
412     return render_to_response('app.html', {
413         'app_name': settings.HTSW_ST_APPNAME,
414         'page_name': 'Samples Index',
415         'media': '',
416         'bcmagic': BarcodeMagicForm(),
417         'select': 'samples',
418         'body': t.render(c)
419     })
420
421
422 def sample_homeless(request):
423     """
424     Returns an index of homeless samples
425     """
426     sample_list = Sample.objects.filter(container=None)
427     c = Context({'title': 'Homeless Samples',
428                  'sample_list': sample_list})
429     t = get_template('sample_homeless.html')
430     
431     return render_to_response('app.html', {
432         'app_name': "HTSW-SampleTracker",
433         'page_name': 'Homeless Samples',
434         'bcmagic': BarcodeMagicForm(),
435         'select': 'samples',
436         'body': t.render(c)
437     })
438     
439     
440 def sample_add(request):
441     """
442     Allow adding of a new sample
443     """
444     ASSIGNED_CONTAINER = False
445     
446     # If user submitted the sample form.
447     if request.method == 'POST':
448         # Fill in SampleForm with POSTed data.
449         form = SampleForm(request.POST)
450         
451         # If the form is valid, process it.
452         if form.is_valid():
453             # Create new Sample object from form data
454             s_obj = form.save(commit=False)
455             
456             # Add sample ID
457             s_obj.sampleid = get_sampleid()
458             
459             # If the user wants us to assign a container.
460             if form.cleaned_data['assign_container']:
461                 # Choose container
462                 try:
463                     assign_to_container(s_obj)
464                     ASSIGNED_CONTAINER = True
465                 except NoSpaceAvaliable, e:
466                     #return HttpResponse("<b>Error:</b> %s<br /><i>You will need to prepare a new container before continuing.</i>" \
467                     #                    % (e))
468                     ASSIGNED_CONTAINER = False
469                                     
470             # Save to DB.
471             s_obj.save()
472             
473             if not ASSIGNED_CONTAINER:
474                 msg = "NOTE: Sample is homeless"
475                 return sample_summary(request, s_obj.sampleid, msg=msg)
476
477             
478             msg = "Sample created."
479             return sample_summary(request, s_obj.sampleid, msg=msg)
480     else:
481         # If users first visit, create empty form.
482         form = SampleForm()
483
484     # Load form template
485     c = Context({'form': form,
486                  'action_url': '/samples/sample/add/'})
487     t = get_template('generic_form.html')
488
489     return render_to_response('app.html', {
490         'app_name': settings.HTSW_ST_APPNAME,
491         'page_name': 'Sample Add',
492         'media': form.media,
493         'bcmagic': BarcodeMagicForm(),
494         'select': 'samples',
495         'body': t.render(c)
496     })
497     
498
499 def sample_edit(request, sampleid):
500     """
501     Allow editing of a sample
502     """
503     try:
504         sample = models.Sample.objects.get(sampleid=sampleid)
505     except ObjectDoesNotExist, e:
506         msg = "Sample (%s) cannot be edited as it does not exist." % (sampleid)
507         return render_to_response('app.html', {
508                     'app_name': settings.HTSW_ST_APPNAME,
509                     'page_name': 'Sample Edit',
510                     'media': '',
511                     'bcmagic': BarcodeMagicForm(),
512                     'select': 'samples',
513                     'body': msg
514                 })
515     
516     # If user submitted the sample form.
517     if request.method == 'POST':
518         # Fill in SampleForm with POSTed data.
519         form = SampleForm(request.POST, instance=sample)
520         
521         # If the form is valid, process it.
522         if form.is_valid():
523             # Save Sample object from form data
524             s_obj = form.save(commit=True)
525             
526             msg = "Sample Update Saved"
527             return sample_summary(request, s_obj.sampleid, msg)
528     else:
529         # If users first visit, create form from sample instance.
530         form = SampleForm(instance=sample)
531
532     # Load form template
533     c = Context({'form': form,
534                  'action_url': '%sedit/' % (sample.get_absolute_url())})
535     t = get_template('generic_form.html')
536
537     return render_to_response('app.html', {
538         'app_name': settings.HTSW_ST_APPNAME,
539         'page_name': 'Sample Edit',
540         'media': form.media,
541         'bcmagic': BarcodeMagicForm(),
542         'select': 'samples',
543         'body': t.render(c)
544     })
545
546
547 def sample_assign_container(request, sampleid):
548     """
549     Assigns sample to container using the assign to container algorithm
550     """
551     
552     sample = Sample.objects.get(sampleid=sampleid)
553     
554     try:
555         assign_to_container(sample)
556     except NoSpaceAvaliable, e:
557         return render_to_response('app.html', {
558         'app_name': settings.HTSW_ST_APPNAME,
559         'page_name': 'Sample Assign',
560         'media': '',
561         'bcmagic': BarcodeMagicForm(),
562         'select': 'samples',
563         'body': mark_safe("<b>Error:</b> %s<br /><i>You will need to prepare a new container before continuing.</i>" \
564                             % (e))
565         })
566         
567     sample.save()
568     
569     body = 'Sample (<a href="%s">%s</a>) assigned to container (<a href="%s">%s</a>)' \
570            % (sample.get_absolute_url(), str(sample), sample.container.get_absolute_url(), str(sample.container))
571     
572     return render_to_response('app.html', {
573             'app_name': settings.HTSW_ST_APPNAME,
574             'page_name': 'Sample Assign',
575             'media': '',
576             'bcmagic': BarcodeMagicForm(),
577             'select': 'samples',
578             'body': mark_safe(body),
579             })
580
581
582 def sample_summary(request, sampleid, msg=None):
583     """
584     Display a summary of a given sample
585     """
586     try:
587         sample = models.Sample.objects.get(sampleid=sampleid)
588     except ObjectDoesNotExist, e:
589         msg = "Sample (%s) does not exist." % (sampleid)
590         return render_to_response('app.html', {
591                     'app_name': settings.HTSW_ST_APPNAME,
592                     'page_name': 'Sample Summary',
593                     'media': '',
594                     'bcmagic': BarcodeMagicForm(),
595                     'select': 'samples',
596                     'body': msg
597                 })
598     
599     c = Context({'sample': sample,
600                  'msg': msg,})
601     t = get_template('sample_summary.html')
602     
603     return render_to_response('app.html', {
604         'app_name': settings.HTSW_ST_APPNAME,
605         'page_name': 'Sample Summary',
606         'media': '',
607         'bcmagic': BarcodeMagicForm(),
608         'select': 'samples',
609         'body': t.render(c)
610     })
611
612     
613 def sample_print(request, sampleid):
614     """
615     prints a sample!
616     """
617     
618     try:
619         sample = models.Sample.objects.get(sampleid=sampleid)
620     except ObjectDoesNotExist:
621         return HttpResponse('Sample (%s) does not exist!' % (sampleid))
622     
623     params = {}
624     params['fullid'] = str(sample)
625     params['sampleid'] = __center_line("s|%s" % (sample.sampleid), 16)
626     params['line1'] = __center_line(sample.name[0:10], 10)
627     params['line2'] = __center_line(sample.name[10:25], 14)
628     params['line3'] = __center_line('ExpType %s' % (sample.sample_type.name[0:8]), 15)
629     params['slot_num'] = random.randint(1,81)
630     
631     c = Context(params)
632     t = get_template('half_inch_samples.txt')
633     print_zpl(t.render(c))
634     
635     return HttpResponse('print command for sample %s sent.' % (sample.sampleid))
636
637
638 ################################################
639 # Barcode Magic Commands
640 ################################################
641
642 def cmd_move_sample(request):
643     """
644     Moves a sample to a target container
645     """
646     return render_to_response('app.html', {
647         'app_name': settings.HTSW_ST_APPNAME,
648         'page_name': 'CMD: Move Sample',
649         'media': '',
650         'bcmagic': BarcodeMagicForm(),
651         'select': 'samples',
652         'body': ""
653     })