Name

sn_ci_analytics.VAConditionBuilderTransformationUtils

Description

No description available

Script

var VAConditionBuilderTransformationUtils = Class.create();

VAConditionBuilderTransformationUtils.prototype = {
  initialize: function() {
      this.vaConditionBuilderMetadataUtils = new VAConditionBuilderMetadataUtils();
      this.vaConditionBuilderAPIUtils = new VAConditionBuilderAPIUtils();
      this.logger = CIAnalyticsLogger.getLogger("VAConditionBuilderTransformationUtils");
      this.choiceFields = ['is_choice', 'contains_choice', 'contains_parent_topic_choice', 'choice_single_select'];
  },

  /**
   * When saving conditions, instead of putting the entire comparisonModel JSON in records we
   * store only the minimum necessary information using which we can rebuild the comparisonModel 
   * later while reloading.
   */
  getMinifiedComparisonModel: function(comparisonModel) {
      var minifiedComparisonModel = [];
      var self = this;
      comparisonModel.forEach(function(comparison) {
          var fieldType = comparison.field.type;
          var conditionValue = comparison.conditionValue.internal;
          if (self.isJson(conditionValue) && self.isChoiceType(fieldType)) {
              conditionValue = JSON.parse(conditionValue);
          }
          minifiedComparisonModel.push({
              comparisonType: comparison.comparisonType,
              field: comparison.field.internal,
              operator: comparison.operator.internal,
              conditionValue: conditionValue,
              tableName: comparison.tableName,
              hideAndButton: comparison.hideAndButton,
              hideOrButton: comparison.hideOrButton,
              children: self.getMinifiedComparisonModel(comparison.children)
          });
      });
      return minifiedComparisonModel;
  },

  /**
   * @param {string} minifiedComparisonModel - Minified comparisonModel with only the necessary information
   * @param {string} tableName - Primary table to which the query belongs
   * @returns {Array} Returns a comparisonModel with all the required information and metadata that is required by
   * now-condition-builder to render filters.
   */
  getFullScaleComparisonModel: function(minifiedComparisonModel, tableName) {
      return this._getFullScaleComparisonModel(minifiedComparisonModel, null, tableName, tableName, null);
  },

  _getFullScaleComparisonModel: function(minifiedComparisonModel, parentComparisonModel, tableName, baseTableName, parentComparison) {
      var comparisonModel = [];
      var choiceValues = {};
      var self = this;
      minifiedComparisonModel.forEach(function(minifiedComparison) {
          var fieldInternalValue = minifiedComparison.field;
          var comparisonId = self.guid();
          minifiedComparison.id = comparisonId;
          var computedTableName = minifiedComparison.tableName || tableName || self._buildTableName(baseTableName, minifiedComparison, parentComparison); //to support the case where minified model has tableName
          var fieldObject = self.getFieldObject(fieldInternalValue, computedTableName, comparisonId, parentComparisonModel);
          var pathLength = fieldObject.pathData.path.length;
          var leafTableName = fieldObject.pathData.path[pathLength - 1].table;
          if (self.isChoiceType(fieldObject.type) && !choiceValues[fieldInternalValue]) {
              choiceValues[fieldInternalValue] = self.getPrePopulatedChoiceValues(comparisonId, fieldInternalValue, leafTableName, parentComparisonModel);
          }

          var conditionValue = self.getConditionValueObject(minifiedComparison.conditionValue, fieldObject.type, choiceValues[fieldInternalValue], fieldInternalValue, leafTableName);
          conditionValue['meta']['canShowCustomOptions'] = conditionValue['meta']['canShowCustomOptions'] && minifiedComparison.children.length === 0 ? true : false;

          comparisonModel.push({
              id: comparisonId,
              comparisonType: minifiedComparison.comparisonType,
              field: fieldObject,
              operator: self.getOperatorObject(minifiedComparison.operator, fieldObject.type),
              conditionValue: conditionValue,
              tableName: computedTableName,
              hideAndButton: minifiedComparison.hideAndButton,
              hideOrButton: minifiedComparison.hideOrButton,
              children: self._getFullScaleComparisonModel(minifiedComparison.children, minifiedComparisonModel, null, baseTableName, minifiedComparison)
          });
      });
      return comparisonModel;
  },

  getFieldObject: function(fieldInternalValue, bucketName, comparisonId, parentComparisonModel) {
      /**
       * Field internal value can be in a dotwalked format in which case it looks like:
       * Topics.SETUP_TOPIC
       */
      var fields = fieldInternalValue.split('.');
      var fieldDisplayValue = '',
          path = [];
      for (var i in fields) {
          var fieldType;
          var tableName = i == 0 ? bucketName : path[i - 1].referenceDataKey;
          var fieldMetadata = this.vaConditionBuilderMetadataUtils.getFieldMetadata(fields[i], tableName);
          var displayValue = fieldMetadata ? fieldMetadata['displayValue'] : fields[i];
          // Example: "Topics > TOPIC_BLOCKS". Display of Dot walked fields are separated by '>'.
          fieldDisplayValue += i == 0 ? displayValue : (' > ' + displayValue);

          // fieldMetadataRecord will exist only for static fields like Channel and Favorite.
          // For dynamic fields like Event Properties or Topic SubCat there wont be any field record.
          if (fieldMetadata) {
              fieldType = fieldMetadata['type'];
              var referenceTable = fieldMetadata['referenceDataKey'];
              path.push(this.getFieldPathObject(fields[i], displayValue, fieldType, tableName, referenceTable));
          } else {
              // By default marking root level dynamic fields as type string, eg. Event props
              // TODO: Should we rather save type in minifiedComparisonModel ? Consult!
              fieldType = i == 0 ? 'string' : path[i - 1].type;
              path.push(this.getFieldPathObject(fields[i], fields[i], fieldType, tableName));
          }
      }

      return {
          internal: fieldInternalValue,
          display: fieldDisplayValue,
          type: path.length ? path[path.length - 1].type : '',
          pathData: {
              dotwalkMode: 'TABLE_FIELDS',
              path: path
          }
      };
  },

  getOperatorObject: function(operatorInternalValue, fieldType) {
      return {
          internal: operatorInternalValue,
          display: this.vaConditionBuilderMetadataUtils.getOperatorDisplayValue(operatorInternalValue, fieldType),
          meta: {}
      };
  },

  getConditionValueObject: function(conditionInternalValue, fieldType, choiceValues, fieldInternalValue, tableName) {
      var isChoiceType = this.isChoiceType(fieldType);
      if (isChoiceType) {
          var displayValue = '';
          for (var i in conditionInternalValue) {
              for (var j in choiceValues) {
                  if (conditionInternalValue[i] === choiceValues[j].id) {
                      // Adding comma separated display values.
                      displayValue += (i == 0) ? choiceValues[j].label : ', ' + choiceValues[j].label;
                      break;
                  }
              }
          }
          var conditionMeta = {
              customOptions: this.getCustomFilterMeta(fieldInternalValue, tableName),
              canShowCustomOptions: conditionInternalValue.length === 1
          };
          if (tableName === 'topic_filters' && fieldInternalValue === 'Topic Nodes') {
              // Cannot show custom options if nodeType is not choice/boolean
              try {
                  var parsedInternalValue = JSON.parse(conditionInternalValue[0]);
                  conditionMeta.canShowCustomOptions = conditionInternalValue.length === 1 && parsedInternalValue.nodeType ? true : false;
              } catch (err) {
                  var errorMessage = 'Parsing failed during condition load: ' + err.message;
                  this.logger.error(errorMessage);
              }
          }

          return {
              internal: JSON.stringify(conditionInternalValue),
              display: displayValue,
              displayParts: choiceValues,
              meta: conditionMeta
          };
      }
      if ((fieldType == 'glide_date_time' || fieldType == 'glide_duration') && conditionInternalValue) {
          var vaUtils = new sn_ci_analytics.VADashboardUtils();
          var gdt = new GlideDateTime(conditionInternalValue);
          var displayVal = vaUtils.getFormattedTime(gdt.getNumericValue());
          return {
              internal: conditionInternalValue,
              display: displayVal,
              meta: {}
          };
      }
  	if (fieldType == 'boolean' || fieldType == 'boolean_is_any') {
  		return {
  			internal: conditionInternalValue,
  			display: conditionInternalValue,
  			displayParts: [conditionInternalValue],
  			meta: {}
  		};
  	}
      return {
          internal: conditionInternalValue,
          display: '',
          meta: {}
      };
  },

  getFieldPathObject: function(fieldInternalValue, fieldDisplayValue, fieldType, fieldBucketName, fieldReferenceTable) {
      return {
          fieldName: fieldInternalValue,
          fieldLabel: fieldDisplayValue,
          type: fieldType,
          table: fieldBucketName,
          referenceDataKey: fieldReferenceTable
      };
  },

  getCustomFilterMeta: function(field, tableName) {
      var getDefaultFilterMeta = function(linkName) {
          return [{
              "display": linkName,
              "type": "link",
              "action": "SN_VA_CONDITION_ROW#FILTER_BY_PROPS"
          }];
      };
      switch (tableName) {
          case 'va_funnel':
              {
                  switch (field) {
                      case "Event Trigger":
                      case "Topic":
                          return getDefaultFilterMeta("Filter properties");
                      default:
                          return [];
                  }
              }
          case 'va_conversations':
              {
                  switch (field) {
                      case "Events":
                          return getDefaultFilterMeta("Filter properties");
                      case "Topics":
                          return getDefaultFilterMeta("Add properties");
                      default:
                          return [];
                  }
              }

          case 'topic_sub_categories':
              {
                  return getDefaultFilterMeta("Add properties");
              }

          case 'topic_filters':
              {
                  switch (field) {
                      case 'Topic Nodes':
                          return getDefaultFilterMeta("Refine properties");
                  }
              }
      }
  },

  getAppseeRequestPayload: function(comparisonId, fieldInternalValue, tableName, comparisonModel) {
      switch (tableName) {
          case 'va_conversations':
          case 'va_users':
          case 'va_funnel':
              {
                  switch (fieldInternalValue) {
                      case 'End State':
                      case 'Feedback Result':
                      case 'Provider Name':
                          {
                              return {
                                  dynamic_property_name: fieldInternalValue
                              };
                          }

                      case 'Channel':
                      case 'Channels':
                          {
                              return {
                                  dynamic_property_name: 'Channel Name'
                              };
                          }

                      case 'Events':
                          {
                              return {
                                  event_type_filter: 'Custom'
                              };
                          }

                      case 'Topic Categories':
                          {
                              return {
                                  event_type_filter: 'Topic Category'
                              };
                          }

                      case 'Topic Blocks':
                          {
                              return {
                                  event_property_name: 'Parent Topic Name',
                                  event_type: 'Topic',
                                  valueTransformer: function(data) {
                                      return Object.keys(data);
                                  }
                              };
                          }

                      case 'Language':
                          {
                              return {
                                  language: true
                              };
                          }
                          // Funnel specific fields
                      case 'Event Trigger':
                          {
                              return {
                                  event_type_filter: 'Custom'
                              };
                          }
                      case 'Topic':
                          {
                              return {
                                  screen_type: '3',
                                  platform: '3',
                                  valueTransformer: function(data) {
                                      return data.map(function(topic) {
                                          return {
                                              id: topic.Name,
                                              label: topic.DisplayName
                                          };
                                      });
                                  }
                              };
                          }


                      case 'Topics':
                          {
                              return {
                                  platform: '3',
                                  screen_type: '3',
                                  valueTransformer: function(data) {
                                      return data.map(function(topic) {
                                          return ({
                                              id: topic.DisplayName,
                                              label: topic.DisplayName
                                          });
                                      });
                                  }
                              };
                          }
                  }
                  break;
              }

          case 'topic_sub_categories':
              {
                  return {
                      event_property_name: 'Sub Category',
                      event_type: 'Topic',
                      tableName: tableName,
                      fieldInternalValue: fieldInternalValue.split('.')[1]
                  };
              }

          case 'topic_nodes':
              {
                  return {
                      screen_type: '4',
                      platform: '3'
                  };
              }

          case 'topic_filters':
              {
                  var parentRow = this.getParentComparison(comparisonId, comparisonModel);
                  var topicName = parentRow.conditionValue[0];
                  return {
                      screen_type: '4',
                      platform: '3',
                      topicName: topicName
                  };
              }
          case 'topic_filters_funnel':
              {
                  var parentComparison = this.getParentComparison(comparisonId, comparisonModel);
                  var comparison = this._getComparison(comparisonId, comparisonModel);
                  var screenName = JSON.parse(comparison.conditionValue[0]).screenName;
                  var topicId = screenName.split("/")[0];

                  var payload = {
                      screen_type: '4',
                      platform: '3',
                  };
                  var topicName = parentComparison.conditionValue[0];
                  if (topicName === topicId) { // Topic as screen
                      payload['screenId'] = topicName;
                  } else { // Topic as event
                      payload['topicName'] = topicName;
                  }

                  return payload;
              }

          case 'topic_node_filter':
              {
                  var topicNodeComparisonRow = this.getParentComparison(comparisonId, comparisonModel);
                  var topicNodeId = JSON.parse(topicNodeComparisonRow.conditionValue[0]).screenName.split('/')[1];
                  return {
                      event_property_name: 'Selected Value',
                      event_type: 'Topic Node',
                      tableName: tableName,
                      fieldInternalValue: fieldInternalValue.split('.')[1],
                      nodeType: 'choice',
                      topicNodeId: topicNodeId
                  };
              }
      }
  },

  getPrePopulatedChoiceValues: function(comparisonId, fieldInternalValue, tableName, comparisonModel) {
      var requestPayload = this.getAppseeRequestPayload(comparisonId, fieldInternalValue, tableName, comparisonModel);
      var choices = requestPayload ? this.vaConditionBuilderAPIUtils.getAppseeAPIResponse(requestPayload) : this.conditionBuilderStaticChoiceValueFields[fieldInternalValue];
      if (requestPayload && requestPayload.valueTransformer) {
          choices = requestPayload.valueTransformer(choices);
      }
      var choiceList = [];
      if (Array.isArray(choices)) {
          if (typeof choices[0] === 'string') {
              choiceList = choices.map(function(choice) {
                  return {
                      id: choice,
                      label: choice
                  };
              });
          } else {
              choiceList = choices;
          }
      } else {
          for (var key in choices) {
              choiceList.push({
                  id: key,
                  label: choices[key]
              });
          }
      }
      return choiceList;
  },

  isJson: function(str) {
      try {
          JSON.parse(str);
      } catch (e) {
          return false;
      }
      return true;
  },

  getParentComparison: function(comparisonId, comparisonModel) {
      for (var i in comparisonModel) {
          if (comparisonModel[i].children.length) {
              var children = comparisonModel[i].children;
              for (var j in children) {
                  if (children[j].id === comparisonId) {
                      return comparisonModel[i];
                  }
              }

              var searchParentInChildArray = this.getParentComparison(comparisonId, comparisonModel[i].children);
              if (searchParentInChildArray) {
                  return searchParentInChildArray;
              }
          }
      }
  },

  _getComparison: function(comparisonId, comparisonModel) {
      for (var i in comparisonModel) {
          if (comparisonModel[i].id === comparisonId)
              return comparisonModel[i];

          if (comparisonModel[i].children.length) {
              var match = this._getComparison(comparisonId, comparisonModel[i].children);
  		if(match) return match;
          }
      }
  },

  isChoiceType: function(type) {
      return this.choiceFields.indexOf(type) > -1;
  },

  conditionBuilderStaticChoiceValueFields: {
      'Setup Topic Types': [
          'Anything else',
          'Live agent',
          'Error',
          'Greeting',
          'Closing',
          'Survey',
          'AI Search fallback',
          'Fallback',
          'Explore help'
      ],
      'Type': [
          'VA Only',
          'LA Only',
          'VA to LA',
          'Never Engaged'
      ]
  },

  guid: function() {
      var x = 2147483648;
      return Math.floor(Math.random() * x).toString(36) + Math.abs(Math.floor(Math.random() * x) ^ Date.now()).toString(36);
  },

  _getComparisonModelForTable: function(table, minifiedComparison, parentComparison) {
      if (!table || !parentComparison || !parentComparison.field) return minifiedComparison;
      var internalTableMeta = this.vaConditionBuilderMetadataUtils.getInternalTableMapMetaData();
      if (internalTableMeta && internalTableMeta[table] && internalTableMeta[table].indexOf(parentComparison.field) >= 0) return parentComparison;
      return minifiedComparison;
  },
  _buildTableName: function(table, minifiedComparison, parentComparison) {
      try {
          var tableInternalMap = this.vaConditionBuilderMetadataUtils.getInternalTableMap();
          var comparison = this._getComparisonModelForTable(table, minifiedComparison, parentComparison);
          if (!tableInternalMap[table] || !tableInternalMap[table][comparison.field]) throw "default";
          var tableNameExpression = tableInternalMap[table][comparison.field];
          var regexs = tableNameExpression.match("%(.*)%");
          if (regexs) {
              var modelKey = regexs[1];
              if (!comparison[modelKey]) throw "error";
              return tableNameExpression.replace(regexs[0], comparison[modelKey]);
          }
          return tableNameExpression;
      } catch (e) {
          return table;
      }
  },

  type: 'VAConditionBuilderTransformationUtils'
};

Sys ID

99ec8b4e533320109f84ddeeff7b1244

Offical Documentation

Official Docs: