<commit_before>/*
 Copyright 2013 SimplifyOps Inc, <http://simplifyops.com>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

package rundeck.services.workflow

import com.dtolabs.rundeck.core.execution.workflow.state.ExecutionState
import com.dtolabs.rundeck.core.execution.workflow.state.StateUtils
import com.dtolabs.rundeck.core.execution.workflow.state.StepIdentifier
import com.dtolabs.rundeck.core.execution.workflow.state.StepState
import com.dtolabs.rundeck.core.execution.workflow.state.WorkflowState
import com.dtolabs.rundeck.core.execution.workflow.state.WorkflowStepState

import java.text.SimpleDateFormat

/**
 * read/write a generic Map from a WorkflowState
 */
class StateMapping {

    def Map summarize(Map map,List<String> nodes,boolean selectedOnly){
        def nodeSummaries=[:]
        def nodeSteps=[:]
        (selectedOnly?nodes:map.allNodes).each{node->

            def states = stepStatesForNode(map, node)
            nodeSummaries[node]=summarizeForNode(map,node, states)
            if(nodes.contains(node)){
                nodeSteps[node]=states
            }
        }
        map.nodeSummaries=nodeSummaries
        map.nodeSteps=nodeSteps
        map.steps=[]
        map.remove('nodes')
        map.remove('targetNodes')
        map
    }
    def boolean stateCompare(a,b){
        if (a == b) {
            return false
        }
        def states = ['SUCCEEDED', 'NONE','NOT_STARTED', 'WAITING', 'FAILED', 'ABORTED', 'RUNNING', 'RUNNING_HANDLER'];
        def ca = states.indexOf(a);
        def cb = states.indexOf(b);
        if (ca < 0) {
            return true;
        }
        return cb > ca
    }
    def Map summarizeForNode(Map map,String node, List steps){
        def summary=[:]
        def currentStep=[:];

        //step summary info
        def summarydata = [
            total: 0,
            SUCCEEDED: 0,
            FAILED: 0,
            WAITING: 0,
            NOT_STARTED: 0,
            RUNNING: 0,
            RUNNING_HANDLER: 0,
            other: 0,
            duration_ms_total: 0,
        ]

        Date updated=null;
        def testStates= ['SUCCEEDED', 'FAILED', 'WAITING', 'NOT_STARTED', 'RUNNING', 'RUNNING_HANDLER'];
        def duration=-1;
        Date dateStarted=null;
        steps.each{step->
            def z = step.executionState;
            if(step.stepctx.indexOf('@')<0) {
                summarydata.total++
                if (testStates.indexOf(z) >= 0 && null != summarydata[z]) {
                    summarydata[z]++;
                } else {
                    summarydata['other']++;
                }
            }
            if (!currentStep && stateCompare('NONE', step.executionState)
                    || currentStep && stateCompare(currentStep.executionState, step.executionState)) {
                currentStep.putAll(step);
            }
            def started=step.startTime?decodeDate(step.startTime):null
            if(!dateStarted || (started && started<dateStarted)){
                dateStarted=started;
            }
            def lastUpdated=lastUpdatedFor(step)
            if(!updated || lastUpdated>updated){
                updated=lastUpdated
            }
        }
        if (updated && dateStarted) {
            duration = updated.time - dateStarted.time
        }
        summary.duration=duration
        if(currentStep){
            summary.currentStep=currentStep
        }
        summary.lastUpdated=encodeDate(updated)

        //based on step states set the summary for this node
        if (summarydata.total > 0) {
            if (summarydata.RUNNING > 0) {
                summary.summaryState=("RUNNING");
            } else if (summarydata.RUNNING_HANDLER > 0) {
                summary.summaryState=("RUNNING_HANDLER");
            } else if (summarydata.total == summarydata.SUCCEEDED && summarydata.pending < 1) {
                summary.summaryState=("SUCCEEDED");
            } else if (summarydata.FAILED > 0) {
                summary.FAILED=summarydata.FAILED;
                summary.summaryState=("FAILED");
            } else if (summarydata.WAITING > 0) {
                summary.WAITING=summarydata.WAITING;
                summary.summaryState=("WAITING");
            } else if (summarydata.NOT_STARTED == summarydata.total && summarydata.pending < 1) {
                summary.summaryState=("NOT_STARTED");
            } else if (summarydata.NOT_STARTED > 0) {
                summary.PARTIAL_NOT_STARTED=summarydata.NOT_STARTED;
                summary.summaryState=("PARTIAL_NOT_STARTED");
            }else if(summarydata.pending > 0 ){
                summary.summaryState=("WAITING");
            } else if (summarydata.SUCCEEDED > 0) {
                summary.PARTIAL_SUCCEEDED=summarydata.total - summarydata.SUCCEEDED;
                summary.summaryState=("PARTIAL_SUCCEEDED");
            } else {
                summary.summaryState=("NONE_SUCCEEDED");
            }
        } else if(summarydata.pending > 0){
            summary.summaryState=("WAITING");
        } else {
            summary.summaryState=("NONE");
        }
        summary
    }
    def long durationForStep(Map step){
        if(step.duration){
            return step.duration
        }else if(step.startTime && (step.endTime || step.updateTime)){
            Date start=decodeDate(step.startTime)
            Date end=step.endTime?decodeDate(step.endTime):null
            Date update=step.updateTime?decodeDate(step.updateTime):null
            return (end!=null?end.time:update.time)-start.time
        }else{
            -1
        }
    }
    def Date lastUpdatedFor(Map step){
        if(step.endTime || step.updateTime){
            long end=step.endTime?decodeDate(step.endTime).time:0
            long update=step.updateTime?decodeDate(step.updateTime).time:0
            return new Date(Math.max(end,update))
        }else{
            null
        }
    }
    def String pluralize(int amount, String singular, String plural=null){
        return amount==1?singular: plural?:singular+'s'
    }
    def List stepStatesForNode(Map map,String node){
        def newsteps=[]
        def steps = map.nodes[node]
        steps.each{step->
            def stepStateForCtx = stepStateForCtx(map, StateUtils.stepIdentifierFromString(step.stepctx),node)
            if(stepStateForCtx) {
                def found = stepStateForCtx.nodeStates?.get(node)
                if (found) {
                    def newfound=new HashMap(found)
                    newfound.stepctx = step.stepctx
                    newsteps.push(newfound)
                }
            }
        }
        newsteps
    }

    def Map stepStateForCtx(Map model,StepIdentifier stepctx,String node=null){

        def stepid = stepctx.context[0]

        def ndx = stepid.step - 1
        def params = stepid.params?StateUtils.parameterString(stepid.params):''

        Map step = model.steps[ndx];
        if(params && step.parameterStates && step.parameterStates[params]){
            step = step.parameterStates[params];
        }else if(!params && node && step.parameterStates["node=${node}"]){
            step = step.parameterStates["node=${node}"];
        }
        if (stepctx.context.size()>1 && step.workflow) {
            return stepStateForCtx(step.workflow, StateUtils.stepIdentifierTail(stepctx))
        } else {
            return step
        }
    }
    def Map mapOf(Long id, WorkflowState workflowState) {<commit_msg>avoid NPE if step state not found<commit_after>/*
 Copyright 2013 SimplifyOps Inc, <http://simplifyops.com>

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

package rundeck.services.workflow

import com.dtolabs.rundeck.core.execution.workflow.state.ExecutionState
import com.dtolabs.rundeck.core.execution.workflow.state.StateUtils
import com.dtolabs.rundeck.core.execution.workflow.state.StepIdentifier
import com.dtolabs.rundeck.core.execution.workflow.state.StepState
import com.dtolabs.rundeck.core.execution.workflow.state.WorkflowState
import com.dtolabs.rundeck.core.execution.workflow.state.WorkflowStepState

import java.text.SimpleDateFormat

/**
 * read/write a generic Map from a WorkflowState
 */
class StateMapping {

    def Map summarize(Map map,List<String> nodes,boolean selectedOnly){
        def nodeSummaries=[:]
        def nodeSteps=[:]
        (selectedOnly?nodes:map.allNodes).each{node->

            def states = stepStatesForNode(map, node)
            nodeSummaries[node]=summarizeForNode(map,node, states)
            if(nodes.contains(node)){
                nodeSteps[node]=states
            }
        }
        map.nodeSummaries=nodeSummaries
        map.nodeSteps=nodeSteps
        map.steps=[]
        map.remove('nodes')
        map.remove('targetNodes')
        map
    }
    def boolean stateCompare(a,b){
        if (a == b) {
            return false
        }
        def states = ['SUCCEEDED', 'NONE','NOT_STARTED', 'WAITING', 'FAILED', 'ABORTED', 'RUNNING', 'RUNNING_HANDLER'];
        def ca = states.indexOf(a);
        def cb = states.indexOf(b);
        if (ca < 0) {
            return true;
        }
        return cb > ca
    }
    def Map summarizeForNode(Map map,String node, List steps){
        def summary=[:]
        def currentStep=[:];

        //step summary info
        def summarydata = [
            total: 0,
            SUCCEEDED: 0,
            FAILED: 0,
            WAITING: 0,
            NOT_STARTED: 0,
            RUNNING: 0,
            RUNNING_HANDLER: 0,
            other: 0,
            duration_ms_total: 0,
        ]

        Date updated=null;
        def testStates= ['SUCCEEDED', 'FAILED', 'WAITING', 'NOT_STARTED', 'RUNNING', 'RUNNING_HANDLER'];
        def duration=-1;
        Date dateStarted=null;
        steps.each{step->
            def z = step.executionState;
            if(step.stepctx.indexOf('@')<0) {
                summarydata.total++
                if (testStates.indexOf(z) >= 0 && null != summarydata[z]) {
                    summarydata[z]++;
                } else {
                    summarydata['other']++;
                }
            }
            if (!currentStep && stateCompare('NONE', step.executionState)
                    || currentStep && stateCompare(currentStep.executionState, step.executionState)) {
                currentStep.putAll(step);
            }
            def started=step.startTime?decodeDate(step.startTime):null
            if(!dateStarted || (started && started<dateStarted)){
                dateStarted=started;
            }
            def lastUpdated=lastUpdatedFor(step)
            if(!updated || lastUpdated>updated){
                updated=lastUpdated
            }
        }
        if (updated && dateStarted) {
            duration = updated.time - dateStarted.time
        }
        summary.duration=duration
        if(currentStep){
            summary.currentStep=currentStep
        }
        summary.lastUpdated=encodeDate(updated)

        //based on step states set the summary for this node
        if (summarydata.total > 0) {
            if (summarydata.RUNNING > 0) {
                summary.summaryState=("RUNNING");
            } else if (summarydata.RUNNING_HANDLER > 0) {
                summary.summaryState=("RUNNING_HANDLER");
            } else if (summarydata.total == summarydata.SUCCEEDED && summarydata.pending < 1) {
                summary.summaryState=("SUCCEEDED");
            } else if (summarydata.FAILED > 0) {
                summary.FAILED=summarydata.FAILED;
                summary.summaryState=("FAILED");
            } else if (summarydata.WAITING > 0) {
                summary.WAITING=summarydata.WAITING;
                summary.summaryState=("WAITING");
            } else if (summarydata.NOT_STARTED == summarydata.total && summarydata.pending < 1) {
                summary.summaryState=("NOT_STARTED");
            } else if (summarydata.NOT_STARTED > 0) {
                summary.PARTIAL_NOT_STARTED=summarydata.NOT_STARTED;
                summary.summaryState=("PARTIAL_NOT_STARTED");
            }else if(summarydata.pending > 0 ){
                summary.summaryState=("WAITING");
            } else if (summarydata.SUCCEEDED > 0) {
                summary.PARTIAL_SUCCEEDED=summarydata.total - summarydata.SUCCEEDED;
                summary.summaryState=("PARTIAL_SUCCEEDED");
            } else {
                summary.summaryState=("NONE_SUCCEEDED");
            }
        } else if(summarydata.pending > 0){
            summary.summaryState=("WAITING");
        } else {
            summary.summaryState=("NONE");
        }
        summary
    }
    def long durationForStep(Map step){
        if(step.duration){
            return step.duration
        }else if(step.startTime && (step.endTime || step.updateTime)){
            Date start=decodeDate(step.startTime)
            Date end=step.endTime?decodeDate(step.endTime):null
            Date update=step.updateTime?decodeDate(step.updateTime):null
            return (end!=null?end.time:update.time)-start.time
        }else{
            -1
        }
    }
    def Date lastUpdatedFor(Map step){
        if(step.endTime || step.updateTime){
            long end=step.endTime?decodeDate(step.endTime).time:0
            long update=step.updateTime?decodeDate(step.updateTime).time:0
            return new Date(Math.max(end,update))
        }else{
            null
        }
    }
    def String pluralize(int amount, String singular, String plural=null){
        return amount==1?singular: plural?:singular+'s'
    }
    def List stepStatesForNode(Map map,String node){
        def newsteps=[]
        def steps = map.nodes[node]
        steps.each{step->
            def stepStateForCtx = stepStateForCtx(map, StateUtils.stepIdentifierFromString(step.stepctx),node)
            if(stepStateForCtx) {
                def found = stepStateForCtx.nodeStates?.get(node)
                if (found) {
                    def newfound=new HashMap(found)
                    newfound.stepctx = step.stepctx
                    newsteps.push(newfound)
                }
            }
        }
        newsteps
    }

    def Map stepStateForCtx(Map model,StepIdentifier stepctx,String node=null){

        def stepid = stepctx.context[0]

        def ndx = stepid.step - 1
        def params = stepid.params?StateUtils.parameterString(stepid.params):''

        Map step = model.steps[ndx];
        if (!step) {
            return null
        }
        if(params && step.parameterStates && step.parameterStates[params]){
            step = step.parameterStates[params];
        }else if(!params && node && step.parameterStates["node=${node}"]){
            step = step.parameterStates["node=${node}"];
        }
        if (stepctx.context.size()>1 && step.workflow) {
            return stepStateForCtx(step.workflow, StateUtils.stepIdentifierTail(stepctx))
        } else {
            return step
        }
    }
    def Map mapOf(Long id, WorkflowState workflowState) {
{'fraction_non_alphanumeric': 0.10697965571205008, 'fraction_numerical': 0.004882629107981221, 'mean_word_length': 2.0541005543873063, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '2772266', 'n_tokens_mistral': 4579, 'n_tokens_neox': 4413, 'n_words': 910}