class AMEE::DataAbstraction::OngoingCalculation

Instances of the OngoingCalculation class represent actual calculations made via the AMEE platform.

The class inherits from the Calculation class and is therefore primarly characterised by the label, name, and path attributes, as well as an associated instance of the TermsList class which represents each of the values (input, outputs, metdata) involved in the calculation.

Instances of OngoingCalcualtion are typically instantiated from an instance of PrototypeCalculation using the #begin_calculation method, e.g.

my_prototype.begin_calculation       #=> <AMEE::DataAbstraction::OngoingCalculation ...>

In this case, the new instance inherits all of the attributes and terms defined on the PrototypeCalculation template.

In contrast to instances of PrototypeCalculation, instances of the OngoingCalculation class will typically have their term values explicitly set according to the specific calculation scenario being represented. Other term attributes, e.g. units, may also be modified on a calculation-by-calculation basis. These values and other attributes form (at least some of) the data which is passed to the AMEE platform in order to make caluclations.

Constants

MemoizedProfileInformation

Methods which should be memoized once per interaction with AMEE to minimise API calls. These require wiping at every pass, because otherwise, they might change, e.g. if metadata changes change the profile

Attributes

invalidity_messages[RW]

Hash of invalidity messages. Keys are represent the labels of terms assocaited with self. Values are string error message reports associated with the keyed term.

profile_item_uid[RW]

String representing the AMEE platform profile item UID assocaited with self

profile_uid[RW]

String representing the AMEE platform profile UID assocaited with self

Public Class Methods

new() click to toggle source

Construct an Ongoing Calculation. Should be called only via PrototypeCalculation#begin_calculation. Not intended for external use.

Calls superclass method AMEE::DataAbstraction::Calculation::new
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 55
def initialize
  super
  dirty!
  reset_invalidity_messages
end

Public Instance Methods

==(other_calc) click to toggle source
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 243
def ==(other_calc)
  !terms.inject(false) do |boolean,term|
    boolean || term != other_calc[term.label]
  end && label == other_calc.label
end
amee_drill(options={}) click to toggle source

Instantiate an AMEE::Data::DrillDown object representing the drill down sequence defined by the drill terms associated with self. As with #drill_options, An optional hash argument can be provided, with the key :before in order to specify the drill down choice to which the representation is required, e.g.

my_calc.amee_drill(:before => :size)
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 547
def amee_drill(options={})
  AMEE::Data::DrillDown.get(connection,"/data#{path}/drill?#{drill_options(options)}")
end
autodrill() click to toggle source

Automatically set the value of a drill term if there is only one choice

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 520
def autodrill
  
  picks=amee_drill.selections
  picks.each do |path,value|
    # If drill term does not exist, initialize a dummy instance.
    #
    # This is useful in those cases where some drills selections are unecessary
    # (i.e. not all choices require selection for data items to be uniquely
    # identified) and removes the need to explicitly specify the blank drills
    # in configuration. This doesn't matter if calculations are auto configured.
    #
    if drill = drill_by_path(path)
      drill.value value
    else
      drills << Drill.new {path path; value value}
    end
  end
end
calculate!() click to toggle source

Synchonizes the current term values and attributes with the AMEE platform if self is dirty?, and subsequently calls clean! on self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 198
def calculate!
  return unless dirty?
  syncronize_with_amee
  clean!
end
choose(choice) click to toggle source

Similar to #choose! but returns false if any term attributes are invalid, rather than raising an exception. Returns true if validation is successful.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 185
def choose(choice)
  begin
    choose!(choice)
    return true
  rescue AMEE::DataAbstraction::Exceptions::ChoiceValidation
    return false
  end
end
choose!(choice) click to toggle source

Similar to #choose_without_validation! but performs validation on the modified input terms and raises a ChoiceValidation exception if any of the values supplied is invalid

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 174
def choose!(choice)
  choose_without_validation!(choice)
  validate!
  raise AMEE::DataAbstraction::Exceptions::ChoiceValidation.new(invalidity_messages) unless
    invalidity_messages.empty?
end
choose_without_validation!(choice) click to toggle source

Mass assignment of (one or more) term attributes (value, unit, per_unit) based on data defined in choice. choice should be a hash with keys representing the labels of terms which are to be updated. Hash values can represent either the the value to be assigned explicitly, or, alternatively, a hash representing any or all of the term value, unit and per_unit attributes (keyed as :value, :unit and :per_unit).

