#!/usr/bin/env python
#############################################################
#                                                           #
#   Author: Herve Menager                                   #
#   Organization:'Biological Software and Databases' Group, #
#                Institut Pasteur, Paris.                   #
#   Distributed under GPLv2 Licence. Please refer to the    #
#   COPYING.LIB document.                                   #
#                                                           #
#############################################################
import sys, os
from lxml import etree
abspath = os.path.dirname(__file__)
sys.path.append(abspath)
os.chdir(abspath)

import shutil
import mb_cgi
import warnings
import re
from Mobyle.JobState import url2path
from  Mobyle.Parser import parseService


#from Mobyle.JobFacade import LocalJobFacade
from Mobyle.JobFacade import JobFacade
from Mobyle.Registry import registry
from Mobyle.StatusManager import StatusManager
import Mobyle.Net
import time #@UnresolvedImport
from Mobyle.JobState import JobState
from operator import itemgetter,attrgetter
from Mobyle.AuthenticatedSession import AuthenticatedSession
from Mobyle.AnonymousSession import AnonymousSession 

debug = False
#debug = True

graphml_file_name = ".workflow_template_graphml.xml"
if debug:
    sys.stderr = sys.stdout
    print "Content-Type: text/plain\n"


def process(self):
    graphmlWorkFlowPath = None
    action_name = self.request.getfirst('action')
    self.email = self.request.getfirst('email',None)
    if action_name == 'form_submit' or action_name == 'submit':
        if self.session is None:
            self.jsonMap = [{'errormsg':'you need to login to execute a job on the server'}]
        else:
            pUrl  = None
            wfUrl = None
            if (self.request.has_key('pr_name')) :
                pUrl = registry.getProgramUrl(self.request.getfirst('pr_name',None))
            elif (self.request.has_key('wf_name')) :
                dirName = AuthenticatedSession.DIRNAME if self.session.isAuthenticated() else AnonymousSession.DIRNAME
                wfName = self.request.getfirst('wf_name',None)
                wfUrl= "%s/%s/%s/%s" % ( self.cfg.user_sessions_url(),dirName,self.sessionKey,'BMW') 
                wfUrl= "%s/%s" % (wfUrl, wfName +'_mobyle.xml')
                graphmlWorkFlowPath = os.path.realpath( os.path.join(self.session.getDir(), 'BMW', wfName + '.graphml'))
            else:
                wfUrl=self.request.getfirst('workflowUrl',None)
                
            j = JobFacade.getFromService(programUrl=pUrl,workflowUrl=wfUrl)
            j.email_notify = self.request.getfirst('_email_notify','auto')
            job = j.create(self.request,None,self.session)
            jobDetail= {}
            jobDetail['wfUrl'] = wfUrl
            if job.has_key('id') and not(job.has_key('errormsg')):
                j.addJobToSession(self.session, job)
                descr = self.request.getfirst('annotation_description',None)
                if descr:
                    self.session.setJobDescription(job['id'],descr)
                graphmlJobDirPath = os.path.join(url2path(job['id']), graphml_file_name) 
                if (graphmlWorkFlowPath is not None and os.path.exists(graphmlWorkFlowPath)) :
                    shutil.copyfile(graphmlWorkFlowPath, graphmlJobDirPath)
                #else :
                    # Todo : 
                    # 1. need to run wf_to_graphml.xsl script on the index.xml to graphml file
                    # 2. convert graphml to dot using graphml_to_dot.xsl (as done in genericService.cgi)
                    # 3. Get the coordinates of the application by running dot application
                    #job1 = mb_cgi.get_formatted_job_entries(job)[0]
                jobUrl= job['id']
                job = self.session.getJob(jobUrl)
                jf = JobFacade.getFromJobId( jobUrl )
                job['subjobs']  = jf.getSubJobs() 
                job['jobPID'] = registry.getJobPID(jobUrl)
                job1= get_formatted_job_entries(job,self.session)
                #job1['jobID'] = registry.getJobPID(jobUrl)
                #job1['jobID'] = re.sub("/",".",jobId)
                job1['wfUrl'] = wfUrl
                self.jsonMap = [job1]
            else:
                if job.has_key('pid'):
                    job['jobID'] = job['pid']
                job['jobDate'] = time.strftime( "%x  %X", time.localtime())
                job['status'] = "submit_error"
                self.jsonMap = [job]
    elif action_name == 'set_job_description':
        jobId  = self.request.getfirst('id',None)
        desc   = self.request.getfirst('description',None)
        jobUrl = registry.getJobURL(jobId)
        self.session.setJobDescription(jobUrl,desc)
        self.jsonMap['ok'] = str(True)
    elif action_name == 'rename_job':
        # rename the workflow
        # action=rename_wf&wf_from_name=xxx&wf_to_name=yyy
        user_name = self.request.getfirst('userName')
        job_id = self.request.getfirst('id')
        job_url = registry.getJobURL(job_id)
        job_graphml_file_path = os.path.join(url2path(job_url), graphml_file_name)
        #modify user name in workflow graphml file if it exists
        if os.path.exists(job_graphml_file_path):
            # rename in the graphml file directly as well
            source_file = open(job_graphml_file_path, 'r')
            root_tree = etree.parse(source_file)
            source_file.close()
            graph = root_tree.find('graph')
            if graph.get('userName')!=user_name:
                graph.set('userName', user_name)
                wffile = open(job_graphml_file_path, 'w' )
                wffile.write( etree.tostring(root_tree)) 
                wffile.close()
        self.session.renameJob(job_url,user_name)
        self.jsonMap['ok'] = str(True)
    elif action_name == 'set_job_labels':
        jobId  = self.request.getfirst('id',None)
        jobUrl = registry.getJobURL(jobId)
        labels = self.request.getlist('label')
        if not labels :
            labels = []
        else:
            labels = _unique_list(labels)
        self.session.setJobLabels(jobUrl,labels)
        self.jsonMap['ok'] = str(True)
    elif action_name == 'get_job_description':
        jobId  = self.request.getfirst('id',None)
        jobUrl = registry.getJobURL(jobId)
        desc = self.session.getJobDescription(jobUrl)
        jobDetail = {}
        jobDetail['description'] = desc 
        self.jsonMap = jobDetail
    elif action_name == 'get_all_labels':
        jobDetail = {}
        if self.session is not None:
            allUniqlabels = self.session.getAllUniqueLabels()
            if not allUniqlabels:
                allUniqlabels = []
            jobDetail['labels'] = allUniqlabels 
        self.jsonMap = jobDetail
    elif action_name == 'get_job_labels':
        jobId  = self.request.getfirst('id',None)
        jobUrl = registry.getJobURL(jobId)
        jobDetail = {}
        if self.session is not None:
            labels = self.session.getJobLabels(jobUrl)
            if not labels :
                labels = []
            jobDetail['labels'] = labels
        self.jsonMap = jobDetail
    elif action_name == 'get_job_status':
        jobId =self.request.getfirst('id',None)
        jobUrl = registry.getJobURL(jobId)
        jobDetails = None
        if self.session is not None:
            job = self.session.getJobBMPS(jobUrl)
            if job is not None:
                jf = JobFacade.getFromJobId( job[ 'jobID' ] )
                job['subjobs']  = jf.getSubJobs() 
                job['jobPID'] = jobId
                jobDetails = get_formatted_job_entries(job,self.session)
        self.jsonMap = [jobDetails]
    elif action_name == 'delete_jobs':
        pids = self.request.getlist('id')
        for pid in pids:
            self.session.removeJob(registry.getJobURL(pid))
        self.jsonMap['ok'] = str(True)
    elif action_name == 'get_jobs':
        self.mime_type="application/json"
        if self.session is None:
            jobs = []
        else:
            jobs = self.session.getAllJobs()
        limit = self.request.getfirst('limit',None)
        offset = self.request.getfirst('offset',None)
        sortType = self.request.getfirst('order','desc')
        cgiSortField  = self.request.getfirst('sortedBy','Submitted Time')
        cgiFilteredByField = self.request.getfirst('filteredBy',"")
        if debug:
            print cgiFilteredByField
        jobsOut = []
        if sortType == 'desc' :
            reverse = True
        else :
            reverse = False

        if cgiSortField == 'Job ID':
            sortField = 'jobID'
        if cgiSortField == 'Job Name':
            sortField = 'description'
        elif cgiSortField == 'Job Status':
            sortField = 'status'
        else :
            #cgiSortField =='Submitted Time' :
            sortField = 'jobSortDate'

        filteredBy= {}
        cgiFilteredByField = re.sub("%3B",";",cgiFilteredByField)
        #cgiFilteredByField = re.sub(";*$","",cgiFilteredByField)
        if len(cgiFilteredByField) > 0 :
            for eachField in cgiFilteredByField.split(';'):
                fieldContent = eachField.split(':')
                if debug:
                    print fieldContent
                values = fieldContent[1].split(',') 
                filteredBy[fieldContent[0]] =  values


        fromDate = toDate = None
        if ('Submitted Time' in filteredBy) :
            dateValues = filteredBy['Submitted Time']
            if (len(dateValues) < 2) :
                toDate      = int(time.strftime( "%Y%m%d%H%M%S"))
            else:
                toDate    = int(time.strftime( "%Y%m%d%H%M%S", time.strptime(dateValues[1],"%m/%d/%Y")))

            fromDate    = int(time.strftime( "%Y%m%d%H%M%S", time.strptime(dateValues[0],"%m/%d/%Y")))

        for job in jobs:
            formatted_job = get_formatted_job_entries(job,self.session,compact=True)
            jobUrl = registry.getJobURL(job['jobID'])
            descr = self.session.getJobDescription(jobUrl)
            if not descr:
                descr = self.session.getJob(jobUrl).get('userName',job['jobID'])
                self.session.setJobDescription(jobUrl,descr)
            formatted_job['description'] = descr
            labels = self.session.getJobLabels(jobUrl)
            if not labels :
                labels = []
            formatted_job['labels'] = ", ".join(labels)
            formatted_job['filteredBy'] = filteredBy
            if ('Job ID' in filteredBy and ( not _match_string(filteredBy['Job ID'], [ formatted_job['jobID'] ] ))):
                continue
            if ('Job Name' in filteredBy and ( not _match_string(filteredBy['Job Name'], [ formatted_job['description'] ] ))):
                continue
            if ('Job Status' in filteredBy and ( not _match_string(filteredBy['Job Status'], [ formatted_job['status'] ] ))):
                continue
            if ('Labels' in filteredBy and ( not _match_string(labels,filteredBy['Labels'], strict = True ))):
                continue
            if ('Submitted Time' in filteredBy) :
                a = int(formatted_job['jobSortDate'])
                if ( a < fromDate or a > toDate) :
                    continue
            
            jobsOut.append(formatted_job)
        jobsOut = sorted(jobsOut,key=itemgetter(sortField),reverse=reverse)

        jobDetails = {}
        jobDetails['total'] = len(jobsOut)
        
        if (offset == None ) :
            offsetInt = 0
        else :
            offsetInt = int(offset)

        if (limit == None or limit == "All" or len(jobsOut) < offsetInt + int(limit)):
            jobDetails['jobs']  = jobsOut[offsetInt:len(jobsOut)]
        elif (int(limit) > 0 and len(jobsOut) >= int(limit) + offsetInt):
            jobDetails['jobs']  = jobsOut[offsetInt:int(limit) + offsetInt]
        self.jsonMap = jobDetails
    elif action_name == 'get_params_status' :
        app_name = self.request.getfirst('app_name')
        task_id = self.request.getfirst('app_id')
        program_url = registry.serversByName['local'].programsByName[app_name].url
        service = parseService(program_url)
        evaluator = service.getEvaluator()
        paramsStatus = {}
        for paramName in service.getUserInputParameterByArgpos():
            param = service.getParameter(paramName)
            paramValue = self.request.getfirst(paramName,None)
            if paramValue is not None :
                param.setValue(paramValue)
        for paramName in service.getUserInputParameterByArgpos() + service.getAllOutParameter():
            param = service.getParameter(paramName)
            preconds = service.getPreconds( paramName , proglang='python' )
            allPrecondTrue = True
            for precond in preconds:
                try:
                    evaluatedPrecond = evaluator.eval( precond )
                except EvaluationError, err:
                    warnings.warn(err)
                if not evaluatedPrecond :
                    allPrecondTrue = False
                    break
            paramsStatus[paramName]= (allPrecondTrue,", ".join(preconds))
            #if value is None :
            #    if service.ismandatory( paramName ):
            #       paramsStatus[''] = "Mandatory Parameter mission" 
        self.jsonMap = paramsStatus

