Contribution

Created by Vijendra Sawant, Modified on Fri, 10 Nov, 2023 at 4:13 PM by Vijendra Sawant


/*
Calculation Script name - WhizCalContribution
Description - Contribution is the portion of the total value that belongs to any dimension.
               TRx contribution for product P1 =(TRx for product P1 / Total TRx)*100

Product Version - v66
Script Version - 1.0

***Disclaimer - This is proprietary code of Whiz.AI. If you want to build on top of this computation script please make a copy/clone of this script and then alter the script which is copied or cloned. When version upgrade happens there are chances that this script may get modified hence any customization done by customer/partner developer may be overwritten.

Key Functions - 1.Function name : compute
                 purpose : perform the computation without creating join on dataSource
                 input parameters : metaDataQuery
                 output parameters : dataFrame
                
                2.Function name : computeWithJoins
                 purpose : perform the computation by creating join on dataSource
                 input parameters : metaDataQuery
                 output parameters : dataFrame
                                
                           
Key variables - query : implicit variable
                outputMetricName : implicit variable
                WhizCalPaginationLib : The name of the object present in WhizCalPaginationLib.js(library script).
                                         execution scripts can call WhizCalPaginationLib scripts functions using given object.
                dataAccessManager: This class is exposed to scripts by Framework through implicit variable dataAccessManager.
                                   this acts as gateway to data exposed by the framework. A query can be fired through this instance to get the data.             
              
*/
//**Start of Function code


