Pulling in barcode magic from sample tracker and starting to update it to use ExtJS
authorBrandon King <kingb@caltech.edu>
Sat, 27 Jun 2009 01:59:13 +0000 (01:59 +0000)
committerBrandon King <kingb@caltech.edu>
Sat, 27 Jun 2009 01:59:13 +0000 (01:59 +0000)
16 files changed:
htsworkflow/frontend/bcmagic/__init__.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/admin.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/forms.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/models.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/plugin.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/urls.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/utils.py [new file with mode: 0644]
htsworkflow/frontend/bcmagic/views.py [new file with mode: 0644]
htsworkflow/frontend/samples/views.py
htsworkflow/frontend/static/js/bcmagic-ext.js [new file with mode: 0644]
htsworkflow/frontend/static/js/htsw.js
htsworkflow/frontend/static/js/magicbc.js
htsworkflow/frontend/templates/app_base.html
htsworkflow/frontend/templates/bcmagic/magic.html [new file with mode: 0644]
htsworkflow/frontend/templates/magic.html [deleted file]
htsworkflow/frontend/urls.py

diff --git a/htsworkflow/frontend/bcmagic/__init__.py b/htsworkflow/frontend/bcmagic/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/htsworkflow/frontend/bcmagic/admin.py b/htsworkflow/frontend/bcmagic/admin.py
new file mode 100644 (file)
index 0000000..2dce2ec
--- /dev/null
@@ -0,0 +1,7 @@
+from django.contrib import admin
+from htsworkflow.frontend.bcmagic.models import KeywordMap
+
+class KeywordMapAdmin(admin.ModelAdmin):
+    list_display = ('keyword','regex', 'url_template')
+
+admin.site.register(KeywordMap, KeywordMapAdmin)
\ No newline at end of file
diff --git a/htsworkflow/frontend/bcmagic/forms.py b/htsworkflow/frontend/bcmagic/forms.py
new file mode 100644 (file)
index 0000000..000acd9
--- /dev/null
@@ -0,0 +1,9 @@
+from django import forms
+
+class BarcodeMagicForm(forms.Form):
+    magic = forms.CharField(label="Barcode Magic", required=False)
+    bcm_mode = forms.CharField(widget=forms.HiddenInput, initial="default")
+    
+    class Media:
+        js = ('js/jquery.timers-1.0.0.js',
+              'js/bcmagic-ext.js')
diff --git a/htsworkflow/frontend/bcmagic/models.py b/htsworkflow/frontend/bcmagic/models.py
new file mode 100644 (file)
index 0000000..12d8736
--- /dev/null
@@ -0,0 +1,9 @@
+from django.db import models
+
+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()
diff --git a/htsworkflow/frontend/bcmagic/plugin.py b/htsworkflow/frontend/bcmagic/plugin.py
new file mode 100644 (file)
index 0000000..6653fd9
--- /dev/null
@@ -0,0 +1,18 @@
+#from htsworkflow.frontend.samples import bcm_cmds
+
+#BCM_PLUGINS = {'cmd_move_sample': bcm_cmds.cmd_move_sample}
+
+
+def bcm_plugin_processor(keyword, text, bcm_mode):
+    """
+    Fixme should be made generic plugable, but am hard coding values for proof
+    of concept.
+    """
+    d = {}
+    
+    if bcm_mode not in BCM_PLUGINS:
+        d['mode'] = 'Error'
+        d['status'] = 'bcm_mode plugin called "%s" was not found' % (bcm_mode)
+        return d
+    
+    return BCM_PLUGINS[bcm_mode](keyword, text, bcm_mode)
\ No newline at end of file
diff --git a/htsworkflow/frontend/bcmagic/urls.py b/htsworkflow/frontend/bcmagic/urls.py
new file mode 100644 (file)
index 0000000..62497d2
--- /dev/null
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+    (r'^json_test/$', 'htsworkflow.frontend.bcmagic.views.json_test'),
+    (r'^magic/$', 'htsworkflow.frontend.bcmagic.views.magic'),
+    (r'^$', 'htsworkflow.frontend.bcmagic.views.index'),
+)
\ No newline at end of file
diff --git a/htsworkflow/frontend/bcmagic/utils.py b/htsworkflow/frontend/bcmagic/utils.py
new file mode 100644 (file)
index 0000000..5ee81a9
--- /dev/null
@@ -0,0 +1,22 @@
+
+
+def report_error(message):
+    """
+    Return a dictionary with a command to display 'message'
+    """
+    return {'mode': 'Error', 'status': message}
+    
+
+def redirect_to_url(url):
+    """
+    Return a bcm dictionary with a command to redirect to 'url'
+    """
+    return {'mode': 'redirect', 'url': url}
+    
+
+def autofill(field, value):
+    """
+    Return a bcm dictionary with a command to automatically fill the
+    corresponding "field" with "value"
+    """
+    return {'mode': 'autofill', 'field': field, 'value': value}
\ No newline at end of file
diff --git a/htsworkflow/frontend/bcmagic/views.py b/htsworkflow/frontend/bcmagic/views.py
new file mode 100644 (file)
index 0000000..492d7e5
--- /dev/null
@@ -0,0 +1,146 @@
+from django.http import HttpResponse
+from django.template import RequestContext, Template, Context
+from django.shortcuts import render_to_response
+from django.core.exceptions import ObjectDoesNotExist
+
+from htsworkflow.frontend.bcmagic import models
+from htsworkflow.frontend.bcmagic.utils import report_error, redirect_to_url
+from htsworkflow.frontend.bcmagic.plugin import bcm_plugin_processor
+
+import json
+import re
+
+from htsworkflow.frontend.bcmagic import forms
+
+def index(request):
+    """
+    Display a barcode magic input box
+    """
+    form = forms.BarcodeMagicForm()
+    
+    return render_to_response('bcmagic/magic.html', {'bcmagic': form},
+                              context_instance=RequestContext(request))
+
+
+def __magic_process(text):
+    """
+    Based on scanned text, check to see if there is map object to use
+    for a useful redirect.
+    """
+    # Split text on |
+    split_text = text.split('|')
+    
+    # There should always be at least one | in a valid scan.
+    if len(split_text) <= 1:
+        return report_error('Invalid text: %s' % (text))
+    
+    # Keyword is the first element in the list
+    keyword = split_text[0]
+    
+    # Attempt to find a KeywordMap based on keyword
+    try:
+        keymap = models.KeywordMap.objects.get(keyword=keyword)
+    except ObjectDoesNotExist, e:
+        return report_error('Keyword (%s) is not defined' % (keyword))
+    
+    # Remove keyword and only scan the content
+    content = '|'.join(split_text[1:])
+    
+    #FIXME: would be faster to cache compiled regex
+    search = re.compile(keymap.regex)
+    
+    mo = search.search(content)
+    
+    # if the search was invalid
+    if not mo:
+        return report_error('(%s) failed to match (%s)' % (keymap.regex, content))
+    
+    t = Template(keymap.url_template)
+    c = Context(mo.groupdict())
+    
+    return redirect_to_url(str(t.render(c)))
+    
+    
+    
+    
+
+def magic(request):
+    """
+    Let the magic begin
+    """
+    d = {}
+    
+    #Retrieve posted text from processing
+    if 'text' in request.POST:
+        text = request.POST['text']
+    else:
+        text = None
+        
+    #Retrieve bmc_mode for processing
+    if 'bcm_mode' in request.POST:
+        bcm_mode = request.POST['bcm_mode']
+    else:
+        bcm_mode = None
+        
+    ################################
+    # Handle some errors
+    ################################
+    
+    # Did not receive text error
+    if text is None or text.strip() == '':
+        d['mode'] = 'Error'
+        d['status'] = 'Did not recieve text'
+        j = json.JSONEncoder()
+        return HttpResponse(j.encode(d), 'text/plain')
+    
+    # Did not receive bcm_mode error
+    if bcm_mode is None or bcm_mode.strip() == '':
+        d['mode'] = 'Error'
+        d['status'] = 'Missing bcm_mode information'
+    
+    
+    ################################
+    # Figure out which mode to use
+    ################################
+    keyword = text.split('|')[0]
+    
+    # Handle URL mode by default
+    if keyword == 'url':
+        d['mode'] = 'redirect'
+        d['url'] = text.split('|')[1]
+        
+    # Pass off processing to plugins
+    elif bcm_mode != 'default':
+        d = bcm_plugin_processor(keyword, text, bcm_mode)
+    
+    # Try keyword mapper
+    else:
+        d = __magic_process(text)
+    j = json.JSONEncoder()
+    return HttpResponse(j.encode(d), 'text/plain')
+
+
+
+def json_test(request):
+    d = {}
+    
+    if 'text' in request.POST:
+        text = request.POST['text']
+    else:
+        text = None
+    
+    #return HttpResponse(json.write(request.POST.items()), 'text/plain')
+    if text is None or text.strip() == '':
+        d['mode'] = 'Error'
+        d['status'] = 'Did not recieve text'
+        return HttpResponse(json.write(d), 'text/plain')
+    
+    if text.split('|')[0] == 'url':
+        d['mode'] = 'redirect'
+        d['url'] = text.split('|')[1]
+    else:
+        d['msg'] = 'Recieved text: %s' % (text)
+        d['mode'] = 'clear'
+    
+    j = json.JSONEncoder()
+    return HttpResponse(j.encode(d), 'text/plain')
index 52907edc0e8a18b868d27ef40d4ca86564ea7ed7..669a1265f4d9abdcc40651af9e8f3e772571171c 100644 (file)
@@ -3,6 +3,7 @@ from htsworkflow.frontend.experiments.models import FlowCell
 from htsworkflow.frontend.samples.changelist import ChangeList
 from htsworkflow.frontend.samples.models import Library
 from htsworkflow.frontend.samples.results import get_flowcell_result_dict, parse_flowcell_id
