# 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 <i>Calculation</i> # will typically be associated with a specific AMEE category. # # Instances of <i>Calculation</i> 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 <tt>self</tt> # # fixed_usage:: String representing the AMEE platform path for the usage to # be used by <tt>self</tt>, if defined # # An instance of <i>Calculation</i> also holds an arbitrary number of objects # of the class <i>Term</i>. These represent values associated with the # calculation, e.g. inputs, outputs, metadatum, etc. These can be accessed # using the <tt>#terms</tt> methods or the term subset methods provided by # the <tt>TermsList</tt> class (e.g. <tt>#profiles</tt>, <tt>#visible</tt>) # # Two classes inherit the <i>Calculation</i> class: # * <i>PrototypeCalculation</i> : provides a templating for a specific calculation # type with defined, but blank, terms (i.e. inputs, outputs, etc.) # # * <i>OngoingCalculation</i> : 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 <tt>self</tt>. # 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 <tt>self</tt>. 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 <tt>self</tt>. 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 # <tt>self</tt>, 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 <i>Term</i>, # representing inputs, outputs, metadatum, etc. which are associated with # <tt>self</tt>. # # Returns all associated terms as an instance of the <i>TermsList</i> class # def terms TermsList.new(@contents.values) end # Retrieve the terms associated with <tt>self</tt> as a hash from labels to terms. attr_accessor :contents # Shorthand method for retrieving the term assocaited with <tt>self</tt> which has a # label matching <tt>sym</tt> # 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 # <i>TermsList::Selectors</i> for valid variants # TermsList::Selectors.each do |sel| delegate sel,:to=>:terms end # Prettyprint a string representation of <tt>self</tt>, 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 <tt>self</tt> # 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) && ::Rails.logger.present? 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 <i>AMEE::Connection</i> object. This is configured in # /config/amee.yml # def connection AMEE::DataAbstraction.connection end # Return the <i>AMEE::Data::Category</i> object associated with <tt>self</tt> def amee_data_category @amee_data_category||=AMEE::Data::Category.get(connection, "/data#{path}") end # Return the <i>AMEE::Admin::ItemDefinition</i> object associated with <tt>self</tt> def amee_item_definition @amee_item_definition||=amee_data_category.item_definition end # Return the <i>AMEE::Admin::ReturnValueDefinitionList</i> object associated # with <tt>self</tt>. 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 <i>Term</i> class associated with <tt>self</tt> and contains # a path attribute matching <tt>path</tt>, e.g. # # my_calculation.by_path('distance') #=> <AMEE::DataAbstraction::Profile ... > # # my_calculation.by_path('type') #=> <AMEE::DataAbstraction::Drill ... > # def by_path(path) terms.detect { |v| v.path==path } end # Return the instance of <i>Drill</i> class associated with <tt>self</tt> and contains # a path attribute matching <tt>path</tt>, e.g. # # my_calculation.by_path('type') #=> <AMEE::DataAbstraction::Drill ... > # def drill_by_path(path) drills.detect { |v| v.path==path } end public # Return the <i>AMEE::Admin::ItemValueDefinitionList</i> object associated # with <tt>self</tt>. 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 <tt>self</tt>. 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 <tt>self</tt> 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