def animal_search_json(request):
    '''\
    Given a request with a query in the 'q' key of the GET string, returns a 
    JSON list of Animals.
    '''
    
    query = u''
    if 'q' in request.GET:
        query = request.GET['q']
    
    # we only do case-less matching that ignore whitespace, so use a case-less, stripped key
    cache_key = query.lower().strip()
    # some cache backends are picky about characters, so use Base64-encoded
    # UTF-8
    cache_key = cache_key.encode('utf-8')
    cache_key = standard_b64encode(cache_key)
    cache_key = 'animal_search_json_' + cache_key
    cached_json = cache.get(cache_key)
    if cached_json:
        return HttpResponse(cached_json)
    
    words = query.split()
    if words:
        firstword = words[0]
        q = Q(name__icontains=firstword)
        q |= Q(field_number__icontains=firstword)
        try:
            q |= Q(id__exact=int(firstword))
        except ValueError:
            pass
        results = Animal.objects.filter(q).order_by('-id')
    else:
        results = tuple()
    
    # since we wont have access to the handy properties and functions of the
    # Animal objects, we have to call them now and include their output
    # in the JSON
    animals = []
    for result in results:
        
        plain_name = unicode(result)
        
        html_name = html(result, block=True)

        taxon = result.determined_taxon
        if taxon:
            taxon = unicode(taxon.scientific_name())
        
        animals.append({
            'id': result.id,
            'plain_name': plain_name,
            'html_name': html_name,
            'taxon': taxon,
        })
    # TODO return 304 when not changed?
    
    json_result = json.dumps(animals)
    cache.set(cache_key, json_result, 60) # timeout quickly to catch updates
                                          # to the animals
    return HttpResponse(json_result)
 def id_to_html_display(self, id):
     taxon = Taxon.objects.get(id=id)
     return html(taxon)
def taxon_search_json(request):
    '''\
    Given a request with a query in the 'q' key of the GET string, returns a 
    JSON list of Taxons.
    '''
    
    get_query = u''
    if 'q' in request.GET:
        get_query = request.GET['q']
    
    cache_key = get_query.lower().strip()
    # some cache backends are picky about characters, so use Base64-encoded
    # UTF-8
    cache_key = cache_key.encode('utf-8')
    cache_key = standard_b64encode(cache_key)
    cache_key = 'taxon_search_json_%s' % cache_key
    cached_json = cache.get(cache_key)
    if cached_json:
        return HttpResponse(cached_json)
    
    words = get_query.split()
    if words:
        common_query = Q(common_names__icontains=get_query)
        
        genus_query = Q()
        abbr_match = re.search(r'^(?u)\s*(\w+)\.', words[0])
        if abbr_match:
            # the first word is a genus abbr, so remove it from the list of 
            # words
            words = words[1:]
            if len(words) == 0:
                # put in a dummy first word since latin_query assumes there will
                # be one
                words = ['']
            
            genuses = Taxon.objects.filter(rank=0, name__istartswith=abbr_match.group(1)).values_list('id', flat=True)
            if genuses:
                # add all their descendants to the results
                # TODO fetch more than 2 deep)
                genus_query |= Q(supertaxon__id__in=genuses)
                genus_query |= Q(supertaxon__supertaxon__id__in=genuses)
        
        latin_query = Q(name__istartswith=words[0])
        
        db_query = (common_query | latin_query) & genus_query
        results = Taxon.objects.filter(db_query).order_by('-rank', 'name')
    else:
        results = tuple()
    
    # since we wont have access to the handy properties and functions of the
    # Taxon objects, we have to call them now and include their output
    # in the JSON
    taxa = []
    for result in results:
        taxa.append({
            'id': result.id,
            'plain_name': result.scientific_name(),
            'html_name': html(result),
            'common_names': result.common_names,
        })
    # TODO return 304 when not changed?
    
    json_result = json.dumps(taxa)
    # we can do a long timeout since adding or editing a new Taxon will clear
    # the cache
    cache.set(cache_key, json_result, 7 * 24 * 60 * 60)
    return HttpResponse(json_result)