(() => {
    log.info("******************** WhizCalContribution **********************");

    function compute(query) {

        //calling the function from library script 
        const infoObj = WhizCalPaginationLib.modifyQueryAndGetInfo(query);

        // get list of dimensions from entity-configuration
        //dimensions and dim filters are set by application service
        const exceptDims = addonProps?.["defaultFilter"]?.["dynamicArgs"]?.["dimensions"];

        const dimsToRemove = new Set(exceptDims);

        //names of dimensions configured.
        const dimNames = query.dimNames();
        dimNames.forEach(item => dimsToRemove.add(item));

        //copy()- creates deep copy from existing query
        //name(String name)- name for metadata query, this helps logging and identification of query
        const denomQuery = query.copy().name("WhizCalContribution_DenominatorQuery");

        //removes all contents referring to each column present in the array from all places applicable 
        denomQuery.removeColumns(Array.from(dimsToRemove));

        //copy(String copyPrefix, MetadataQuery query)- creates deep copy of this aggregation,name of each aggregation is set as (copyPrefix+current name)
        const totalAggr = primaryAggr.copy("totalAggr", denomQuery);

        //clears the aggregations set on this query
        denomQuery.clearAggrs();

        //adds given aggregation/post-aggregation to the query  
        denomQuery.aggr(totalAggr);

        const joinOnCols = [];
        if (dimNames.length > 1) {
            //adds given dimension into the query
            denomQuery.dim(dimNames[1]);

            joinOnCols.push(dimNames[1]);
        }

        //name(String name)- name for metadata query, this helps logging and identification of query
        query.name("WhizCalContribution_NumeratorQuery");

        //fires the MetadataQuery passed, returns DataFrame representing the response of the query
        const numeratorDf = dataAccessManager.fireQuery(query);

        //replaceOrderingColumn(String currentOrderingColum,String replacementColum)-Replaces any ordering clauses in the query with given column with ordering on new column
        denomQuery.replaceOrderingColumn(primaryAggr.column(), totalAggr.name());

        //fires the MetadataQuery passed, returns DataFrame representing the response of the query
        const denominatorDf = dataAccessManager.fireQuery(denomQuery);

        //granularity()- returns effective granularity. So even when not set, returns granularity of type 'all'
        //type()- returns type of granularity like 'all', 'period', 'duration'.
        if (Granularity.TYPE_ALL == (denomQuery.granularity().type())) {

            //removes the given column from the DataFrame
            denominatorDf.removeColumn(TIMESTAMP_RESPONSE_COLUMN);
        } else if (joinOnCols.length > 0) {
            joinOnCols.push(TIMESTAMP_RESPONSE_COLUMN);
        }

        let finalDf;
        if (joinOnCols.length > 0) {
            //innerJoin(DataFrame otherDF, List<String> joinColumns, Map<String, Object> defaults) - creates response DF with inner join of this DF with other DF being passed
            finalDf = numeratorDf.innerJoin(denominatorDf, joinOnCols, {});
        } else {

            //innerJoin(DataFrame otherDF)- creates response DF with inner join of this DF with other DF being passed. All common columns are assumed as join columns 
            finalDf = numeratorDf.innerJoin(denominatorDf);
        }

        /* percent(String aggrName, Object value, Object outOf, double divByZeroResponse)- returns the Aggregation object, whose arithmetically result is 100*(value/base)
            aggrName: Name of the post aggregation for output results
            value: The value whose percentage is to be calculated against total (outOf)
            outOf: Total part of the percentage out of which 'value' is specified
            divByZeroResponse: The value that should be returned back when base value (outOf) is zero
        */
        const aggr = Aggregation.percent(outputMetricName, primaryAggr.name(), totalAggr.name(), 0.0);

        //adds column with given aggregation, with name same as name of aggregation passed. The aggregation passed defines how would value of column to be added is derived
        finalDf.addColumn(aggr);

        //calling the function from library script 
        return WhizCalPaginationLib.getFinalResponse(finalDf, infoObj);
    }

    function computeWithJoins(query) {
        //replaceOrderingColumn(String currentOrderingColum,String replacementColum)-Replaces any ordering clauses in the query with given column with ordering on new column
        query.replaceOrderingColumn(primaryAggr.column(), outputMetricName);

        //copy()- creates deep copy from existing query
        //name(String name)- name for metadata query, this helps logging and identification of query
        const numQuery = query.copy().name("WhizCalContribution_NumeratorQuery");

        //returns offset set in metadataQuery
        const offset = numQuery.offset();

        //returns limit set in metadataQuery
        const limit = numQuery.limit();

        //returns shallow Copy of sort orders configured
        const order = numQuery.orderBy();

        //limit(Integer limit)- sets limit on number of records that would be sent back by query
        //offset(Integer offset)- the number of first records with specified offset would be skipped
        //clearOrderBy()- clears the order by clauses set on this query
        numQuery.limit(null).offset(null).clearOrderBy();

        //copy()- creates deep copy from existing query
        //name(String name)- name for metadata query, this helps logging and identification of query
        const denomQuery = numQuery.copy().name("WhizCalContribution_DenominatorQuery");

        // get list of dimensions from entity-configuration
        const exceptDims = addonProps?.["defaultFilter"]?.["dynamicArgs"]?.["dimensions"];

        const dimsToRemove = new Set(exceptDims);

        //names of dimensions configured.
        const dimNames = query.dimNames();
        dimNames.forEach(item => dimsToRemove.add(item));

        //removes all contents referring to each column present in the array from all places applicable 
        denomQuery.removeColumns(Array.from(dimsToRemove));

        //copy(String copyPrefix, MetadataQuery query)- creates deep copy of this aggregation,name of each aggregation is set as (copyPrefix+current name)
        const totalAggr = primaryAggr.copy("totalAggr", denomQuery);

        //clears the aggregations set on this query
        denomQuery.clearAggrs();

        //adds given aggregation/post-aggregation to the query  
        denomQuery.aggr(totalAggr);

        const joinOnCols = [];
        if (dimNames.length > 1) {

            //adds given dimension into the query
            denomQuery.dim(dimNames[1]);
            joinOnCols.push(dimNames[1]);
        }

        //granularity()- returns effective granularity. So even when not set, returns granularity of type 'all'
        //type()- returns type of granularity like 'all', 'period', 'duration'.
        if (Granularity.TYPE_ALL !== (denomQuery.granularity().type())) {
            joinOnCols.push(TIMESTAMP_RESPONSE_COLUMN);
        }

        const numDataSource = DataSource.innerQuery("numrDS", numQuery);
        const denDataSource = DataSource.innerQuery("denDS", denomQuery);

        let selectColumns = [];
        let aggregations = [];
    //adds given aggregation/post-aggregation to the query  
        denomQuery.aggr(totalAggr);
        const numColumn = numDataSource.column(primaryAggr.name());
        const denColumn = denDataSource.column(totalAggr.name());
        selectColumns = [numColumn, denColumn];
        aggregations.push(Aggregation.percent(outputMetricName, numColumn, denColumn, 0));

        const joinOnColsMap = new Map();
        joinOnCols.forEach(col => joinOnColsMap.set(col, col));

        //creates a query with specified details
        const joinQuery = MetadataQuery.create()
            //Sets the model on which this query is to be run, model + dataSource combination decides the DB Table or Druid data source to be used to run the query
            .model(numQuery.model())
            //name for metadata query, this helps logging and identification of query
            .name("WhizCalContribution_JoinQuery")
            //columns to be fetch from database
            .select(numQuery.dimNames())
            .select(selectColumns.concat(aggregations))
            //from(DBDataSource ds) -returns reference name of primary data source associated with this query
            .from(numDataSource)
            //leftJoinOn(DBDataSource dataSource, Map<String, String> joinColumnsLeftToRightMap)- create left join on datasource with given map of column    
            .leftJoinOn(denDataSource, joinOnColsMap)

            //offset(Integer offset)- the number of first records with specified offset would be skipped
            .offset(offset)

            //limit(Integer limit)- sets limit on number of records that would be sent back by query
            .limit(limit)

            //orderBy(List sortData)- convenience method calling orderBy(String) or orderBy(SortData) depending on type of each element  
            .orderBy(order)

            //granularity(Granularity granularity)- sets given granularity into the query    
            .addonProperties(query.addonProperties());

        //fires the MetadataQuery passed, returns DataFrame representing the response of the query
        return dataAccessManager.fireQuery(joinQuery);
    }

    // get value of apply_join_query variable from entity-configuration
    const applyJoin = addonProps?.["defaultFilter"]?.["dynamicArgs"]?.["apply_join_query"];

    if (applyJoin) {
        return computeWithJoins(query);
    } else {
        return compute(query)
    }

})();


/*
 ** configuration to be set in Computation metric **

default_filter:In this section's dynamicArgs sub-section, we have added,
               "apply_join_query" - flag to switch between two calculation approaches
        true - if join query needs to enable
        
**The configuration below should be copied and pasted to calculate the contribution.*
*/

"default_filter": {
      "dynamicArgs": {
        "apply_join_query": true
      }
    }

Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article