Files
ember-panda/addon/utils/project/workflow-action-definition-offset-nodes.coffee
2017-12-21 11:51:55 +01:00

296 lines
18 KiB
CoffeeScript

#projectGetWorkflowActionDefinitionOffsets = () ->
# true
#export default projectGetWorkflowActionDefinitionOffsets
import Ember from 'ember'
RecordNode = Ember.Object.extend
resolved: false
title: Ember.computed 'record.title', 'value_shift', ->
title = @get('record.title')
title = " #{title} (#{value_shift}d)" if value_shift = @get('value_shift')
title
OffsetNode = Ember.Object.extend
record: null
total_offset: 0
value_shift: 0
resulting_absolute_value: 0
WorkflowActionDefinitionOffsetNodes = Ember.Object.extend
iteration_context_counter: 0
workflow_action_definitions: []
reference_date: null
batch_size: 0
active_project_scenario: null
forEach: ->
@get('processed_workflow_action_definitions').forEach(arguments...)
filterBy: ->
@get('processed_workflow_action_definitions').filterBy(arguments...)
get_resolved: ->
@get('processed_workflow_action_definitions').filterBy('resolved')
init: ->
@set 'scope_workflow_action_definition_ids', @get('workflow_action_definitions').mapBy('id').toArray()
@set 'processed_workflow_action_definitions', []
@set 'working_workflow_action_definitions', []
@get_workflow_action_definition_offsets()
get_workflow_action_definition_offsets: ->
base = @
active_scenario_identifier = @get('active_project_scenario.identifier')
scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
first_loop_actions = []
working_workflow_action_definitions = []
@get('workflow_action_definitions').forEach (workflow_action_definition) ->
action_added = false
if workflow_action_definition.get('offset_type') is 'configured'
workflow_action_definition.get('workflow_action_offsets').forEach (workflow_action_offset) ->
return if action_added
return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
if not workflow_action_offset.get('base_workflow_action_definition.content') # x_based
[offset_date, offset_value] = base.get_offset_for_target(workflow_action_offset, 0)
[date, value_shift] = base.get_dynamic_offset_for_target(workflow_action_offset, offset_value)
absolute_value = offset_value + value_shift
record_wrapper = RecordNode.create
record: workflow_action_definition
record_id: workflow_action_definition.get('id')
offset_wrappers: [
OffsetNode.create(total_offset: absolute_value, value_shift: value_shift)
]
absolute_value: absolute_value
value_shift: value_shift
date: date
resolve_type: 'x'
resolved: true
resolve_level: 0
first_loop_actions.push record_wrapper
action_added = true
working_workflow_action_definitions.push workflow_action_definition unless action_added
@set 'processed_workflow_action_definitions', first_loop_actions
@set 'working_workflow_action_definitions', working_workflow_action_definitions
@incrementProperty 'iteration_context_counter'
#@set 'working_workflow_action_definitions', @get('workflow_action_definitions').filterBy('offset_type', 'configured')
@run_critical_path_resolve_loop()
resolve_level = @get('iteration_context_counter')
@get('working_workflow_action_definitions').forEach (unresolved_workflow_action_definition) => # add unresolved records
offset_wrappers = []
unresolved_workflow_action_definition.get('workflow_action_offsets').forEach (action_offset) ->
return unless action_offset.get('base_workflow_action_definition.id') in scope_workflow_action_definition_ids # do not add offsets from other scenarios
offset_wrappers.push OffsetNode.create
total_offset: action_offset.get('offset_value')
record: action_offset
@get('processed_workflow_action_definitions').push RecordNode.create
record: unresolved_workflow_action_definition
record_id: unresolved_workflow_action_definition.get('id')
offset_wrappers: offset_wrappers
resolved: false
resolve_level: resolve_level
resolve_type: 'unresolved'
get_dynamic_offset_for_target: (target, absolute_value) ->
base_date = @get('reference_date').clone().add(absolute_value, 'day')
empty = [base_date, 0]
return empty unless batch_size = @get('batch_size')
return empty unless threshold = target.get('batch_element_threshold_value')
return empty unless threshold_exceedance = Math.floor(batch_size / (0.1 + parseInt(threshold))) # + 0.1 (delta) to exclude exact match of batch_size and threshold. First day is action day
period_unit = target.get('batch_element_threshold_unit')
offset_value = target.get('offset_value')
operation = if offset_value < 0 then 'subtract' else 'add'
if period_unit is 'businessday'
operation = "business#{operation.capitalize()}"
shifted_date = base_date.clone()[operation](threshold_exceedance, business_days: target.get('batch_element_threshold_business_days'))
else
shifted_date = base_date.clone()[operation](threshold_exceedance, period_unit)
value_shift = shifted_date.diff(base_date, 'day')
[shifted_date, value_shift]
get_offset_for_target: (target, absolute_value) ->
base_date = @get('reference_date').clone().add(absolute_value, 'day')
empty = [base_date, 0]
return empty unless offset_value = target.get('offset_value')
period_unit = target.get('offset_unit')
operation = if offset_value < 0 then 'subtract' else 'add'
if period_unit is 'businessday'
operation = "business#{operation.capitalize()}"
shifted_date = base_date.clone()[operation](Math.abs(offset_value), business_days: target.get('offset_business_days'))
else
shifted_date = base_date.clone()[operation](Math.abs(offset_value), period_unit)
value_shift = shifted_date.diff(base_date, 'day')
#debugger if target.get('workflow_action_definition.title') is 'Initial check done'
[shifted_date, value_shift]
run_critical_path_resolve_loop: ->
working_indices_to_be_removed = []
active_scenario_identifier = @get('active_project_scenario.identifier')
processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
get_offset_for_target = @get('get_offset_for_target').bind(@)
resolve_level = @get('iteration_context_counter')
@get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
return unless working_workflow_action_definition.get('offset_type') is 'configured'
farthest_absolute_value = 0
farthest_absolute_value_value_shift = 0
farthest_result_total_offset = 0
date = null # scoping
resolved_offset_wrappers = []
all_offsets_resolved = working_workflow_action_definition.get('workflow_action_offsets').every (workflow_action_offset) ->
return true if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('base_workflow_action_definition.id')
if workflow_action_offset.get('base_workflow_action_definition.id') not in scope_workflow_action_definition_ids # base is not present in this scenario, do not pursue this connection
return true
return false
processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
[offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
[date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
current_connection_total_offset_value = current_connection_value_shift + working_offset_value
current_connection_absolute_value = processed_absolute_value + current_connection_total_offset_value
#debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
#debugger if working_workflow_action_definition.get('title') is 'Initial check done'
if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
farthest_absolute_value = current_connection_absolute_value
farthest_absolute_value_value_shift = current_connection_value_shift
farthest_result_total_offset = current_connection_total_offset_value
resolved_offset_wrappers.push OffsetNode.create
record: workflow_action_offset
value_shift: current_connection_value_shift
total_offset: current_connection_total_offset_value
resulting_absolute_value: current_connection_absolute_value
true # indicate processed_base_workflow_action_definition_wrapper found for all_offsets_resolved value
if all_offsets_resolved and resolved_offset_wrappers.length
record_wrapper = RecordNode.create
record: working_workflow_action_definition
record_id: working_workflow_action_definition.get('id')
offset_wrappers: resolved_offset_wrappers
value_shift: farthest_absolute_value_value_shift
absolute_value: farthest_absolute_value
date: date
resolved: true
resolve_level: resolve_level
resolve_type: 'critical-path'
processed_workflow_action_definitions.push record_wrapper
working_indices_to_be_removed.push working_index
if working_indices_to_be_removed.length
@get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
@run_critical_path_resolve_loop()
else
@incrementProperty 'iteration_context_counter'
@run_unresolved_tree_resolve_loop()
run_unresolved_tree_resolve_loop: ->
working_indices_to_be_removed = []
active_scenario_identifier = @get('active_project_scenario.identifier')
processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
get_offset_for_target = @get('get_offset_for_target').bind(@)
get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
resolve_level = @get('iteration_context_counter')
@get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
return unless working_workflow_action_definition.get('offset_type') is 'configured'
farthest_absolute_value = 0
farthest_absolute_value_value_shift = 0
farthest_result_total_offset = 0
date = null # scoping
resolved_offset_wrappers = []
working_workflow_action_definition.get('workflow_action_offsets').forEach (workflow_action_offset) ->
return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
return unless workflow_action_offset.get('base_workflow_action_definition.id') in scope_workflow_action_definition_ids
return unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('base_workflow_action_definition.id')
processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
[offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
[date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
current_connection_total_offset_value = current_connection_value_shift + working_offset_value
current_connection_absolute_value = processed_absolute_value + current_connection_total_offset_value
#debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
#debugger if working_workflow_action_definition.get('title') is 'Initial check done'
if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
farthest_absolute_value = current_connection_absolute_value
farthest_absolute_value_value_shift = current_connection_value_shift
farthest_result_total_offset = current_connection_total_offset_value
resolved_offset_wrappers.push OffsetNode.create
record: workflow_action_offset
value_shift: current_connection_value_shift
total_offset: current_connection_total_offset_value
resulting_absolute_value: current_connection_absolute_value
if resolved_offset_wrappers.length
record_wrapper = RecordNode.create
record: working_workflow_action_definition
record_id: working_workflow_action_definition.get('id')
offset_wrappers: resolved_offset_wrappers # .clone()?
value_shift: farthest_absolute_value_value_shift
absolute_value: farthest_absolute_value
date: date
resolved: true
resolve_level: resolve_level
resolve_type: 'with-non-rooted-offsets'
processed_workflow_action_definitions.push record_wrapper
working_indices_to_be_removed.push working_index
if working_indices_to_be_removed.length
@get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
@run_critical_path_resolve_loop()
else
@run_offset_forwarding_loop()
run_offset_forwarding_loop: ->
working_indices_to_be_removed = []
active_scenario_identifier = @get('active_project_scenario.identifier')
processed_workflow_action_definitions = @get('processed_workflow_action_definitions')
scope_workflow_action_definition_ids = @get('scope_workflow_action_definition_ids')
get_offset_for_target = @get('get_offset_for_target').bind(@)
get_dynamic_offset_for_target = @get('get_dynamic_offset_for_target').bind(@)
resolve_level = @get('iteration_context_counter')
@get('working_workflow_action_definitions').forEach (working_workflow_action_definition, working_index) ->
farthest_absolute_value = 0
farthest_absolute_value_value_shift = 0
farthest_result_total_offset = 0
date = null # scoping
resolved_offset_wrappers = []
working_workflow_action_definition.get('dependent_workflow_action_offsets').forEach (workflow_action_offset) ->
return if active_scenario_identifier and workflow_action_offset.get('project_scenario.identifier') and workflow_action_offset.get('project_scenario.identifier') isnt active_scenario_identifier
return unless workflow_action_offset.get('workflow_action_definition.id') in scope_workflow_action_definition_ids
return unless processed_base_workflow_action_definition_wrapper = processed_workflow_action_definitions.findBy 'record_id', workflow_action_offset.get('workflow_action_definition.id')
#return if workflow_action_offset.get('workflow_action_definition.offset_type') is 'none' # do not resolve on none offset typed nodes
processed_absolute_value = processed_base_workflow_action_definition_wrapper.get('absolute_value')
[offset_date, working_offset_value] = get_offset_for_target(workflow_action_offset, processed_absolute_value)
[date, current_connection_value_shift] = get_dynamic_offset_for_target(workflow_action_offset, processed_absolute_value + working_offset_value)
current_connection_total_offset_value = current_connection_value_shift + working_offset_value
#NOTE MINUS!!!!!!!!!!!!!!!!!!!!!
current_connection_absolute_value = processed_absolute_value - current_connection_total_offset_value
#debugger if processed_base_workflow_action_definition_wrapper.get('record.title') is 'Initial check done'
#debugger if working_workflow_action_definition.get('title') is 'Initial check done'
if Math.abs(current_connection_absolute_value) > Math.abs(farthest_absolute_value)
farthest_absolute_value = current_connection_absolute_value
farthest_absolute_value_value_shift = current_connection_value_shift
farthest_result_total_offset = current_connection_total_offset_value
resolved_offset_wrappers.push OffsetNode.create
record: workflow_action_offset
value_shift: current_connection_value_shift
total_offset: current_connection_total_offset_value
resulting_absolute_value: current_connection_absolute_value
if resolved_offset_wrappers.length
record_wrapper = RecordNode.create
record: working_workflow_action_definition
record_id: working_workflow_action_definition.get('id')
offset_wrappers: resolved_offset_wrappers # .clone()?
value_shift: farthest_absolute_value_value_shift
absolute_value: farthest_absolute_value
date: date
resolved: true
resolve_level: resolve_level
resolve_type: 'offset-lookup-inversion'
processed_workflow_action_definitions.push record_wrapper
working_indices_to_be_removed.push working_index
if working_indices_to_be_removed.length
@get('working_workflow_action_definitions').splice(working_indices_to_be_removed.pop(), 1) while working_indices_to_be_removed.length
@run_critical_path_resolve_loop()
export default WorkflowActionDefinitionOffsetNodes