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
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.
String representing the AMEE
platform profile item UID assocaited with self
String representing the AMEE
platform profile UID assocaited with self
Public Class Methods
Construct an Ongoing Calculation
. Should be called only via PrototypeCalculation#begin_calculation
. Not intended for external use.
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
# 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
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
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
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
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
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
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
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
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
# File lib/amee-data-abstraction/ongoing_calculation.rb, line 239 def clear_outputs outputs.each {|output| output.value nil } end
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
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
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
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
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
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 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
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
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
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 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
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
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
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 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
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
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
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
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
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
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
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
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
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
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
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