Unit attributes can be represented by any form which is accepted by the Quantify::Unit#for method (either an instance of Quantify::Unit::Base (or subclass) or a symbolized or string representation of the a unit symbol, name or label).

Nil values are ignored. Term attributes can be intentionally blanked by passing a blank string as the respective hash value.

Examples of options hash which modify only term values:

options = { :type => 'van' }

options = { :type => 'van',
            :distance => 100 }

options = { :type => 'van',
            :distance => "" }

Examples of options hash which modify other term attributes:

options = { :type => 'van',
            :distance => { :value => 100 }}

options = { :type => 'van',
            :distance => { :value => 100,
                           :unit => :mi }}

options = { :type => 'van',
            :distance => { :value => 100,
                           :unit => 'feet' }}

my_distance_unit = <Quantify::Unit::NonSI:0xb71cac48 @label="mi" ... >
my_time_unit     = <Quantify::Unit::NonSI:0xb71c67b0 @label="h" ... >

options = { :type => 'van',
            :distance => { :value => 100,
                           :unit => my_unit,
                           :per_unit => my_time_unit }}

my_calculation.choose_without_validation!(options)

Do not attempt to check that the values specified are acceptable.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 144
def choose_without_validation!(choice)
  # Make sure choice keys are symbols since they are mapped to term labels
  # Uses extension methods for Hash defined in /core_extensions
  choice = choice.recursive_symbolize_keys

  new_profile_uid= choice.delete(:profile_uid)
  self.profile_uid=new_profile_uid if new_profile_uid
  new_profile_item_uid= choice.delete(:profile_item_uid)
  self.profile_item_uid=new_profile_item_uid if new_profile_item_uid
  choice.each do |k,v|
    next unless self[k]
    if v.is_a? Hash
      # <tt>if has_key?</tt> clause included so that single attributes can
      # be updated without nullifying others if their values are not
      # explicitly passed. Intentional blanking of values is enabled by
      # passing nil or "".
      #
      self[k].value v[:value] if v.has_key?(:value)
      self[k].unit v[:unit] if v.has_key?(:unit)
      self[k].per_unit v[:per_unit] if v.has_key?(:per_unit)
    else
      self[k].value v
    end
  end
end
clean!() click to toggle source

Declare that the calculation is not dirty, and need not be sent to AMEE for results to be valid.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 81
def clean!
  inputs.each{|i| i.clean!}
  @dirty=false
end
clear_invalid_terms!() click to toggle source

Set the values of any invalid terms to nil. Can be called following any of the #choose... methods so that invalid terms resulting from modification of a previously valid calculation can be cleared. This is particularly useful in cases where drill down choices have been changed thus invalidating the choices for subsequent drills.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 230
def clear_invalid_terms!
  terms.select do |term|
    invalidity_messages.keys.include?(term.label)
  end.each do |term|
    term.value nil
  end
  reset_invalidity_messages
end
clear_outputs() click to toggle source
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 239
def clear_outputs
  outputs.each {|output| output.value nil }
end
dirty!() click to toggle source

Declare that the calculation is dirty, i.e. that changes to term values have been made since self was last synchronized with the AMEE platform, in which case a synchonization with with AMEE must occur for self to be valid.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 74
def dirty!
  @dirty=true
end
dirty?() click to toggle source

Returns true if the value of a term associated with self has been changed since the calculation was last synchronized with the AMEE platform. Otherwise, return false.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 65
def dirty?
  @dirty
end
invalid(label,message) click to toggle source

Declare that the term labelled by label has an unnaceptable value and load the message into the invalidity_messages hash.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 220
def invalid(label,message)
  @invalidity_messages[label]=message
end
satisfied?() click to toggle source

Returns true if all compulsory terms are set, i.e. ahve non-nil values. This inidicates that self is ready to be sent to the AMEE platform for outputs to be calculated

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 90
def satisfied?
  inputs.compulsory.unset.empty?
end
validate!() click to toggle source

Check that the values set for all terms are acceptable, and raise a ChoiceValidation exception if not. Error messages are available via the self.invalidity_messages hash.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 208
def validate!
  return unless dirty?
  reset_invalidity_messages
  inputs.each do |d|
    d.validate! unless d.unset?
  end
  autodrill
