<commit_before>#!/usr/bin/python

# Brutally primitive reference taxonomy browser.
# Basically just a simple shim on the taxomachine 'taxon' method.
# Intended to be run as a CGI command, but it can be tested by running it 
# directly from the shell; just set the environment QUERY_STRING to be
# the name or id of the taxon to browse to.

# Apache (or other server) must be configured to be able to run CGI scripts, 
# and this program must be in the directory where it looks for such scripts.
# The file name there should be simply 'browse' (not browse.py).

# NOT YET IMPLEMENTED: percent-escaping and -unescaping

# If this were to be written using peyotl, it might do something similar to the following:
# from peyotl.api import APIWrapper
# taxo = APIWrapper().taxomachine
# print taxo.taxon(12345)

default_api_base_url = 'https://devapi.opentreeoflife.org/'

import os
import sys
import cgi, cgitb, StringIO

import requests
import simplejson

headers = {
    'content-type' : 'application/json',
    'accept' : 'application/json',
}

# Main entry point.  Returns HTML as a string.

def browse(id=None, name=None, limit=None, api_base=None):
    output = StringIO.StringIO()
    try:
        if id != None: id = int(id)
    except ValueError:
        report_invalid_arg(output, "Argument 'id' should be an integer!")
        return output.getvalue()
    try:
        if limit != None: limit = int(limit)
    except ValueError:
        report_invalid_arg(output, "Argument 'limit' should be an integer!")
        return output.getvalue()
    if api_base == None: api_base = default_api_base_url

    if id != None:
        browse_by_id(id, limit, api_base, output)
    elif name is None:
        # bump them to our default taxon (root of synthetic tree)
        browse_by_name('cellular organisms', limit, api_base, output)
    else:
        browse_by_name(name, limit, api_base, output)

    return output.getvalue()

def report_invalid_arg(output, info):
    start_el(output, 'h1')
    output.write('Open Tree taxonomy: <strong class="error">invalid argument</strong>')
    end_el(output, 'h1')
    output.write('<p class="error">There was a problem with the name or ID provided:</p>\n')
    start_el(output, 'pre', 'error')
    output.write(cgi.escape(simplejson.dumps(info, sort_keys=True, indent=4)))
    end_el(output, 'pre')

def browse_by_name(name, limit, api_base, output):
    result = look_up_name(name, api_base)
    if result is None:
        report_invalid_arg(output, "No taxa found matching name '%s'" % name)
        return None
    matches = result[u'matches']
    if len(matches) == 0:
        output.write('no TNRS matches for %s\n' % cgi.escape(name))
        return None
    elif len(matches) > 1:
        output.write('Matches for %s: \n' % cgi.escape(name))
        for match in matches:
            output.write("  %s\n" % link_to_taxon(match[u'ot:ottId'], match[u'unique_name']))
    else:
        id = matches[0][u'ot:ottId']
        browse_by_id(id, limit, api_base, output)

# Map taxon name to taxonomy id using match_names service

def look_up_name(name, api_base):
    response = requests.post(api_base + 'v2/tnrs/match_names',
                             headers=headers,
                             data=simplejson.dumps({'names':[name]}))
    response.raise_for_status()
    answer = response.json()
    results = answer[u'results']
    if len(results) == 0: return None
    if len(results) > 1:
        output.write('multiple results - should not happen\n')
        return None # shouldn't happen
    return results[0]

def browse_by_id(id, limit, api_base, output):
    info = get_taxon_info(id, api_base)
    #print simplejson.dumps(info, sort_keys=True, indent=4)
    display_taxon_info(info, limit, output)

def get_taxon_info(ottid, api_base):
    d=simplejson.dumps({'ott_id': ottid, 'include_children': True, 'include_lineage': True})
    response = requests.post(api_base + 'v2/taxonomy/taxon',
                             headers=headers,
                             data=d)
    response.raise_for_status()
    return response.json()

def display_taxon_info(info, limit, output):
    included_children_output = StringIO.StringIO()
    suppressed_children_output = StringIO.StringIO()
    if u'ot:ottId' in info:
        id = info[u'ot:ottId']
        start_el(output, 'h1')
        output.write('Open Tree taxonomy: <strong>%s</strong>' % get_display_name(info))
        end_el(output, 'h1')

        start_el(output, 'p')
        synth_tree_url = "/opentree/ottol@%s" % id
        output.write('  <a target="_blank" href="%s">view in latest synthetic tree</a>' % cgi.escape(synth_tree_url))
        end_el(output, 'p')

        start_el(output, 'p', 'legend')
        output.write('See the OTT wiki for <a href="https://github.com/OpenTreeOfLife/reference-taxonomy/wiki/Taxon-flags">an explanation of the taxon flags used</a> below, e.g., <span class="flag">extinct</span>\n')
        end_el(output, 'p')

        output.write('<h3>Taxon details</h3>')
        start_el(output, 'p', 'taxon')
        display_basic_info(info, output)
        output.write(' (OTT id %s)' % id)
        end_el(output, 'p')

        if u'synonyms' in info:
            synonyms = info[u'synonyms']
            name = info[u'ot:ottTaxonName']
            if name in synonyms:
                synonyms.remove(name)
            if len(synonyms) > 0:
                output.write('<h3>Synonym(s)</h3>')
                start_el(output, 'p', 'synonyms')
                output.write("%s\n" % ', '.join(map(link_to_name, synonyms)))
                end_el(output, 'p')<commit_msg>Even better placement of synth-tree link<commit_after>#!/usr/bin/python

