1

Adventures in Groovy – Part 25: Groovy Functions

Building on the previous post and in the spirit of reusing code, this will expand on the concept by walking through the creation and use of functions in Groovy calculations.  Functions have huge value in reducing your Groovy calculations and streamlining development. 

Functions allow access to processes that either do some action(s) or return a value to be used.  There are hundreds of cases I can think of where this is valuable.  Here are a few to get you started.

  1. Getting the form POV, or a dimension in the POV
  2. Creating and executing data maps (automatically using the form POV, for example)
  3. Identifying if the account should be converted to a different currency (headcount will never be converted)
  4. Logging repetative messages to the job console

Share your ideas with the community by posting a comment.  I am sure your thoughts will be valuable to the community!

These functions can be kept in a script that is reusable as discussed in Part 24.  Or, they can be created in individual Groovy calculations if they are unique to a specific calculation.

The Anatomy Of A Function

Functions can be dissected into three pieces.

  1. A name (required)
  2. The parameters (not required)
  3. The logic (required)

The construction is below.

def name(parameter1, parameter2, ...)
{
  logic
}

The name is simple.  This is a reference to the function and the string you use to call it.  I would suggest making it meaningful, but not terribly long.  Typing long names gets annoying after a while!  Also, come up with a consistent case strategy.  I normally capitalize the first letter in all but the first word (getSomethingGood).

The function can have parameters.  Unless it is just doing some generic action, it will need something.  It can be a numeric value, a string, a boolean value, or really any other type of object, like collections.  Parameters are separated by commas and can have default values.

The logic is what is inside the function and the whole purpose to have a function.  Functions can return values, execute some action, or both.

Function Examples

Getting the POV

A lot of functions need members from the POV.  Sometimes surrounding them with quotes causes problems in the PBCS functions so this gives an option to include or exclude them.  This also will return the member as a list for functions that require a list, like data maps.

The second two parameters are not required unless quotes or a list is needed as the return value.

def getPovMbr(dimension, boolean quotes = false, boolean returnAsList = false) {
  if(returnAsList == true)
    // tokenize will split the value based on a delimiter and convert the string to 
    // a collection.  Since this is one value and we just need the one value converted
    // to a collection, I used ### as the delimiter since it will never occur in any
    // dimension name.
    return dimension.tokenize('###')
  else if(quotes == true)
    return '"' + operation.grid.pov.find{it.dimName==dimension}.essbaseMbrName + '"'
  else
    return operation.grid.pov.find{it.dimName==dimension}.essbaseMbrName
}

Assuming the Plan is selected in the POV,

  • getPovMbr(“Scenario”) will return OEP_Plan
  • getPovMbr(“Scenario”,true) will return  “OEP_Plan”
  • getPovMbr(“Scenario”,false,true) will return [OEP_Plan]
Print To The Log

Monitoring processing times of actions inside a calculation is helpful to diagnose issues and manage performance.  This function will accept a message and return the duration from the previous execution of the function.

def startTime = currentTimeMillis()
def procTime = currentTimeMillis()
def elapsed=(currentTimeMillis()-startTime)/1000

def printPerformance(message, boolean printTotalTime = false)
{
  // Gets the elapsed time
  elapsed=(currentTimeMillis()-procTime)/1000
  // Sets the new start time
  procTime = currentTimeMillis()
  // Print to log
  println "****************************************************"
  println "$message = $elapsed secs"
  if (printTotalTime = true)
    println "Total Time = " + (currentTimeMillis()-startTime)/1000 + " secs"
  println "***************************************
}

printPerformance(“The Data Map processed”,true) would write the following message in the log.

****************************************************
The Data Map Processed = .204 secs
Total Time = 1.44 secs
****************************************************

This can obviously be customized to whatever you want to show.  As it is something used often, having a function saves a lot of time, reduces the non-business related code in the rule, and makes reading the business rule more digestible.

Rather than repeat all this,

time elapsed=(currentTimeMillis()-procTime)/1000
procTime = currentTimeMillis()
println “****************************************************”
println “$message = $elapsed secs”
println “Total Time = ” + (currentTimeMillis()-startTime)/1000 + ” secs”
println “***************************************

you simply call the function.

printPerformance(“The Data Map processed”,true)

Convert Account To USD

Groovy calculations don’t need to run Essbase calculations to execute business logic.  The logic can be executed in Groovy.  For a presentation in Orlando, I wanted to prove this.  I replicated a calculation that calculates revenue, gross profit, and margins.  It also needs to produce converted currencies.  As you know, accounts like units, headcount, and rates don’t get converted even if the local currency is something other than USD.  In the example I showed at KScope, a gridbuilder was used and every account needed to be identified as an account that would be converted, or not converted.  The following function returns a true/false for an account based on whether that account has a UDA of IgnoreCurrencyConversion.

boolean convertCurrency(def account)
{
  Dimension AccountDim = operation.application.getDimension("Account")
  Member AccountMbr = AccountDim.getMember(account)
  def memberProps = AccountMbr.toMap()
  if(memberProps['UDA'].toString().contains('IgnoreCurrencyConversion'))
    return false
  else 
    return true
}

convertCurrency(“Units”) would return a false
convertCurrency(“Revenue”) would return a true

In the code, as it is looping through the accounts, it applies the conversion only if it needs to.

if(convertCurrency(account)){
  sValuesUSD.add(currencyRates[cMonth].toString().toDouble() * setValue)
  addcellsUSD << currencyRates[cMonth].toString().toDouble() * setValue
}
else
{
  sValuesUSD.add(setValue)
  addcellsUSD << setValue
}