def getJobDetails(jobState,name=None):
    out ={}
    jobdatestring = jobState.getDate()
    jobId = jobState.getID()
    jobId = re.sub("/$","",jobId)
    m = re.search(".*/(.+?/.+?)$",jobId)
    if m:
        out['id'] = m.group(1)
    else:
        out['id'] = jobId
    out['url'] = jobState.getID() 
    if (name):
        out['title'] = name
    elif (name is None and jobState.getName()):
        out['title'] = jobState.getName() + ' - ' + jobdatestring
    else:
        out['title'] =  ' job - ' + jobdatestring
    status_manager = StatusManager()
    out['status'] = str(status_manager.getStatus(jobState.getDir()))
    out['date'] =jobdatestring 
    return out

def getJobLinks(wfNode):
    jobLinksEle = wfNode.findall( './jobLink' )
    out = []
    for jobLinkEle in jobLinksEle:
        out.append((jobLinkEle.get('taskRef'),jobLinkEle.get('jobId')))
    return out

#http://www.peterbe.com/plog/uniqifiers-benchmark - function f5
def _unique_list(seq, idfun=None): 
    # order preserving
    if idfun is None:
        def idfun(x): return x
    seen = {}
    result = []
    for item in seq:
        marker = idfun(item)
        if marker in seen: continue
        seen[marker] = 1
        result.append(item)
    return result