end

Private Instance Methods

amee_name() click to toggle source

Generate a unique name for the profile item assocaited with self. This is required in order to make similarly drilled profile items within the same profile distinguishable.

This is random at present but could be improved to generate more meaningful name by interrogating metadata according to specifications of an organisational model.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 421
def amee_name
  UUIDTools::UUID.timestamp_create
end
create_profile_item() click to toggle source

Create a profile item in the AMEE platform to be associated with self. Raises AlreadyHaveProfileItem exception if a profile item value is already associated with self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 430
def create_profile_item
  raise Exceptions::AlreadyHaveProfileItem unless profile_item_uid.blank?
  @profile_item = AMEE::Profile::Item.create(profile_category,amee_drill.data_item_uid,
    profile_options.merge(:get_item => true, :name => amee_name))
  self.profile_item_uid = @profile_item.uid
end
data_item() click to toggle source

Return the AMEE::Data::Item object associated with self. If not set, instantiates via the AMEE platform and assigns to self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 506
def data_item
  @data_item||=AMEE::Data::Item.get(connection, data_item_path, get_options)
end
data_item_path() click to toggle source

Returns a string representing the AMEE platform path to the data item associated with <tt>self</self>

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 492
def data_item_path
  "/data#{path}/#{data_item_uid}"
end
data_item_uid() click to toggle source

Returns a string representing the AMEE platform UID for the data item associated with <tt>self</self>

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 499
def data_item_uid
  profile_item.data_item_uid
end
delete_profile_item() click to toggle source

