# 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: Module: AMEE::DataAbstraction::CalculationCollectionAnalyticsSupport module AMEE module Analytics # Mixin module for the AMEE::DataAbstraction::CalculationCollection # class, providing methods for handling collections of calculations. # module CalculationCollectionAnalyticsSupport # Returns true if all calculations in self are # representatives of the same prototype calculation. Otherwise, # returns false. # def homogeneous? calculation_labels.size == 1 end # Returns true if all calculations in self are NOT # representatives of the same prototype calculation. Otherwise, # returns false. # def heterogeneous? !homogeneous? end # Returns an array containing all of the unique labels for calculations # held in self. # def calculation_labels map(&:label).uniq end def +(other_calc_coll) self.class.new(self.to_a + other_calc_coll.to_a) end def -(other_calc_coll) other_calc_coll = [other_calc_coll].flatten self.delete_if { |calc| other_calc_coll.include?(calc) } end # Similar to #sort_by! but returns a new instance of # CalculationCollection arranged according to the values on the # term labelled term. E.g. # # my_calculation_collection.sort_by :co2 # # #=> # def sort_by(term) term = term.to_sym unless term.is_a? Symbol AMEE::DataAbstraction::CalculationCollection.new(send(term).sort_by(:value).map(&:parent)) end # Sorts the calculation collection in place according to the values on the # term labelled term, returning self. # # my_calculation_collection.sort_by! :mass # # #=> # def sort_by!(term) replace(sort_by(term)) end # Returns a new instance of CalculationCollection containing the same # calculations contained within self but with the units of the term # term standardized on each. # # If no further arguments are provided, the standardized units represent # those which currently predominate amongst the relevent terms. Otherwise, # the specific unit and/or per unit which are required for each instance # of term can be explicitly specified as the second and third # arguments respecitively. Units can be specified in any of the formats # which are acceptable to the Quantify::Unit.for method (i.e. # stringified unit names or symbols, symbolized labels, or # Quantify::Unit::Base instances of the required unit). E.g. # # my_calculation_collection.standardize_units(:mass) # # #=> # # my_calculation_collection.standardize_units(:mass, :lb) # # #=> # # my_calculation_collection.standardize_units(:mass, 'pound') # # #=> # # my_calculation_collection.standardize_units(:mass, ) # # #=> # # my_calculation_collection.standardize_units(:distance, :km, 'year') # # #=> # def standardize_units(term,unit=nil,per_unit=nil) term = term.to_sym unless term.is_a? Symbol new_calcs = send(term).standardize_units(unit,per_unit).map do |term| calc = term.parent calc.contents[term.label] = term calc end AMEE::DataAbstraction::CalculationCollection.new(new_calcs) end # Similar to #standardize_units but standardizes units in place, # returning self # def standardize_units!(term,unit=nil,per_unit=nil) new_calcs = standardize_units(term,unit,per_unit) replace(new_calcs) end # Returns an array of instances of the Result class representing the # sums of all outputs represented within the collection # def sum_all_outputs AMEE::DataAbstraction::TermsList.new(terms.outputs.visible.labels.uniq.map { |o| send(o).sum }) end # Returns a new instance of the class TermsList representing either # CO2 or CO2e outputs from each calculation # def co2_or_co2e_outputs terms = AMEE::DataAbstraction::TermsList.new each do |calculation| if calculation['co2e'] terms << calculation['co2e'] elsif calculation.outputs.visible.labels.size == 1 && calculation.outputs.visible.labels.first == :co2 terms << calculation['co2'] end end return terms end # Call the #calculate! method on all calculations contained within # self. # def calculate_all! each { |calc| calc.calculate! } end # Call the #save method on all calculations contained within # self. # def save_all! each { |calc| calc.save } end # Returns a terms list of all terms held by calculations contained within # self. # def terms AMEE::DataAbstraction::TermsList.new( (self.map { |calc| calc.terms.map { |term| term } }).flatten ) end AMEE::DataAbstraction::TermsList::Selectors.each do |sel| delegate sel,:to=>:terms end # Return an instance of TermsList containing only terms labelled # :type. # # This method overrides the standard #type method (which is deprecated) and # mimics the functionality provied by the first #method_missing method in # dynamically retrieving a subset of terms according their labels. # def type terms.type end def respond_to?(method) if terms.labels.include? method.to_sym return true elsif method.to_s =~ /sort_by_(.*)!/ and terms.labels.include? $1.to_sym return true elsif method.to_s =~ /sort_by_(.*)/ and terms.labels.include? $1.to_sym return true else super end end # Syntactic sugar for several instance methods. # # --- # # Call a method on self which named after a specific term label # contained with an associated calculation and return an instance of the # TermsList class contain each of those terms. E.g., # # my_terms = my_calculation_collection.type #=> # my_terms.label #=> :type # # my_terms = my_calculation_collection.mass #=> # my_terms.label #=> :mass # # my_terms = my_calculation_collection.co2 #=> # my_terms.label #=> :co2 # # --- # # Call either the #sort_by or #sort_by! methods including # the argument term as part of the method name, e.g., # # my_calculation_collection.sort_by_co2 # # #=> # # my_calculation_collection.sort_by_mass! # # #=> # def method_missing(method, *args, &block) if terms.labels.include? method.to_sym terms.send(method.to_sym) elsif method.to_s =~ /sort_by_(.*)!/ and terms.labels.include? $1.to_sym sort_by! $1.to_sym elsif method.to_s =~ /sort_by_(.*)/ and terms.labels.include? $1.to_sym sort_by $1.to_sym else super end end end end end