# Copyright (C) 2011 AMEE UK Ltd. - http://www.amee.com
# Released as Open Source Software under the BSD 3-Clause license. See LICENSE.txt for details.
# :title: Class: AMEE::DataAbstraction::Calculation
module AMEE
module DataAbstraction
# Base class providing attributes and methods for representing a calculation
# which can be made using the AMEE platform. An instance of Calculation
# will typically be associated with a specific AMEE category.
#
# Instances of Calculation are represented by several primary attributes:
#
# label:: Symbol representing the unique, machine-readable name for the
# calculation
#
# name:: String representing a human-readable name for the calculation
#
# path:: String representing the AMEE platform path to the data category
# which is associated with self
#
# fixed_usage:: String representing the AMEE platform path for the usage to
# be used by self, if defined
#
# An instance of Calculation also holds an arbitrary number of objects
# of the class Term. These represent values associated with the
# calculation, e.g. inputs, outputs, metadatum, etc. These can be accessed
# using the #terms methods or the term subset methods provided by
# the TermsList class (e.g. #profiles, #visible)
#
# Two classes inherit the Calculation class:
# * PrototypeCalculation : provides a templating for a specific calculation
# type with defined, but blank, terms (i.e. inputs, outputs, etc.)
#
# * OngoingCalculation : represents a particular calculation - possibly
# incomplete - which can be updated, submitted for calculation, and saved
#
class Calculation
public
# Symbol representing the unique, machine-readable name for self.
# Set a value by passing an argument. Retrieve a value by calling without
# an argument, e.g.,
#
# my_calculation.label :fuel
#
# my_calculation.label #=> :fuel
#
attr_property :label
# String representing a human-readable name for self. Set a
# value by passing an argument. Retrieve a value by calling without an
# argument, e.g.,
#
# my_calculation.name 'Domestic fuel consumption'
#
# my_calculation.name #=> 'Domestic fuel consumption'
#
attr_property :name
# String representing the AMEE platform path to the data category which is
# associated with self. Set a value by passing an argument. Retrieve
# a value by calling without an argument, e.g.,
#
# my_calculation.path '/some/path/in/amee/'
#
# my_calculation.path #=> '/some/path/in/amee/'
#
attr_property :path
# String representing the AMEE platform path for the usage to be used by
# self, if defined. Set a value by passing an argument. Retrieve
# a value by calling without an argument, e.g.,
#
# my_calculation.fixed_usage 'byMass'
#
# my_calculation.fixed_usage #=> 'byMass'
#
attr_property :fixed_usage
# Calculations contain a list of "terms" of the base class Term,
# representing inputs, outputs, metadatum, etc. which are associated with
# self.
#
# Returns all associated terms as an instance of the TermsList class
#
def terms
TermsList.new(@contents.values)
end
# Retrieve the terms associated with self as a hash from labels to terms.
attr_accessor :contents
# Shorthand method for retrieving the term assocaited with self which has a
# label matching sym
#
def [](sym)
@contents[sym.to_sym]
end
# Syntactic sugar to enable the return of a subset of associated terms according
# to their type or status (e.g. drills, profiles, set, unset, visible). See
# TermsList::Selectors for valid variants
#
TermsList::Selectors.each do |sel|
delegate sel,:to=>:terms
end
# Prettyprint a string representation of self, together with associated terms
def inspect
elements = {:label => label.inspect, :terms => terms.map{|t| "<#{t.class.name.demodulize} label:#{t.label}, value:#{t.value.inspect}>"}}
attr_list = elements.map {|k,v| "#{k}: #{v}" } * ', '
"<#{self.class.name} #{attr_list}>"
end
def initialize_copy(source)
super
@contents=ActiveSupport::OrderedHash.new
source.contents.each do |k,v|
@contents[k]=v.clone
@contents[k].parent=self
end
end
# Return a string representing the AMEE Explorer URL which is assocaited
# with self
#
def discover_url
"http://discover.amee.com/categories#{path}"
end
def explorer_url
::Rails.logger.info "#explorer_url method deprecated. Use #discover_url" if defined? Rails
discover_url
end
protected
def initialize
@contents=ActiveSupport::OrderedHash.new
end
# Methods which will be memoized at application start, as they do not
# change over application instance lifetime
#
AmeeMemoised=[:amee_data_category, :amee_item_definition, :amee_ivds,
:amee_return_values, :amee_usages]
# Return all the values of the memoized quantities
def saved_amee
AmeeMemoised.map{|x|instance_variable_get("@#{x.to_s}")}
end
# Save the memoized quantities
def save_amee(values)
AmeeMemoised.zip(values).each do |prop,val|
instance_variable_set("@#{prop.to_s}",val)
end
end
private
# Return the global AMEE::Connection object. This is configured in
# /config/amee.yml
#
def connection
AMEE::DataAbstraction.connection
end
# Return the AMEE::Data::Category object associated with self
def amee_data_category
@amee_data_category||=AMEE::Data::Category.get(connection, "/data#{path}")
end
# Return the AMEE::Admin::ItemDefinition object associated with self
def amee_item_definition
@amee_item_definition||=amee_data_category.item_definition
end
# Return the AMEE::Admin::ReturnValueDefinitionList object associated
# with self. This represents each of the return value definitions which are
# associated with the calculation
#
def amee_return_values
@amee_return_values||=AMEE::Admin::ReturnValueDefinitionList.new(connection,amee_item_definition.uid)
end
# Return the instance of Term class associated with self and contains
# a path attribute matching path, e.g.
#
# my_calculation.by_path('distance') #=>
#
# my_calculation.by_path('type') #=>
#
def by_path(path)
terms.detect { |v| v.path==path }
end
# Return the instance of Drill class associated with self and contains
# a path attribute matching path, e.g.
#
# my_calculation.by_path('type') #=>
#
def drill_by_path(path)
drills.detect { |v| v.path==path }
end
public
# Return the AMEE::Admin::ItemValueDefinitionList object associated
# with self. This represents each of the item value definitions which are
# associated with the calculation
#
def amee_ivds
@amee_ivds||=amee_item_definition.item_value_definition_list.select{|x|x.versions.include?("2.0")}
end
# Returns a String representing the AMEE platform path for the usage currently
# used by self. If not usage is defined, returns nil
#
# my_calculation.current_usage #=> 'byMass'
#
def current_usage
usages.empty? ? fixed_usage : usages.first.value
end
# Returns an Array containing the AMEE platform paths for all valid usage
# available to self according to those defined under #item_definition. If
# no usage(s) is defined, returns nil, e.g.
#
# my_calculation.amee_usages #=> [ 'byMass', 'byEnergy' ]
#
def amee_usages
@amee_usages||=amee_item_definition.usages
end
end
end
end