+from htsworkflow.frontend.bcmagic.forms import BarcodeMagicForm
 from htsworkflow.pipelines.runfolder import load_pipeline_run_xml
 from htsworkflow.pipelines import runfolder
 from htsworkflow.frontend import settings
@@ -22,7 +23,8 @@ import os
 
 LANE_LIST = [1,2,3,4,5,6,7,8]
 SAMPLES_CONTEXT_DEFAULTS = {
-    'app_name': 'Flowcell/Library Tracker'
+    'app_name': 'Flowcell/Library Tracker',
+    'bcmagic': BarcodeMagicForm()
 }
 
 def create_library_context(cl):
diff --git a/htsworkflow/frontend/static/js/bcmagic-ext.js b/htsworkflow/frontend/static/js/bcmagic-ext.js
new file mode 100644 (file)
index 0000000..a3d9c6c
--- /dev/null
@@ -0,0 +1,169 @@
+//-----------------------------------------------
+// Barcode Magic JavaScript
+// Authors: Brandon W. King
+// Feb. 2009
+//-----------------------------------------------
+
+//---------------------------------------
+// BCMagic Core Processing AJAX Callback
+//---------------------------------------
+//var bcmagic_process_callback = function(data, textStatus) {
+var bcmagic_process_callback = function(response, opt) {
+    //FIXME: temp bypass hack
+    var textStatus = 'success';
+    if (textStatus != 'success')
+    {
+        bcmagic_message('AJAX Status: '+textStatus);
+        return;
+    }
+    
+    var data = Ext.decode(response.responseText);
+    
+    for (key in data)
+    {
+        if (key == 'mode')
+        {
+            if (data['mode'] == 'clear')
+            {
+                bcmagic_status('','');
+            }
+            else if (data['mode'] == 'redirect')
+            {
+                if ('url' in data)
+                {
+                    bcmagic_redirect(data['url']);
+                }
+                else
+                {
+                    bcmagic_status('Error', 'No redirect URL provided by server');
+                }
+            }
+            else if (data['mode'] == 'autofill')
+            {
+                bcmagic_autofill(data['field'], data['value'])
+            }
+            else {
+                bcmagic_message('Message recieved!');
+                bcmagic_status(data['mode'], data['status']);    
+            }
+            
+        }
+        if (key == 'msg')
+        {
+            bcmagic_message(data['msg']);
+        }
+    }
+}
+
+var bcmagic_callback = function(data, textStatus)
+{
+    if (textStatus != 'success')
+        bcmagic_message('Failed!');
+    else
+        bcmagic_message('Success!');
+}
+
+var bcmagic_process = function(){
+    var magic = Ext.get("bcmagic_input_field");
+    var text = magic.getValue();
+    magic.dom.value = '';
+    magic.focus();
+    
+    //var bcm_mode = $("#id_bcm_mode");
+    //var mode = bcm_mode.attr('value');
+    var mode = 'default';
+    
+    // Show what we have captured
+    bcmagic_message('Sent command to server');
+    //$.post('/bcmagic/magic/', {'text': text, 'bcm_mode': mode}, bcmagic_process_callback, 'json');
+    Ext.Ajax.request({
+        url: '/bcmagic/magic/',
+        success: bcmagic_process_callback,
+        failure: function (r, o) { quick_msg('Some AJAX Fail!'); },
+        params: {'text': text, 'bcm_mode': mode}
+    });
+}
+
+var bcmagic_keyhandler = function(e) {
+    //Process upon enter key as input.
+    if (e.which == 13)
+      bcmagic_process();
+}
+
+//---------------------------------------
+// Utility Functions
+//---------------------------------------
+var bcmagic_message = function(text)
+{
+    // Show message
+    //$("#bcm_msg").html(text);
+    quick_msg(text);
+    
+    // clear message after 3000ms
+    /*
+    setTimeout(function() {
+        $("#bcm_msg").html('');
+        }, 3000);
+    */
+}
+
+var bcmagic_status = function(state, text)
+{
+    //var msg = $('#bcm_status');
+    if (state.length > 0 || text.length > 0)
+        quick_msg(state+': '+text);
+    //else
+    //    msg.html('');
+}
+
+
+var bcmagic_redirect = function(url)
+{
+    bcmagic_message('Redirecting to:' + url);
+    window.location = url;
+}
+
+var bcmagic_autofill = function(field, val)
+{
+    var txtbox = $('#'+field);
+    txtbox.attr('value', val);
+    
+    var input_fields = $('form input').not(':hidden').not('[type=submit]');
+    
+    // Count the number of text fields which have been filled in.
+    var count = 0;
+    input_fields.each( function(){
+                   if(this.value.length > 0){
+                        count = count + 1;
+                   }
+                });
+    
+    // If the number of text fields filled in is equal to the number of input_fields in the form, submit the form!
+    if (count == input_fields.length)
+    {
+        bcmagic_status('Form Full', 'Form is now full and ready to process');
+        form = $('form');
+        form.submit();
+        form.reset();
+    
+    }
+    else
+    {
+        bcmagic_status('Form Fill Count', 'Count(' + count +') - Total(' + input_fields.length + ')');
+    }
+}
+
+//---------------------------------------
+// Main Ready Function
+//---------------------------------------
+/*$(document).ready(function() {
+        
+        // Grab initial focus on magic text input
+        $("#id_magic").focus();
+        
+        // Set some initial text
+        //$("#id_magic").attr('value','Moo cow!');
+        
+        // Trigger when enterkey is pressed
+        $("#id_magic").keypress(bcmagic_keyhandler)
+});*/
\ No newline at end of file
index 83f4ac05bd0ed3011ead4db4fbae3f9bce9e00bd..6f00cb16c10f092460c383ce5f48d1e891e94890 100644 (file)
@@ -220,6 +220,25 @@ $(document).ready(function(){
     
     // Shifts the remaining toolbar options to the right side.
     main_tb.add({ xtype: 'tbfill' });
+    
+    //----------------------------------------
+    // ExtJS Barcode Magic Implementation
+    var bcmagic_ext_keyhandler = function(sObj, e){
+      //e.preventDefault();
+      //Process upon enter key as input.
+      if (e.getKey() == e.ENTER)
+       bcmagic_process();
+    }
+    
+    var bcmagic_input = new Ext.form.TextField({
+      id: 'bcmagic_input_field',
+      emptyText: 'barcode magic'
+    });
+    bcmagic_input.on('specialkey', bcmagic_ext_keyhandler);
+    
+    main_tb.add(bcmagic_input);
+    //--------------------------------------
+    
     var user_info = Ext.fly('login_info');
     var logout_url = user_info.getAttribute('logouturl');
     var login_url = user_info.getAttribute('loginurl');
@@ -255,5 +274,6 @@ $(document).ready(function(){
     add_buttons_from_html(app_tb, 'app_toolbar_east');
     app_tb.doLayout();
     
-    
+    // Focus on barcode magic, because it's awesome and needs attention! ;-)
+    bcmagic_input.focus();
 });
\ No newline at end of file
index 2044193f17e4fe14a8627325e0882da853aeadae..b294abd9493dfcc5485535bf45237d4fcde1d292 100644 (file)
@@ -140,7 +140,7 @@ var bcmagic_autofill = function(field, val)
 //---------------------------------------
 // Main Ready Function
 //---------------------------------------
-$(document).ready(function() {
+/*$(document).ready(function() {
         
         // Grab initial focus on magic text input
         $("#id_magic").focus();
@@ -150,4 +150,4 @@ $(document).ready(function() {
         
         // Trigger when enterkey is pressed
         $("#id_magic").keypress(bcmagic_keyhandler)
-});
\ No newline at end of file
+});*/
\ No newline at end of file
index 5338da2b938da3b90f974664df540d6d52f27a0b..9e03aaaa0022598b426193ee8a61128babca24bd 100644 (file)
     </div>
     
     <!-- Barcode Magic Div -->
+    <!--
     <div id="bcmagic_div" class="x-hidden">
-        {% include "magic.html" %}
+        {% include "bcmagic/magic.html" %}
     </div>
+    -->
     
     {% block east_region %}
     <div id="east_region_config" class="x-hidden">{% if east_region_config_div %}{{ east_region_config_div }}{% endif %}</div>
diff --git a/htsworkflow/frontend/templates/bcmagic/magic.html b/htsworkflow/frontend/templates/bcmagic/magic.html
new file mode 100644 (file)
index 0000000..e1d1061
--- /dev/null
@@ -0,0 +1,3 @@
+<div id="bcm_msg"></div>
+{{ bcmagic }}<br />
+<div id="bcm_status"></div>
diff --git a/htsworkflow/frontend/templates/magic.html b/htsworkflow/frontend/templates/magic.html
deleted file mode 100644 (file)
index e1d1061..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<div id="bcm_msg"></div>
-{{ bcmagic }}<br />
-<div id="bcm_status"></div>
index 4be3ed5b4df0b2fd2af9ae3798b75f4236cad6af..1cd34b4f83d5fcb56ee4795e93c5cc4d29e4e123 100644 (file)
@@ -44,6 +44,7 @@ urlpatterns = patterns('',
       'htsworkflow.frontend.samples.views.bedfile_fc_cnm_eland_lane_ucsc'),
     (r'^results/(?P<fc_id>\w+)/(?P<cnm>C[1-9]-[0-9]+)/bedfile/(?P<lane>[1-8])',
       'htsworkflow.frontend.samples.views.bedfile_fc_cnm_eland_lane'),
+    (r'^bcmagic/', include('htsworkflow.frontend.bcmagic.urls')),
     
     # databrowser
     #(r'^databrowse/(.*)', databrowse.site.root)