Delete the profile item which is associated with self from the AMEE platform and nullify the local references (i.e. @profile_item and #profile_item_uid)

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 469
def delete_profile_item
  AMEE::Profile::Item.delete(connection,profile_item_path)
  self.profile_item_uid=nil
  @profile_item=nil
end
drill_options(options={}) click to toggle source

Returns a String representation of drill down choices appropriate for submitting to the AMEE platform. An optional hash argument can be provided, with the key :before in order to specify the drill down choice to which the representation is required, e.g.

my_calc.drill_options                     #=> "type=van&fuel=petrol&size=2.0+litres"

my_calc.drill_options(:before => :size)   #=> "type=van&fuel=petrol"
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 362
def drill_options(options={})
  to=options.delete(:before)
  drills_to_use=to ? drills.before(to).set : drills.set
  drills_to_use.map{|x| "#{CGI.escape(x.path)}=#{CGI.escape(x.value)}"}.join("&")
end
find_profile() click to toggle source

Return the AMEE::Profile::Profile object under which the AMEE profile item associated with self belongs and and update self.profile_uid to make the appropriate reference.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 405
def find_profile
  unless self.profile_uid
    prof ||= AMEE::Profile::ProfileList.new(connection).first
    prof ||= AMEE::Profile::Profile.create(connection)
    self.profile_uid=prof.uid
  end
end
get_options() click to toggle source

Returns a Hash of options for profile item GET requests to the AMEE platform.

This is where is where return unit convertion requests can be handled if/when theseare implemented.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 393
def get_options
  # Specify unit options here based on the contents
  # getopts={}
  # getopts[:returnUnit] = params[:unit] if params[:unit]
  # getopts[:returnPerUnit] = params[:perUnit] if params[:perUnit]
  return {}
end
load_drills() click to toggle source

Load drill values from the AMEE platform. If the remote drills selections are different than locally set values, raise a Syncronization exception.

If an exception is raised, typical practice would be to delete the profile item associated with self and create a new one with the current selection of drill down choices (see, for example, the #synchronize_with_amee method

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 310
def load_drills
  return unless profile_item
  drills.each do |term|
    next unless term.value.nil? || term.dirty?
    ameeval=data_item.value(term.path)
    raise Exceptions::Syncronization if term.set? && ameeval!=term.value
    term.value ameeval
  end
end
load_metadata() click to toggle source

Load any metadata stored in the AMEE profile which can be used to set metadata for self. Not implemented in AMEE yet.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 284
def load_metadata  
end
load_outputs() click to toggle source

Obtain from the AMEE platform the results of a calculation, and set these to the output terms of self.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 259
def load_outputs
  outputs.each do |output|
    res=nil
    if output.path.to_s=='default'
      res= profile_item.amounts.find{|x| x[:default] == true}
    else
      res= profile_item.amounts.find{|x| x[:type] == output.path}
    end
    if res
      output.value res[:value]
      output.unit res[:unit]
      output.per_unit res[:per_unit]
    else
      # If no platform result, then no outputs should be set. Added so that
      # nullifying a compulsory PIV wipes output value, otherwise previous
      # output values persist after PIVs have been removed.
      #
      output.value nil
    end
  end
end
load_profile_item_values() click to toggle source

If a profile item exists for self, load the corresponding values and units for any unset Profile terms (profile item values) from the AMEE platform

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 291
def load_profile_item_values
  return unless profile_item
  profiles.unset.each do |term|
    ameeval=profile_item.values.find { |value| value[:path] == term.path }
    term.value ameeval[:value]
    term.unit ameeval[:unit]
    term.per_unit ameeval[:per_unit]
  end
end
new_memoize_pass() click to toggle source

Clear the memoized values.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 444
def new_memoize_pass
  MemoizedProfileInformation.each do |prop|
    instance_variable_set("@#{prop.to_s}",nil)
  end
end
profile_category() click to toggle source

Return the AMEE::Profile::Category object associated with self. If not set, instantiates via the AMEE platform and assigns to self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 513
def profile_category
  @profile_category||=AMEE::Profile::Category.get(connection, profile_category_path)
end
profile_category_path() click to toggle source

Returns a string representing the AMEE platform path to the profile category associated with <tt>self</self>

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 478
def profile_category_path
  "/profiles/#{profile_uid}#{path}"
end
profile_item() click to toggle source

Return the AMEE::Profile::Item object associated with self. If not set, instantiates via the AMEE platform and assigns to self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 453
def profile_item
  @profile_item||=AMEE::Profile::Item.get(connection, profile_item_path, get_options) unless profile_item_uid.blank?
end
profile_item_path() click to toggle source

Returns a string representing the AMEE platform path to the profile item associated with <tt>self</self>

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 485
def profile_item_path
  "#{profile_category_path}/#{profile_item_uid}"
end
profile_options() click to toggle source

Returns a Hash representation of Profile term attributes appropriate for submitting to the AMEE platform via the AMEE rubygem.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 371
def profile_options
  result={}
  profiles.set.each do |piv|
    result[piv.path]=piv.value
    result["#{piv.path}Unit"]=piv.unit.label unless piv.unit.nil?
    result["#{piv.path}PerUnit"]=piv.per_unit.label unless piv.per_unit.nil?
  end
  if contents[:start_date] && !contents[:start_date].value.blank?
    result[:start_date] = contents[:start_date].value
  end
  if contents[:end_date] && !contents[:end_date].value.blank?
    result[:end_date] = contents[:end_date].value
  end
  return result
end
reset_invalidity_messages() click to toggle source

Empty the hash of error messages for term choices.

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 252
def reset_invalidity_messages
  @invalidity_messages={}
end
set_profile_item_values() click to toggle source

Update the associated profile item in the AMEE platform with the current Profile term values and attributes

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 460
def set_profile_item_values
  @profile_item = AMEE::Profile::Item.update(connection,profile_item_path, 
    profile_options.merge(:get_item=>true))
end
syncronize_with_amee() click to toggle source

Dispatch the calculation to AMEE. If necessary, delete an out of date AMEE profile item and create a new one. Fetch any values which are stored in the AMEE platform and not stored locally. Send any values stored locally and not stored in the AMEE platform. Fetch calculation results from the AMEE platform and update outputs assocaited with self

# File lib/amee-data-abstraction/ongoing_calculation.rb, line 326
def syncronize_with_amee
  new_memoize_pass
  find_profile
  load_profile_item_values
  begin
    load_drills
  rescue Exceptions::Syncronization
    delete_profile_item
    clear_outputs
  end
  load_metadata
  # We could create an unsatisfied PI, and just check drilled? here
  if satisfied?
    if profile_item
      set_profile_item_values
    else
      create_profile_item
    end
    load_outputs
  end
rescue AMEE::UnknownError
  # Tidy up, only if we created a "Bad" profile item.
  # Need to check this condition
  delete_profile_item
  raise DidNotCreateProfileItem
end