The full example of this is near the end of the ePBCS Gridbuilder Deep Dive – Last Minute KScope Souvenirs in my Kscope Wrap Up.

Conclusion

There are significant benefits to functions.  As your growth in this space grows, you will likely develop more of these function and reuse them.  Got an idea?  Share it by posting a comment!




Adventures in Groovy – Part 24: Don’t Be Stingy With Reusable Code

Now that you are knee deep in Groovy, help yourself out and reuse common code.  The more you learn, the more efficient you will be and will laugh at some of your initial attempts at your Groovy calculations.  If you are like me, you get excited about all the possibilities and learn as you go.  When you find better ways to do something, or even preferable ways, you end up with an application with inconsistent representations of the same logic.  You are too busy to go back and update all the snippets you have improved, so it stays in a somewhat messy state.

Do yourself a favor and start reusing some of the things you need in every script.  When you start doing more in Groovy calculations, the more you can replicate the better.  Making some of the code business rule agnostic will make you more productive and will create a consistent approach to calculation strategy.  An example of this would be variables for the dimensions in the POV.  Or, maybe a common map used to push data from WFP to the P&L.  Regardless of the use, as soon as you see things that will be duplicated, put them in a script.  Scripts can be embedded in Groovy calculations just like regular Business Rules.

Header Script

This is an example of something that I find useful for every Groovy calculation I am writing.  It accomplished a couple things.

  1. It stores the forecast months so data maps, smart pushes, Essbase calculations, and grid builder functions, can be dynamically execute on all months for a budget and the out months for the forecast based on the scenario selected.
  2. It gets the numeric value for the current month to be used in other localized calculations.
  3. It creates a calendar object to get the current time for logging performance.
  4. The parameters are logged to the job console.
/*RTPS: {RTP_Month}*/

def Month = rtps.RTP_Month.toString().replaceAll("\"", "")
def MonthFix = rtps.RTP_Month.toString()
def months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
def actualMonths = []
def forecastMonths = []

months.eachWithIndex {item, index ->
  if(index > months.findIndexOf{it == Month})
    forecastMonths <<  item 
  else
    actualMonths <<  item 
}

def actualMonthsFix = """\"${actualMonths.join('", "')}\""""
def forecastMonthsFix = """\"${forecastMonths.join('", "')}\""""
def useMonths = []

if(operation.grid.pov.find{it.dimName =='Scenario'}.essbaseMbrName.toString().toLowerCase().contains('forecast'))
  useMonths = forecastMonths
else
  useMonths = months

//Get time for logging perforance
Calendar calendar = Calendar.getInstance()
calendar.setTimeInMillis(currentTimeMillis())

// Get the numeric value for the month (Jan=0)
int monthNumber = Calendar.instance.with {
  time = new Date().parse( "MMM", Month )
  it[ MONTH ]
}

println "The current month number is $monthNumber"
println "The current month is $Month"
println "The current month (Fix) is $MonthFix"
println "The actual months (list) are ${actualMonths}"
println "The actual months (string) are ${actualMonthsFix}"
println "The forecast months (list) are ${forecastMonths}"
println "The forecast months (string) are ${forecastMonthsFix}"

Conversion Table

If the application moves data from more detailed plan types to a consolidated P&L, it likely has conversions.  This is an example of an account map from a Gross Profit plan type to a P&L plan type.  How it is used will be explained in a later submission, but it is used in every calculation that synchronizes data from the GP to the P&L cube.  Therefore, it is a great candidate to have in a shared library (a script) so it can be maintained in one place.

def acctMap = ['Cases':'Units',
               'Sales':'42001',
               'COGS':'50001',
               'Tax':'50015',
               'Chargeback':'56010',
               'Investment Gains':'50010',
               'Writeoffs':'56055',
               'Growth Attributed to New Products':'56300',
               'Samples':'56092'
                ]

Sharing Scripts

Just as scripts can be embedded in regular business rules, the exact same syntax is used in Groovy calculations.  Assume the header script above is called Common Groovy and sits in an application named FinPlan in the GP plan type.  The inclusion of the script into any  Groovy calculation would be done by inserting the following text.

%Script(name:=" Common Groovy ",application:="FinPlan",plantype:="GP")

Conclusion

These are just examples to get you thinking about how you can reduce the headaches down the road when your Groovy calculations need maintained.  It can easily be expanded to include common POV members and other common code.  Be mindful of how global this is as not all POV members are in all forms.  You might find it useful to have a script that is shared everywhere and a few others shared for like calculations or forms.  You aren’t limited to only including one shared script.

If you have a snippet you are using in all your Groovy calculations, share it with the community.  We always like to get feedback and learn from each other.




Adventures in Groovy – Part 23: Disney Style

KScope has concluded, and what a fantastic week it was.  I love the years I get the feedback that I have an abstract selected so I can attend.  This year, I was awarded Oracle Ace, so it was really nice to be nominated and recognized for my contributions to the community. 

I tried to record the sessions and unfortunately, only 1 worked.  All of my presentations were recorded through the event and will be released in the coming months. When they are, I will share the links.  Until them, I hope the visual presentations will inspire you to take a look at Groovy, if I haven’t already pushed you in that direction.

Now that my time has been spent building these decks is over, look forward to more Groovy Adventures!

Top Down and BottomS Up Planning at Breakthru Beverage Group

Why Groovy is Game Changing

ePBCS Gridbuilder Deep Dive – Last Minute KScope Souvenirs