from django.contrib import admin
-from htsworkflow.frontend.bcmagic.models import KeywordMap
+from htsworkflow.frontend.bcmagic.models import KeywordMap, Printer
class KeywordMapAdmin(admin.ModelAdmin):
list_display = ('keyword','regex', 'url_template')
-admin.site.register(KeywordMap, KeywordMapAdmin)
\ No newline at end of file
+class PrinterAdmin(admin.ModelAdmin):
+ list_display = ('name', 'model', 'ip_address', 'label_shape', 'label_width', 'label_height', 'notes')
+
+admin.site.register(KeywordMap, KeywordMapAdmin)
+admin.site.register(Printer, PrinterAdmin)
\ No newline at end of file
-[{"pk": 1, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/samples/freezer/{{ uuid }}/", "keyword": "frzr"}}, {"pk": 2, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/samples/container/{{ uuid }}/", "keyword": "cntr"}}, {"pk": 3, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<sampleid>\\d+)\\|(?P<owner>[A-Za-z0-9_\\- ]+)", "url_template": "/samples/sample/{{ sampleid }}/", "keyword": "s"}}, {"pk": 4, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<search>[\\S\\s]+)", "url_template": "http://www.google.com/search?q={{ search }}", "keyword": "gg"}}, {"pk": 5, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<search>[\\S\\s]+)", "url_template": "http://www.flickr.com/search/?q={{ search }}", "keyword": "flickr"}}, {"pk": 6, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/inventory/{{ uuid }}/", "keyword": "invu"}}, {"pk": 7, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<barcode_id>.+)", "url_template": "/inventory/{{barcode_id}}/", "keyword": "invb"}}]
+[{"pk": 1, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/samples/freezer/{{ uuid }}/", "keyword": "frzr"}}, {"pk": 2, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/samples/container/{{ uuid }}/", "keyword": "cntr"}}, {"pk": 3, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<sampleid>\\d+)\\|(?P<owner>[A-Za-z0-9_\\- ]+)", "url_template": "/samples/sample/{{ sampleid }}/", "keyword": "s"}}, {"pk": 4, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<search>[\\S\\s]+)", "url_template": "http://www.google.com/search?q={{ search }}", "keyword": "gg"}}, {"pk": 5, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<search>[\\S\\s]+)", "url_template": "http://www.flickr.com/search/?q={{ search }}", "keyword": "flickr"}}, {"pk": 6, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<uuid>[A-Fa-f0-9]+)", "url_template": "/inventory/{{ uuid }}/", "keyword": "invu"}}, {"pk": 7, "model": "bcmagic.keywordmap", "fields": {"regex": "(?P<barcode_id>.+)", "url_template": "/inventory/{{barcode_id}}/", "keyword": "invb"}}, {"pk": 1, "model": "bcmagic.printer", "fields": {"name": "ZM400 1.25x1", "label_height": 1.0, "notes": "Everyday use labels", "label_width": 1.25, "label_shape": "Square", "model": "Zebra ZM400", "ip_address": "131.215.54.194"}}, {"pk": 2, "model": "bcmagic.printer", "fields": {"name": "ZM400 3x3", "label_height": 3.0, "notes": "Larger everyday use labels", "label_width": 3.0, "label_shape": "Square", "model": "Zebra ZM400", "ip_address": "131.215.34.199"}}]
from django.db import models
+#FIXME: Should be made more generic and probably pre-populated supported list
+# but for now, since we only have a ZM400, this will do.
+PRINTER_MODELS=[ ('Zebra ZM400', 'Zebra ZM400'),
+ ('Zebra ZM600', 'Zebra ZM600')]
+
+LABEL_SHAPES = [ ('Square', 'Square'), ('Circle', 'Circle') ]
+
class KeywordMap(models.Model):
"""
Mapper object maps keyword|arg1|arg2|...|argN to REST urls
keyword = models.CharField(max_length=64)
regex = models.CharField(max_length=1024)
url_template = models.TextField()
+
+class Printer(models.Model):
+ """
+ Barcode Printer Information
+ """
+ name = models.CharField(max_length=256)
+ model = models.CharField(max_length=64, choices=PRINTER_MODELS)
+ ip_address = models.IPAddressField()
+ label_shape = models.CharField(max_length=32, choices=LABEL_SHAPES)
+ label_width = models.FloatField(help_text='width or diameter in inches')
+ label_height = models.FloatField(help_text='height in inches')
+ notes = models.TextField()
+
+ def __unicode__(self):
+ return u'%s, %s, %s, %s, %sx%s' % (self.name, self.model, self.ip_address, self.label_shape, self.label_width, self.label_width)
\ No newline at end of file
from django.contrib import admin
-from htsworkflow.frontend.inventory.models import Item, ItemInfo, ItemType, Vendor, Location, LongTermStorage, ItemStatus, ReagentFlowcell, ReagentLibrary
+from htsworkflow.frontend.inventory.models import Item, ItemInfo, ItemType, Vendor, Location, LongTermStorage, ItemStatus, ReagentFlowcell, ReagentLibrary, PrinterTemplate
class ItemAdmin(admin.ModelAdmin):
save_as = True
class ReagentLibraryAdmin(admin.ModelAdmin):
pass
+class PrinterTemplateAdmin(admin.ModelAdmin):
+ list_display = ('item_type', 'printer', 'default')
+
admin.site.register(Item, ItemAdmin)
admin.site.register(ItemInfo, ItemInfoAdmin)
admin.site.register(ItemType, ItemTypeAdmin)
admin.site.register(ItemStatus, ItemStatusAdmin)
admin.site.register(ReagentFlowcell, ReagentFlowcellAdmin)
admin.site.register(ReagentLibrary, ReagentLibraryAdmin)
+admin.site.register(PrinterTemplate, PrinterTemplateAdmin)
--- /dev/null
+[{"pk": 1, "model": "inventory.printertemplate", "fields": {"default": false, "item_type": 1, "printer": 2, "template": "^FX=========================\r\n^FX 3\"x3\" Label\r\n^FX=========================\r\n^XA\r\n\r\n\r\n^FX======== Left Side ===========\r\n\r\n^FX------------\r\n^FX ^LH changes the 0,0 point of all subsequent location references\r\n^FX------------\r\n\r\n^LH0,50\r\n\r\n^FX ---Header---\r\n\r\n^FO25,0\r\n^CF0,50\r\n^FB250,2,,C\r\n^FD{{ item.barcode_id }}^FS\r\n\r\n^FX ---Column 1: Flowcells---\r\n\r\n^FX-----------------\r\n^FX FB command for automatic text formatting:\r\n^FX ^FB[dot width of area], [max # of lines], [change line spacing], [justification: L, C, R, J], [hanging indent]\r\n^FX-----------------\r\n\r\n^CF0,30,30\r\n^FO75,125\r\n^FB275,19,,L\r\n^FD{% for flowcell in flowcell_id_list %}{{ flowcell }}{% if not forloop.last %}\\&{% endif %}{% endfor %}^FS\r\n^FX ---Date---\r\n\r\n^FO0,725\r\n^CF0,35\r\n^FB300,2,,C\r\n^FD{{ oldest_rundate|date:\"YMd\" }} - {{ latest_rundate|date:\"YMd\" }}^FS\r\n\r\n^FX ---Barcode---\r\n\r\n^FO135,795\r\n^BXN,3,200^FDinvb|{{ item.barcode_id }}^FS\r\n\r\n^FX======== Right Side ===========\r\n\r\n^LH300,60\r\n\r\n^FX ---Header---\r\n\r\n^FO0,0\r\n^CF0,50\r\n^FB600,2,,C\r\n^FD{{ barcode_id }}^FS\r\n\r\n^FX ---Dividing line---\r\n\r\n^FX---------------\r\n^FX GB command:\r\n^FX ^GB[box width], [box height], [border thickness], [color: B, W], [corner rounding: 0-8]^FS\r\n^FX---------------\r\n\r\n^FO0,100\r\n^GB0,600,10^FS\r\n\r\n^FX ---Column 2: Libraries 1-20---\r\n\r\n^CF0,30,30\r\n^FO75,100\r\n^FB100,20,,L\r\n^FD{% for lib_id in library_id_list_1_to_20 %}{{ lib_id }}{% if not forloop.last %}\\&{% endif %}{% endfor %}^FS\r\n\r\n^FX ---Column 3: Libraries 21-40---\r\n\r\n^CF0,30,30\r\n^FO200,100\r\n^FB100,20,,L\r\n^FD{% for lib_id in library_id_list_21_to_40 %}{{ lib_id }}{% if not forloop.last %}\\&{% endif %}{% endfor %}^FS\r\n\r\n^FX ---Column 4: Libraries 41-60---\r\n\r\n^CF0,30,30\r\n^FO325,100\r\n^FB100,20,,L\r\n^FD{% for lib_id in library_id_list_41_to_60 %}{{ lib_id }}{% if not forloop.last %}\\&{% endif %}{% endfor %}^FS\r\n\r\n^FX ---Column 5: Libraries 61-80---\r\n\r\n^CF0,30,30\r\n^FO450,100\r\n^FB100,20,,L\r\n^FD{% for lib_id in library_id_list_61_to_80 %}{{ lib_id }}{% if not forloop.last %}\\&{% endif %}{% endfor %}^FS\r\n\r\n^FX ---Date---\r\n\r\n^FO0,715\r\n^CF0,35\r\n^FB600,2,,C\r\n^FDRun Dates: {{ oldest_rundate|date:\"YMd\" }}-{{ latest_rundate|date:\"YMd\" }}^FS\r\n\r\n^FX ---Barcode---\r\n\r\n^FO255,785\r\n^BXN,3,200^FDinvb|{{ item.barcode_id }}^FS\r\n\r\n^LH0,0\r\n^FX ---End---\r\n^XZ\r\n"}}, {"pk": 2, "model": "inventory.printertemplate", "fields": {"default": true, "item_type": 2, "printer": 1, "template": "^FX=========================\r\n^FX Harddrive Location Tracking Label\r\n^FX 300x375 dots\r\n^FX=========================\r\n\r\n^XA\r\n^LH 0,25\r\n\r\n^FO0,0\r\n^CF0,35\r\n^FB375,1,,C\r\n^FD{{ item.item_type.name }}:^FS\r\n\r\n^FX -------Text contains HD serial #-------------\r\n^FO15,75\r\n^CF0,42\r\n^FB325,3,,C\r\n^FD{% if use_uuid %}{{ item.uuid }}{% else %}{{ item.barcode_id }}{% endif %}^FS\r\n\r\n^FX -------Barcode contains HD serial #-----------\r\n^FO150,200\r\n^BXN,3,200\r\n^FD{% if use_uuid %}invu|{{ item.uuid }}{% else %}invb|{{ item.barcode_id }}{% endif %}^FS\r\n\r\n^XZ\r\n"}}]
from htsworkflow.frontend.samples.models import Library
from htsworkflow.frontend.experiments.models import FlowCell
+from htsworkflow.frontend.bcmagic.models import Printer
try:
if instance.uuid is None or len(instance.uuid) != 32:
instance.uuid = uuid.uuid1().hex
+def _switch_default(sender, instance, **kwargs):
+ """
+ When new instance has default == True, uncheck all other defaults
+ """
+ if instance.default:
+ other_defaults = PrinterTemplate.objects.filter(default=True)
+
+ for other in other_defaults:
+ other.default = False
+ other.save()
+
class Vendor(models.Model):
name = models.CharField(max_length=256)
name = models.CharField(max_length=256, unique=True)
location_description = models.TextField()
- uuid = models.CharField(max_length=32, blank=True, help_text="Leave blank for automatic UUID generation")
+ uuid = models.CharField(max_length=32, blank=True, help_text="Leave blank for automatic UUID generation", editable=False)
notes = models.TextField(blank=True, null=True)
pre_save.connect(_assign_uuid, sender=Location)
+
class ItemInfo(models.Model):
model_id = models.CharField(max_length=256, blank=True, null=True)
part_number = models.CharField(max_length=256, blank=True, null=True)
name += u"lot:%s " % (self.lot_number)
return u"%s: %s" % (name, self.purchase_date)
+
+ class Meta:
+ verbose_name_plural = "Item Info"
class ItemType(models.Model):
def __unicode__(self):
return self.name
+
+ class Meta:
+ verbose_name_plural = "Item Status"
+
class Item(models.Model):
#Automatically assigned uuid; used for barcode if one is not provided in
# barcode_id
- uuid = models.CharField(max_length=32, blank=True, help_text="Leave blank for automatic UUID generation", unique=True)
+ uuid = models.CharField(max_length=32, blank=True, help_text="Leave blank for automatic UUID generation", unique=True, editable=False)
# field for existing barcodes; used instead of uuid if provided
barcode_id = models.CharField(max_length=256, blank=True, null=True)
pre_save.connect(_assign_uuid, sender=Item)
+class PrinterTemplate(models.Model):
+ """
+ Maps templates to printer to use
+ """
+ item_type = models.ForeignKey(ItemType)
+ printer = models.ForeignKey(Printer)
+
+ default = models.BooleanField()
+
+ template = models.TextField()
+
+ def __unicode__(self):
+ if self.default:
+ return u'%s %s' % (self.item_type.name, self.printer.name)
+ else:
+ return u'%s %s (default)' % (self.item_type.name, self.printer.name)
+
+pre_save.connect(_switch_default, sender=PrinterTemplate)
+
+
class LongTermStorage(models.Model):
flowcell = models.ForeignKey(FlowCell)
def __unicode__(self):
return u"%s: %s" % (str(self.flowcell), ', '.join([ str(s) for s in self.storage_devices.iterator() ]))
+ class Meta:
+ verbose_name_plural = "Long Term Storage"
+
class ReagentBase(models.Model):
-from htsworkflow.frontend.inventory.models import Item, LongTermStorage
+from htsworkflow.frontend.inventory.models import Item, LongTermStorage, ItemType
from htsworkflow.frontend.inventory.bcmagic import item_search
from htsworkflow.frontend.bcmagic.plugin import register_search_plugin
from htsworkflow.frontend.experiments.models import FlowCell
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.template import RequestContext, Template
from django.template.loader import get_template
from django.contrib.auth.decorators import login_required
'bcmagic': BarcodeMagicForm()
}
-INVENTORY_ITEM_PRINT_DEFAULTS = {
- 'Hard Drive': 'inventory/hard_drive_shell.zpl',
- 'default': 'inventory/default.zpl',
- 'host': settings.BCPRINTER_PRINTER1_HOST
+def __flowcell_rundate_sort(x, y):
+ """
+ Sort by rundate
+ """
+ if x.run_date > y.run_date:
+ return 1
+ elif x.run_date == y.run_date:
+ return 0
+ else:
+ return -1
+
+def __expand_longtermstorage_context(context, item):
+ """
+ Expand information for LongTermStorage
+ """
+ flowcell_list = []
+ flowcell_id_list = []
+ library_id_list = []
+
+ for lts in item.longtermstorage_set.all():
+ flowcell_list.append(lts.flowcell)
+ flowcell_id_list.append(lts.flowcell.flowcell_id)
+ library_id_list.extend([ lib.library_id for lib in lts.libraries.all() ])
+
+ flowcell_list.sort(__flowcell_rundate_sort)
+ context['oldest_rundate'] = flowcell_list[0].run_date
+ context['latest_rundate'] = flowcell_list[-1].run_date
+
+ context['flowcell_id_list'] = flowcell_id_list
+ context['library_id_list_1_to_20'] = library_id_list[0:20]
+ context['library_id_list_21_to_40'] = library_id_list[20:40]
+ context['library_id_list_41_to_60'] = library_id_list[40:60]
+ context['library_id_list_61_to_80'] = library_id_list[60:80]
+
+
+EXPAND_CONTEXT = {
+ 'Hard Drive': __expand_longtermstorage_context
}
-def getTemplateByType(item_type):
+#INVENTORY_ITEM_PRINT_DEFAULTS = {
+# 'Hard Drive': 'inventory/hard_drive_shell.zpl',
+# 'default': 'inventory/default.zpl',
+# 'host': settings.BCPRINTER_PRINTER1_HOST
+#}
+
+def getPrinterTemplateByType(item_type):
"""
returns template to use given item_type
"""
- if item_type in INVENTORY_ITEM_PRINT_DEFAULTS:
- return INVENTORY_ITEM_PRINT_DEFAULTS[item_type]
+ assert item_type.printertemplate_set.count() < 2
+
+ # Get the template for item_type
+ if item_type.printertemplate_set.count() > 0:
+ printer_template = item_type.printertemplate_set.all()[0]
+ return printer_template
+ # Get default
else:
- return INVENTORY_ITEM_PRINT_DEFAULTS['default']
+ try:
+ printer_template = PrinterTemplate.objects.get(default=True)
+ except ObjectDoesNotExist:
+ msg = "No template for item type (%s) and no default template found" % (item_type.name)
+ raise ValueError, msg
+
+ return printer_template
+
@login_required
def data_items(request):
context_instance=RequestContext(request))
+
+
+
+
+def __expand_context(context, item):
+ """
+ If EXPAND_CONTEXT dictionary has item.item_type.name function registered, use it to expand context
+ """
+ if item.item_type.name in EXPAND_CONTEXT:
+ expand_func = EXPAND_CONTEXT[item.item_type.name]
+ expand_func(context, item)
+
def _item_print(item, request):
"""
Prints an item given a type of item label to print
"""
#FIXME: Hard coding this for now... need to abstract later.
context = {'item': item}
+ __expand_context(context, item)
# Print using barcode_id
if not item.force_use_uuid and (item.barcode_id is None or len(item.barcode_id.strip())):
context['use_uuid'] = True
msg = 'Printing item with UUID: %s' % (item.uuid)
+ printer_template = getPrinterTemplateByType(item.item_type)
+
c = RequestContext(request, context)
- t = get_template(getTemplateByType(item.item_type.name))
- print_zpl_socket(t.render(c))
+ t = Template(printer_template.template)
+ print_zpl_socket(t.render(c), host=printer_template.printer.ip_address)
return msg
^FX ^LH changes the 0,0 point of all subsequent location references
^FX------------
-^LH0,0
+^LH0,50
^FX ---Header---
^FO25,0
^CF0,50
^FB250,2,,C
-^FD{{ barcode_id }}^FS
+^FD{{ item.barcode_id }}^FS
^FX ---Column 1: Flowcells---
^FO0,725
^CF0,35
^FB300,2,,C
-^FDCreated {{ creation_date }}^FS
+^FD{{ oldest_rundate|date:"YMd" }} - {{ latest_rundate|date:"YMd" }}^FS
^FX ---Barcode---
-^FO135,775
-^BXN,3,200^FDinvb|{{ barcode_id }}^FS
+^FO135,795
+^BXN,3,200^FDinvb|{{ item.barcode_id }}^FS
^FX======== Right Side ===========
^CF0,30,30
^FO75,100
^FB100,20,,L
-^FD{% for lib_id in library_id_list_1_to_20 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfore %}^FS
+^FD{% for lib_id in library_id_list_1_to_20 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfor %}^FS
^FX ---Column 3: Libraries 21-40---
^CF0,30,30
^FO200,100
^FB100,20,,L
-^FD{% for lib_id in library_id_list_21_to_40 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfore %}^FS
+^FD{% for lib_id in library_id_list_21_to_40 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfor %}^FS
^FX ---Column 4: Libraries 41-60---
^CF0,30,30
^FO325,100
^FB100,20,,L
-^FD{% for lib_id in library_id_list_41_to_60 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfore %}^FS
+^FD{% for lib_id in library_id_list_41_to_60 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfor %}^FS
^FX ---Column 5: Libraries 61-80---
^CF0,30,30
^FO450,100
^FB100,20,,L
-^FD{% for lib_id in library_id_list_61_to_80 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfore %}^FS
+^FD{% for lib_id in library_id_list_61_to_80 %}{{ lib_id }}{% if not forloop.last %}\&{% endif %}{% endfor %}^FS
^FX ---Date---
-^FO0,725
+^FO0,715
^CF0,35
^FB600,2,,C
-^FDLast Modified {{ modified_date }}^FS
+^FDRun Dates: {{ oldest_rundate|date:"YMd" }}-{{ latest_rundate|date:"YMd" }}^FS
^FX ---Barcode---
-^FO255,775
-^BXN,3,200^FDinvb|{{ barcode_id }}^FS
+^FO255,785
+^BXN,3,200^FDinvb|{{ item.barcode_id }}^FS
^LH0,0
^FX ---End---