Drop support for simplejson
[htsworkflow.git] / inventory / views.py
1 from __future__ import absolute_import, print_function, unicode_literals
2
3 from django.conf import settings
4 from django.contrib.auth.decorators import login_required
5 from django.core.exceptions import ObjectDoesNotExist
6 from django.http import HttpResponse, HttpResponseRedirect
7 from django.shortcuts import render_to_response
8 from django.template import RequestContext, Template
9 from django.template.loader import get_template
10
11 from samples.changelist import HTSChangeList
12 from .models import Item, LongTermStorage, ItemType
13 from .admin import ItemAdmin, ItemTypeAdmin
14 from .bcmagic import item_search
15 from experiments.models import FlowCell
16 from bcmagic.plugin import register_search_plugin
17 from bcmagic.forms import BarcodeMagicForm
18 from bcmagic.utils import print_zpl_socket
19
20 register_search_plugin('Inventory Item', item_search)
21
22 import json
23
24 INVENTORY_CONTEXT_DEFAULTS = {
25     'app_name': 'Inventory Tracker',
26     'bcmagic': BarcodeMagicForm()
27 }
28
29 def __flowcell_rundate_sort(x, y):
30     """
31     Sort by rundate
32     """
33     if x.run_date > y.run_date:
34         return 1
35     elif x.run_date == y.run_date:
36         return 0
37     else:
38         return -1
39
40 def __expand_longtermstorage_context(context, item):
41     """
42     Expand information for LongTermStorage
43     """
44     flowcell_list = []
45     flowcell_id_list = []
46     library_id_list = []
47
48     for lts in item.longtermstorage_set.all():
49         flowcell_list.append(lts.flowcell)
50         flowcell_id_list.append(lts.flowcell.flowcell_id)
51         library_id_list.extend([ lib.id for lib in lts.libraries.all() ])
52
53     flowcell_list.sort(__flowcell_rundate_sort)
54     context['oldest_rundate'] = flowcell_list[0].run_date
55     context['latest_rundate'] = flowcell_list[-1].run_date
56
57     context['flowcell_id_list'] = flowcell_id_list
58     context['library_id_list_1_to_20'] = library_id_list[0:20]
59     context['library_id_list_21_to_40'] = library_id_list[20:40]
60     context['library_id_list_41_to_60'] = library_id_list[40:60]
61     context['library_id_list_61_to_80'] = library_id_list[60:80]
62
63
64 EXPAND_CONTEXT = {
65     'Hard Drive': __expand_longtermstorage_context
66 }
67
68 #INVENTORY_ITEM_PRINT_DEFAULTS = {
69 #    'Hard Drive': 'inventory/hard_drive_shell.zpl',
70 #    'default': 'inventory/default.zpl',
71 #    'host': settings.BCPRINTER_PRINTER1_HOST
72 #}
73
74 def getPrinterTemplateByType(item_type):
75     """
76     returns template to use given item_type
77     """
78     assert item_type.printertemplate_set.count() < 2
79
80     # Get the template for item_type
81     if item_type.printertemplate_set.count() > 0:
82         printer_template = item_type.printertemplate_set.all()[0]
83         return printer_template
84     # Get default
85     else:
86         try:
87             printer_template = PrinterTemplate.objects.get(default=True)
88         except ObjectDoesNotExist:
89             msg = "No template for item type (%s) and no default template found" % (item_type.name)
90             raise ValueError(msg)
91
92         return printer_template
93
94
95 @login_required
96 def data_items(request):
97     """
98     Returns items in json format
99     """
100     item_list = Item.objects.all()
101     d = { 'results': len(item_list) }
102     rows = []
103
104     for item in item_list:
105         item_d = {}
106         item_d['uuid'] = item.uuid
107         item_d['barcode_id'] = item.barcode_id
108         item_d['model_id'] = item.item_info.model_id
109         item_d['part_number'] = item.item_info.part_number
110         item_d['lot_number'] = item.item_info.lot_number
111         item_d['vendor'] = item.item_info.vendor.name
112         item_d['creation_date'] = item.creation_date.strftime('%Y-%m-%d %H:%M:%S')
113         item_d['modified_date'] = item.modified_date.strftime('%Y-%m-%d %H:%M:%S')
114         item_d['location'] = item.location.name
115
116         # Item status if exists
117         if item.status is None:
118             item_d['status'] = ''
119         else:
120             item_d['status'] = item.status.name
121
122         # Stored flowcells on device
123         if item.longtermstorage_set.count() > 0:
124             item_d['flowcells'] = ','.join([ lts.flowcell.flowcell_id for lts in item.longtermstorage_set.all() ])
125         else:
126             item_d['flowcells'] = ''
127
128         item_d['type'] = item.item_type.name
129         rows.append(item_d)
130
131     d['rows'] = rows
132
133     return HttpResponse(json.dumps(d), content_type="application/javascript")
134
135 @login_required
136 def all_index(request):
137     """
138     Inventory Index View
139     """
140     # build changelist
141     item_changelist = HTSChangeList(request, Item,
142         list_filter=[],
143         search_fields=[],
144         list_per_page=200,
145         model_admin=ItemAdmin(Item, None)
146     )
147
148     context_dict = {
149         'item_changelist': item_changelist,
150         'page_name': 'Inventory Index'
151     }
152     context_dict.update(INVENTORY_CONTEXT_DEFAULTS)
153
154     return render_to_response('inventory/inventory_all_index.html',
155                               context_dict,
156                               context_instance=RequestContext(request))
157
158 @login_required
159 def index(request):
160     """
161     Inventory Index View
162     """
163     # build changelist
164     item_changelist = HTSChangeList(request, ItemType,
165         list_filter=[],
166         search_fields=['name', 'description'],
167         list_per_page=50,
168         model_admin=ItemTypeAdmin(ItemType, None)
169     )
170
171     context_dict = {
172         'item_changelist': item_changelist,
173         'page_name': 'Inventory Index'
174     }
175     context_dict.update(INVENTORY_CONTEXT_DEFAULTS)
176     return render_to_response('inventory/inventory_index.html',
177                               context_dict,
178                               context_instance=RequestContext(request))
179
180 @login_required
181 def itemtype_index(request, name):
182     """
183     Inventory Index View
184     """
185
186     name = name.replace('%20', ' ')
187
188     itemtype = ItemType.objects.get(name=name)
189
190     # build changelist
191     item_changelist = HTSChangeList(request, Item,
192         list_filter=[],
193         search_fields=[],
194         list_per_page=200,
195         model_admin=ItemAdmin(Item, None)
196     )
197
198     context_dict = {
199         'item_changelist': item_changelist,
200         'page_name': 'Inventory Index'
201     }
202     context_dict.update(INVENTORY_CONTEXT_DEFAULTS)
203
204     return render_to_response('inventory/inventory_itemtype_index.html',
205                               context_dict,
206                               context_instance=RequestContext(request))
207
208
209 @login_required
210 def item_summary_by_barcode(request, barcode_id, msg=''):
211     """
212     Display a summary for an item by barcode
213     """
214     try:
215         item = Item.objects.get(barcode_id=barcode_id)
216     except ObjectDoesNotExist as e:
217         item = None
218
219     return item_summary_by_uuid(request, None, msg, item)
220
221
222 @login_required
223 def item_summary_by_uuid(request, uuid, msg='', item=None):
224     """
225     Display a summary for an item
226     """
227     # Use item instead of looking it up if it is passed.
228     if item is None:
229         try:
230             item = Item.objects.get(uuid=uuid)
231         except ObjectDoesNotExist as e:
232             item = None
233
234     context_dict = {
235         'page_name': 'Item Summary',
236         'item': item,
237         'uuid': uuid,
238         'msg': msg
239     }
240     context_dict.update(INVENTORY_CONTEXT_DEFAULTS)
241
242     return render_to_response('inventory/inventory_summary.html',
243                               context_dict,
244                               context_instance=RequestContext(request))
245
246
247
248
249
250
251 def __expand_context(context, item):
252     """
253     If EXPAND_CONTEXT dictionary has item.item_type.name function registered, use it to expand context
254     """
255     if item.item_type.name in EXPAND_CONTEXT:
256         expand_func = EXPAND_CONTEXT[item.item_type.name]
257         expand_func(context, item)
258
259 def _item_print(item, request):
260     """
261     Prints an item given a type of item label to print
262     """
263     #FIXME: Hard coding this for now... need to abstract later.
264     context = {'item': item}
265     __expand_context(context, item)
266
267     # Print using barcode_id
268     if not item.force_use_uuid and (item.barcode_id is None or len(item.barcode_id.strip())):
269         context['use_uuid'] = False
270         msg = 'Printing item with barcode id: %s' % (item.barcode_id)
271     # Print using uuid
272     else:
273         context['use_uuid'] = True
274         msg = 'Printing item with UUID: %s' % (item.uuid)
275
276     printer_template = getPrinterTemplateByType(item.item_type)
277
278     c = RequestContext(request, context)
279     t = Template(printer_template.template)
280     print_zpl_socket(t.render(c), host=printer_template.printer.ip_address)
281
282     return msg
283
284 @login_required
285 def item_print(request, uuid):
286     """
287     Print a label for a given item
288     """
289     try:
290         item = Item.objects.get(uuid=uuid)
291     except ObjectDoesNotExist as e:
292         item = None
293         msg = "Item with UUID %s does not exist" % (uuid)
294
295     if item is not None:
296         msg = _item_print(item, request)
297
298     return item_summary_by_uuid(request, uuid, msg)
299
300
301 def link_flowcell_and_device(request, flowcell, serial):
302     """
303     Updates database records of a flowcell being archived on a device with a particular serial #
304     """
305     assert flowcell is not None
306     assert serial is not None
307
308     LTS_UPDATED = False
309     SD_UPDATED = False
310     LIBRARY_UPDATED = False
311
312     ###########################################
313     # Retrieve Storage Device
314     try:
315         sd = Item.objects.get(barcode_id=serial)
316     except ObjectDoesNotExist as e:
317         msg = "Item with barcode_id of %s not found." % (serial)
318         raise ObjectDoesNotExist(msg)
319
320     ###########################################
321     # Retrieve FlowCell
322     try:
323         fc = FlowCell.objects.get(flowcell_id__startswith=flowcell)
324     except ObjectDoesNotExist as e:
325         msg = "FlowCell with flowcell_id of %s not found." % (flowcell)
326         raise ObjectDoesNotExist(msg)
327
328     ###########################################
329     # Retrieve or create LongTermStorage Object
330     count = fc.longtermstorage_set.count()
331     lts = None
332     if count > 1:
333         msg = "There really should only be one longtermstorage object per flowcell"
334         raise ValueError(msg)
335     elif count == 1:
336         # lts already attached to flowcell
337         lts = fc.longtermstorage_set.all()[0]
338     else:
339         lts = LongTermStorage()
340         # Attach flowcell
341         lts.flowcell = fc
342         # Need a primary keey before linking to storage devices
343         lts.save()
344         LTS_UPDATED = True
345
346
347     ############################################
348     # Link Storage to Flowcell
349
350     # Add a link to this storage device if it is not already linked.
351     if sd not in lts.storage_devices.all():
352         lts.storage_devices.add(sd)
353         SD_UPDATED = True
354
355     ###########################################
356     # Add Library Links to LTS
357
358     for lane in fc.lane_set.all():
359         if lane.library not in lts.libraries.all():
360             lts.libraries.add(lane.library)
361             LIBRARY_UPDATED = True
362
363     # Save Changes
364     lts.save()
365
366     msg = ['Success:']
367     if LTS_UPDATED or SD_UPDATED or LIBRARY_UPDATED:
368         msg.append('  LongTermStorage (LTS) Created: %s' % (LTS_UPDATED))
369         msg.append('   Storage Device Linked to LTS: %s' % (SD_UPDATED))
370         msg.append('       Libraries updated in LTS: %s' % (LIBRARY_UPDATED))
371     else:
372         msg.append('  No Updates Needed.')
373
374     return HttpResponse('\n'.join(msg))