# Brutally primitive reference taxonomy browser.
# Basically just a simple shim on the taxomachine 'taxon' method.
# Intended to be run as a CGI command, but it can be tested by running it 
# directly from the shell; just set the environment QUERY_STRING to be
# the name or id of the taxon to browse to.

# Apache (or other server) must be configured to be able to run CGI scripts, 
# and this program must be in the directory where it looks for such scripts.
# The file name there should be simply 'browse' (not browse.py).

# NOT YET IMPLEMENTED: percent-escaping and -unescaping

# If this were to be written using peyotl, it might do something similar to the following:
# from peyotl.api import APIWrapper
# taxo = APIWrapper().taxomachine
# print taxo.taxon(12345)

default_api_base_url = 'https://devapi.opentreeoflife.org/'

import os
import sys
import cgi, cgitb, StringIO

import requests
import simplejson

headers = {
    'content-type' : 'application/json',
    'accept' : 'application/json',
}

# Main entry point.  Returns HTML as a string.

def browse(id=None, name=None, limit=None, api_base=None):
    output = StringIO.StringIO()
    try:
        if id != None: id = int(id)
    except ValueError:
        report_invalid_arg(output, "Argument 'id' should be an integer!")
        return output.getvalue()
    try:
        if limit != None: limit = int(limit)
    except ValueError:
        report_invalid_arg(output, "Argument 'limit' should be an integer!")
        return output.getvalue()
    if api_base == None: api_base = default_api_base_url

    if id != None:
        browse_by_id(id, limit, api_base, output)
    elif name is None:
        # bump them to our default taxon (root of synthetic tree)
        browse_by_name('cellular organisms', limit, api_base, output)
    else:
        browse_by_name(name, limit, api_base, output)

    return output.getvalue()

def report_invalid_arg(output, info):
    start_el(output, 'h1')
    output.write('Open Tree taxonomy: <strong class="error">invalid argument</strong>')
    end_el(output, 'h1')
    output.write('<p class="error">There was a problem with the name or ID provided:</p>\n')
    start_el(output, 'pre', 'error')
    output.write(cgi.escape(simplejson.dumps(info, sort_keys=True, indent=4)))
    end_el(output, 'pre')

def browse_by_name(name, limit, api_base, output):
    result = look_up_name(name, api_base)
    if result is None:
        report_invalid_arg(output, "No taxa found matching name '%s'" % name)
        return None
    matches = result[u'matches']
    if len(matches) == 0:
        output.write('no TNRS matches for %s\n' % cgi.escape(name))
        return None
    elif len(matches) > 1:
        output.write('Matches for %s: \n' % cgi.escape(name))
        for match in matches:
            output.write("  %s\n" % link_to_taxon(match[u'ot:ottId'], match[u'unique_name']))
    else:
        id = matches[0][u'ot:ottId']
        browse_by_id(id, limit, api_base, output)

# Map taxon name to taxonomy id using match_names service

def look_up_name(name, api_base):
    response = requests.post(api_base + 'v2/tnrs/match_names',
                             headers=headers,
                             data=simplejson.dumps({'names':[name]}))
    response.raise_for_status()
    answer = response.json()
    results = answer[u'results']
    if len(results) == 0: return None
    if len(results) > 1:
        output.write('multiple results - should not happen\n')
        return None # shouldn't happen
    return results[0]

def browse_by_id(id, limit, api_base, output):
    info = get_taxon_info(id, api_base)
    #print simplejson.dumps(info, sort_keys=True, indent=4)
    display_taxon_info(info, limit, output)

def get_taxon_info(ottid, api_base):
    d=simplejson.dumps({'ott_id': ottid, 'include_children': True, 'include_lineage': True})
    response = requests.post(api_base + 'v2/taxonomy/taxon',
                             headers=headers,
                             data=d)
    response.raise_for_status()
    return response.json()

def display_taxon_info(info, limit, output):
    included_children_output = StringIO.StringIO()
    suppressed_children_output = StringIO.StringIO()
    if u'ot:ottId' in info:
        id = info[u'ot:ottId']
        start_el(output, 'h1')
        output.write('Open Tree taxonomy: <strong>%s</strong>' % get_display_name(info))
        end_el(output, 'h1')

        start_el(output, 'p', 'legend')
        output.write('See the OTT wiki for <a href="https://github.com/OpenTreeOfLife/reference-taxonomy/wiki/Taxon-flags">an explanation of the taxon flags used</a> below, e.g., <span class="flag">extinct</span>\n')
        end_el(output, 'p')

        output.write('<h3>Taxon details</h3>')
        start_el(output, 'p', 'taxon')
        display_basic_info(info, output)
        output.write(' (OTT id %s)' % id)
        synth_tree_url = "/opentree/ottol@%s" % id
        output.write('<br/><a target="_blank" href="%s">View this taxon in the latest synthetic tree</a>' % cgi.escape(synth_tree_url))

        end_el(output, 'p')

        if u'synonyms' in info:
            synonyms = info[u'synonyms']
            name = info[u'ot:ottTaxonName']
            if name in synonyms:
                synonyms.remove(name)
            if len(synonyms) > 0:
                output.write('<h3>Synonym(s)</h3>')
                start_el(output, 'p', 'synonyms')
                output.write("%s\n" % ', '.join(map(link_to_name, synonyms)))
                end_el(output, 'p')
{'fraction_non_alphanumeric': 0.12168792934249265, 'fraction_numerical': 0.004282273173342849, 'mean_word_length': 3.0367302844796544, 'pattern_counts': {'":': 0, '<': 36, '<?xml version=': 0, '>': 42, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '487713', 'n_tokens_mistral': 3676, 'n_tokens_neox': 3606, 'n_words': 982}