# -*- coding: utf8 -*-
'''
R E Q U E S T - C L A S S
**********************************************
*      R E Q U E S T - C L A S S
*      class for species2000 request parsing
**********************************************
$Id$
$Revision: 875 $
$Author: markus $
$Date: 2008-04-24 02:23:32 +0200 (Do, 24. Apr 2008) $

The species2000 protocol uses no XML for querying but CGI parameters.
The response is a fixed schema knwon as the Common Data Model (CDM).
This is built into the pywrapper via its namespaces:
http://www.sp2000.org/CDMSchema1.3
'''


import types, string
from xml.sax                        import parse, parseString
from cgi                            import FieldStorage
from biocase.wrapper.errorclasses   import *
from biocase.wrapper.protocol.base_request   import RequestBaseClass
from biocase.wrapper.sql.operators  import comparisonOperator, logicalOperator, compressOperator
from biocase.wrapper.typeconverter  import DBAttributeClass, DBValueClass
from biocase.tools.various_functions import normalizeWhitespace

import biocase.configuration
cfg = biocase.configuration.Cfg()

import logging
log = logging.getLogger("pywrapper.protocol.sp2k")

# ------------------------------------------------------------------------------------
class RequestClass(RequestBaseClass):
    '''REQUEST class, based on the Species2000 CGI specification and the CDM 1.20'''
    # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    def __init__(self, psfObj):
        # init base class
        RequestBaseClass.__init__(self, psfObj)
        self.rtype = None # the species2000 request type
        # optional list of xpaths pointing to nodes in the CMF that should be removed before the CMF objects is being used.
        # Used in Species2000 to select the right response type
        self.removeCMFSubtreePaths = []

    # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    def parseRequest(self, args):
        '''Parses the request hash "args" and fills this object.'''
        # test for CGI parameters:
        if len(args) < 1:
            args = self.getRequestHashFromCGI()
        log.debug("Initial request args received: %s" %str(args))
        # transform species2000 request into pywrapper request hash
        if str(args.get("requesttype", None)) == '0':
            args = self.getRType0RequestHash(args)
        elif str(args.get("requesttype", None)) == '1':
            args = self.getRType1RequestHash(args)
        elif str(args.get("requesttype", None)) == '2':
            args = self.getRType2RequestHash(args)
        elif str(args.get("requesttype", None)) == '3':
            args = self.getRType3RequestHash(args)
        elif str(args.get("requesttype", None)) == '4':
            args = self.getRType4RequestHash(args)
        elif str(args.get("requesttype", None)) == '5':
            args = self.getRType5RequestHash(args)
        else:
            log.warn('Could not identify any species2000 request type. Use parameter "requesttype" for this. Default fall back to request type 0.')
            args = self.getRType0RequestHash(args)
        log.debug("Received a Species2000 RequestType " +args.get("rtype", "?") )
        # init request object with request hash
        RequestBaseClass.init(self, args)
        # additional species2000 specific inits
        self.rtype = args.get('rtype', None)
        
        # set some logging data
        self.sources.append(self.remoteHost)
        # timestamp
        log.debug("parse request")
        
    
    # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    def getRequestHashFromCGI(self):
        '''Get the request hash from GET/POST.'''
        reqHash={} # resulting hash
        # 1. get all GET/POST parameters into the hash
        querystring = FieldStorage()
        keys = querystring.keys()
        for k in keys:
            reqHash[k.lower()]=querystring.getfirst(k)
        log.debug("http request parameters found: %s" %(string.join(keys, ", ")))
        return reqHash
    
    # _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
    def Species2000Args2requestHash(self, reqArgs):
        args={}
        # search string
        args['searchname'] = reqArgs.get("searchname", None)
        # identifier for taxon. Overriden within most request type specific getRType functions!
        args['taxonid']    = reqArgs.get("taxonid", reqArgs.get("taxon", reqArgs.get("highertaxon", None)))
        # identifier for GSD
        args['gsdid']      = reqArgs.get("gsdid", reqArgs.get("identifier", None))
        for k,v in args.items():
            if v is not None:
                args[k]=str(v)
        # rec limit
        try:
            args['limit']    = int(reqArgs.get("limit", 25))
            log.info("Request limited the search to "+args['limit']+" records.")
        except:
            pass
        # rec Start
        try:
            args['start']    = int(reqArgs.get("skip", None))
            log.info("Request skips the first "+args['start']+" records of the search result.")
        except:
            pass
        return args
    
    # ******************************************************************************
    def getRType0RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/TAXONSEARCH','/XMLRESPONSE/TYPE1RESULT','/XMLRESPONSE/TYPE2RESULT','/XMLRESPONSE/TYPE3RESULT','/XMLRESPONSE/TYPE4RESULT','/XMLRESPONSE/TYPE5RESULT',]
        args = {}
        args['rtype'] = '0'
        args['request_type']    = "static"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        return args
        
    # ******************************************************************************
    def getRType1RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/CDMVERSION','/XMLRESPONSE/TYPE2RESULT','/XMLRESPONSE/TYPE3RESULT','/XMLRESPONSE/TYPE4RESULT','/XMLRESPONSE/TYPE5RESULT',]
        args = self.Species2000Args2requestHash(reqArgs)
        args['rtype'] = '1'
        args['request_type']    = "search"
        args['request_schema']  = "http://www.sp2000.org/cdmschema1.3"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        # test if search string exists
        if args['searchname'] is None:
            raise "No search string provided"
        # defaults
        genus     = None
        species   = None
        infraspec = None
        # generate filter string
        searchstring = args['searchname']
        # normalize whitespace
        searchstring = normalizeWhitespace(searchstring)
        log.info("Scientific name search for '%s'"%searchstring)
        
        # search on scientific name
        # treat simple genus search
        parts = string.split(searchstring, ' ')

        if len(parts) == 1:
            # there is no whitespace in the string, so problably its a genus only.
            genus = searchstring
            #if genus[-1] <> '*': genus += '*'
        elif len(parts) == 2:
            genus   = parts[0]
            species = parts[1]
            #if species[-1] <> '*': species += '*'
        elif len(parts) == 3:
            genus     = parts[0]
            species   = parts[1]
            infraspec = parts[2]
            #if infraspec[-1] <> '*': infraspec += '*'
        else:
            raise "TOO MANY SPACES IN THE SEARCH. DONT KNOW WHAT THIS IS ABOUT."
        log.info("Parsed scientific search: Genus='%s', Species='%s', Infraspecies='%s'"%(unicode(genus),unicode(species),unicode(infraspec)))
        # construct search protocol
        cops = []
        if genus     is not None: cops.append( '<like path="/XMLRESPONSE/TAXONSEARCH[@GENUS]">%s</like>' % genus )
        if species   is not None: cops.append( '<like path="/XMLRESPONSE/TAXONSEARCH[@SPECIES]">%s</like>' % species )
        if infraspec is not None: cops.append( '<like path="/XMLRESPONSE/TAXONSEARCH[@INFRASPECIES]">%s</like>' % infraspec )
        # 
        if len(cops) > 1:
            self.doc = '<filter> <and> %s </and> </filter>' % (string.join(cops, ' ') )
        elif len(cops) == 1:
            self.doc = '<filter> %s </filter>' % (string.join(cops, ' ') )
        else:
            raise "WHAT HAPPENED ??? NO SPICE QUERY."
            
        return args
    
    # ******************************************************************************
    def getRType2RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/CDMVERSION','/XMLRESPONSE/TYPE1RESULT','/XMLRESPONSE/TYPE3RESULT','/XMLRESPONSE/TYPE4RESULT','/XMLRESPONSE/TYPE5RESULT',]
        args = self.Species2000Args2requestHash(reqArgs)
        args['rtype'] = '2'
        args['request_type']    = "search"
        args['request_schema']  = "http://www.sp2000.org/cdmschema1.3"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        # test if search string exists
        # identifier for taxon
        args['taxonid']    = reqArgs.get("taxonid", None)
        if args['taxonid'] is None:
            raise 'No taxon identifier supplied. Please use parameter "taxonid".'
        # generate filter string
        self.doc='<filter><equals path="/XMLRESPONSE/TAXONSEARCH[@TAXONID]">%s</equals></filter>' % args['taxonid']
        return args
        
    # ******************************************************************************
    def getRType3RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/CDMVERSION', '/XMLRESPONSE/TAXONSEARCH','/XMLRESPONSE/TYPE1RESULT','/XMLRESPONSE/TYPE2RESULT','/XMLRESPONSE/TYPE4RESULT','/XMLRESPONSE/TYPE5RESULT',]
        args = {}
        args['rtype'] = '3'
        args['request_type']    = "static"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        return args
            
    # ******************************************************************************
    def getRType4RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/CDMVERSION','/XMLRESPONSE/TYPE1RESULT','/XMLRESPONSE/TYPE2RESULT','/XMLRESPONSE/TYPE3RESULT','/XMLRESPONSE/TYPE5RESULT',]
        args = self.Species2000Args2requestHash(reqArgs)
        args['rtype'] = '4'
        args['request_type']    = "search"
        args['request_schema']  = "http://www.sp2000.org/cdmschema1.3"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        # test if search string exists
        args['taxonid']    = reqArgs.get("taxon", None)
        if args['taxonid'] is None:
            raise 'No taxon identifier supplied. Please use parameter "taxon".'
        # generate filter string
        self.doc='<filter><equals path="/XMLRESPONSE/TAXONSEARCH[@TAXONID]">%s</equals></filter>' % args['taxonid']
        return args

    # ******************************************************************************
    def getRType5RequestHash(self, reqArgs):
        # generate request object
        self.removeCMFSubtreePaths = ['/XMLRESPONSE/CDMVERSION','/XMLRESPONSE/TYPE1RESULT','/XMLRESPONSE/TYPE2RESULT','/XMLRESPONSE/TYPE3RESULT','/XMLRESPONSE/TYPE4RESULT',]
        args = self.Species2000Args2requestHash(reqArgs)
        args['rtype'] = '5'
        args['request_type']    = "search"
        args['request_schema']  = "http://www.sp2000.org/cdmschema1.3"
        args['response_schema'] = "http://www.sp2000.org/cdmschema1.3"
        # test if search string exists
        args['taxonid']    = reqArgs.get("highertaxon", None)
        if args['taxonid'] is None:
            raise 'No taxon identifier supplied. Please use parameter "highertaxon".'
        # generate filter string
        self.doc='<filter><equals path="/XMLRESPONSE/TAXONSEARCH[@TAXONID]">%s</equals></filter>' % args['taxonid']
        return args

    