def _match_string(stringVals,theList,strict=False):
    for s in theList:
        if debug:
            print "%s - %s" % (s,"x")
        for stringVal in stringVals:
            if not strict and stringVal.lower() in s.lower():
                return True
            if strict and stringVal == s :
                return True
    return False

def get_formatted_job_entries(job,session,compact=False):
    """
    get_formatted_job_entries formats a list of jobs so that it can be both
    serialized to json and later used by the portal.
    """
    #warnings.warn(str(job))
    if job.has_key('date') and job['date'] is not None and (not isinstance(job['date'],str)):
        jobdatestring = time.strftime( "%x    %H:%M", job['date'])
        jobsortdate = time.strftime( "%Y%m%d%H%M%S", job['date'])
    else:
        jobdatestring = '?'
        jobsortdate = '?'
    job['status_message'] = ""
    if hasattr(job['status'], 'message'):
        job['status_message'] = job['status'].message
    job['status'] = str(job['status'])
    job['url'] = job['jobID']
    job['jobID'] = job['jobPID']

    l = job['jobID'].split('.')
    if len(l)==2:
        server_name='local'
    else:
        server_name= l[0]
    jobUrl = job['url']
    description = ''
    labels = []
    allUniqlabels = []
    user_name = job['jobID']
    # If the job is in the session
    if session.hasJob(jobUrl):
        description = session.getJobDescription(jobUrl)
        user_name = session.getJobUserName(jobUrl)
        labels = session.getJobLabels(jobUrl)
        allUniqlabels = session.getAllUniqueLabels()
    params = []
    other_params = {}
    jobState = JobState(jobUrl)
    for param_name,value in jobState.getInputFiles() or []:
        params.append(param_name)
    outputs     = dict(jobState.getOutputs() or [])
    inputs     = dict(jobState.getInputFiles() or [])
    for param_name,value in jobState.getArgs().items() :
        if param_name in params:
            continue
        other_params[param_name] = value
    command_line_str = ''
    if jobState.isWorkflow() == False:
        command_line_str = jobState.getCommandLine() 
    #command_line_str = str(type(jobState))
    values = {
             'server':server_name,
             'programName': job['programName'] ,
             'jobDate': jobdatestring ,
             'jobSortDate':jobsortdate,
             'status':job['status'] ,
             'status_message':job['status_message'],
             'dataUsed': job.get('dataUsed'),
             'url':job['url'],
             'labels': labels,
             'description': description,
             'userName': user_name,
             'all_unique_labels': allUniqlabels,
             'jobID': job['jobID']}
    top_job_data = values
    top_job_data['userName'] = job['userName']
    if (not compact) :
        values['outputs']= outputs
        values['inputs']=inputs
        values['other_parameters']=other_params
        values['command_line_str'] = command_line_str
        if job.has_key('owner'):
            values['owner'] = job['owner']
        subjob_list = []
        if job.has_key('subjobs') and job['subjobs'] is not None:
            for subjob in job['subjobs']:
                subjob_list.append(get_formatted_job_entries(subjob,session))    
        top_job_data['tasks'] = subjob_list 
    return top_job_data

if __name__ == "__main__":
    mb_cgi.JSONCGI(processFunction=process,useSession=True)

