Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
prerna.sablecc2.PixelUtility Maven / Gradle / Ivy
package prerna.sablecc2;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PushbackReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import prerna.algorithm.api.ITableDataFrame;
import prerna.auth.User;
import prerna.om.ColorByValueRule;
import prerna.om.Insight;
import prerna.om.InsightPanel;
import prerna.om.InsightSheet;
import prerna.om.Pixel;
import prerna.om.PixelList;
import prerna.query.parsers.ParamStruct;
import prerna.query.parsers.ParamStructDetails;
import prerna.query.parsers.ParamStructDetails.QUOTE;
import prerna.query.parsers.ParamStructToJsonGenerator;
import prerna.query.querystruct.AbstractQueryStruct;
import prerna.query.querystruct.SelectQueryStruct;
import prerna.query.querystruct.filters.GenRowFilters;
import prerna.query.querystruct.filters.IQueryFilter;
import prerna.query.querystruct.transform.QsToPixelConverter;
import prerna.reactor.export.IFormatter;
import prerna.reactor.insights.SetInsightConfigReactor;
import prerna.sablecc2.analysis.DepthFirstAdapter;
import prerna.sablecc2.lexer.Lexer;
import prerna.sablecc2.lexer.LexerException;
import prerna.sablecc2.node.Start;
import prerna.sablecc2.om.PixelDataType;
import prerna.sablecc2.om.PixelOperationType;
import prerna.sablecc2.om.VarStore;
import prerna.sablecc2.om.execptions.SemossPixelException;
import prerna.sablecc2.om.nounmeta.NounMetadata;
import prerna.sablecc2.om.task.options.TaskOptions;
import prerna.sablecc2.parser.Parser;
import prerna.sablecc2.parser.ParserException;
import prerna.sablecc2.pipeline.PipelineTranslation;
import prerna.sablecc2.translations.DatasourceTranslation;
import prerna.sablecc2.translations.ParamStructSaveRecipeTranslation;
import prerna.sablecc2.translations.ParameterizeSaveRecipeTranslation;
import prerna.sablecc2.translations.ReplaceDatasourceTranslation;
import prerna.util.Constants;
import prerna.util.insight.InsightUtility;
public class PixelUtility {
private static final Logger logger = LogManager.getLogger(PixelUtility.class);
private static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
/**
*
* @param pixelExpression
* @return
*
* Returns true if the pixel is a valid pixel that can be executed
* Returns false otherwise
* @throws IOException
* @throws LexerException
* @throws ParserException
*/
public static Set validatePixel(String pixelExpression) throws ParserException, LexerException, IOException {
return PixelRunner.validatePixel(pixelExpression);
}
/**
*
* @param pixelExpression
* @return
* @throws ParserException
* @throws LexerException
* @throws IOException
*
* Returns a list of the parsed pixels from the expression
*/
public static List parsePixel(String pixelExpression) throws ParserException, LexerException, IOException {
List parsed = new Vector<>();
List encodingList = new ArrayList<>();
Map encodedTextToOriginal = new HashMap<>();
String processedPixel = PixelPreProcessor.preProcessPixel(pixelExpression, encodingList, encodedTextToOriginal);
List parsedResults = PixelRunner.parsePixel(processedPixel);
for(int i = 0; i < parsedResults.size(); i++) {
String origExpression = PixelUtility.recreateOriginalPixelExpression(parsedResults.get(i), encodingList, encodedTextToOriginal);
parsed.add(origExpression);
}
return parsed;
}
/**
*
* @param value
* @return
*
* Returns the noun for a STRING or NUMBER ONLY
* if value is an instanceof another object IllegalArgumentException will be thrown
*/
public static NounMetadata getNoun(Object value) {
NounMetadata noun = null;
if(value instanceof Number) {
noun = new NounMetadata(((Number)value).doubleValue(), PixelDataType.CONST_DECIMAL);
} else if(value instanceof String) {
if(isLiteral((String)value)) {
//we have a literal
String literal = removeSurroundingQuotes((String)value);
noun = new NounMetadata(literal, PixelDataType.CONST_STRING);
} else {
// try to convert to a number
try {
double doubleValue = Double.parseDouble(value.toString().trim());
noun = new NounMetadata(doubleValue, PixelDataType.CONST_DECIMAL);
} catch(NumberFormatException e) {
// confirmed that it is not a double
// and that we have a column
noun = new NounMetadata(value.toString().trim(),PixelDataType.COLUMN);
}
}
} else {
throw new IllegalArgumentException("Value must be a number or string!");
}
return noun;
}
public static String generatePixelString(String assignment, String value) {
return assignment+" = "+value+";";
}
public static String generatePixelString(String assignment, Object value) {
return generatePixelString(assignment, value.toString());
}
/**
* Adds a pkslString to the planner via lazy translation
* @param translation
* @param pixelString
*/
public static void addPixelToTranslation(DepthFirstAdapter translation, String pixelString) {
try {
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(pixelString.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8))));
Start tree = p.parse();
tree.apply(translation);
} catch (ParserException | LexerException | IOException ioe) {
logger.error("FAILED ON :::: " + pixelString);
logger.error(Constants.STACKTRACE, ioe);
} catch(Exception e) {
logger.error("FAILED ON :::: " + pixelString);
logger.error(Constants.STACKTRACE, e);
}
}
/**
* @param literal
* @return
*
* input: 'literal' OR "literal"
* output: literal
*
* input: 'literal OR "literal
* output: literal
*
* input: literal' OR literal"
* output: literal
*
* input: literal
* output: literal
*/
public static String removeSurroundingQuotes(String literal) {
literal = literal.trim();
if(literal.startsWith("\"") || literal.startsWith("'")) {
//remove the first quote
literal = literal.substring(1);
}
if(literal.endsWith("\"") || literal.endsWith("'")) {
if(literal.length() == 1) {
literal = "";
} else {
//remove the end quote
literal = literal.substring(0, literal.length()-1);
}
}
return literal;
}
/**
*
* @param literal
* @return
*
* Determines if the string is a literal
*/
public static boolean isLiteral(String literal) {
literal = literal.trim();
return ((literal.startsWith("\"") || literal.startsWith("'")) && (literal.endsWith("\"") || literal.endsWith("'")));
}
/**
* Checks if recipe is not cacheable
* True if it is a parameter insight, or grid-delta, etc.
* @param pixels
* @return
*/
public static boolean isNotCacheable(String[] pixels) {
String recipe = String.join("", pixels);
return PixelUtility.isNotCacheable(recipe);
}
/**
* Checks if recipe is not cacheable
* True if it is a parameter insight, or grid-delta, etc.
* @param pixels
* @return
*/
public static boolean isNotCacheable(List pixels) {
String recipe = String.join("", pixels);
return PixelUtility.isNotCacheable(recipe);
}
/**
* Checks if recipe is not cacheable
* True if it is a parameter insight, or grid-delta, etc.
* @param pixel
* @return
*/
public static boolean isNotCacheable(String pixel) {
pixel = PixelPreProcessor.preProcessPixel(pixel, new ArrayList(), new HashMap());
try {
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(pixel.getBytes("UTF-8")), "UTF-8"), pixel.length())));
InsightParamTranslation translation = new InsightParamTranslation();
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
return translation.notCacheable();
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
String eMessage = e.getMessage();
if(eMessage.startsWith("[")) {
Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]");
Matcher matcher = pattern.matcher(eMessage);
if(matcher.find()) {
String location = matcher.group(0);
location = location.substring(1, location.length()-1);
int findIndex = Integer.parseInt(location.split(",")[1]);
eMessage += ". Error in syntax around " + pixel.substring(Math.max(findIndex - 10, 0), Math.min(findIndex + 10, pixel.length())).trim();
}
}
logger.info(eMessage);
}
return false;
}
/**
* Get the insight json parameter if it exists
* @param pixelList
* @return
*/
public static Map getInsightParameterJson(List pixelList) {
String pixel = String.join("", pixelList);
pixel = PixelPreProcessor.preProcessPixel(pixel, new ArrayList(), new HashMap());
try {
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(pixel.getBytes("UTF-8")), "UTF-8"), pixel.length())));
InsightParamTranslation translation = new InsightParamTranslation();
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
return translation.getPanelViewJson();
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
String eMessage = e.getMessage();
if(eMessage.startsWith("[")) {
Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]");
Matcher matcher = pattern.matcher(eMessage);
if(matcher.find()) {
String location = matcher.group(0);
location = location.substring(1, location.length()-1);
int findIndex = Integer.parseInt(location.split(",")[1]);
eMessage += ". Error in syntax around " + pixel.substring(Math.max(findIndex - 10, 0), Math.min(findIndex + 10, pixel.length())).trim();
}
}
logger.info(eMessage);
}
return null;
}
/**
* Check if recipe is for a dashboard
* @param pixels
* @return
*/
public static boolean isDashboard(String[] pixels) {
String recipe = String.join("", pixels);
return PixelUtility.isDashboard(recipe);
}
/**
* Check if recipe is for a dashboard
* @param pixels
* @return
*/
@Deprecated
public static boolean isDashboard(List pixels) {
String recipe = String.join("", pixels);
return PixelUtility.isDashboard(recipe);
}
/**
* Check if recipe is for a dashboard
* @param pixel
* @return
*/
@Deprecated
public static boolean isDashboard(String pixel) {
pixel = PixelPreProcessor.preProcessPixel(pixel, new ArrayList(), new HashMap());
try {
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(pixel.getBytes("UTF-8")), "UTF-8"), pixel.length())));
DashboardRecipeTranslation translation = new DashboardRecipeTranslation();
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
return translation.isDashboard();
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
String eMessage = e.getMessage();
if(eMessage.startsWith("[")) {
Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]");
Matcher matcher = pattern.matcher(eMessage);
if(matcher.find()) {
String location = matcher.group(0);
location = location.substring(1, location.length()-1);
int findIndex = Integer.parseInt(location.split(",")[1]);
eMessage += ". Error in syntax around " + pixel.substring(Math.max(findIndex - 10, 0), Math.min(findIndex + 10, pixel.length())).trim();
}
}
logger.info(eMessage);
}
return false;
}
/**
*
* @param pixel
* @return {into, values}
*/
public static Object[] getFormWidgetInputs(String pixel) {
pixel = PixelPreProcessor.preProcessPixel(pixel, new ArrayList(), new HashMap());
Object[] ret = new Object[2];
try {
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(pixel.getBytes("UTF-8")), "UTF-8"), pixel.length())));
FormWidgetTranslation translation = new FormWidgetTranslation();
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
ret[0] = translation.getInto();
ret[1] =translation.getValues();
return ret;
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
return null;
}
/**
*
* @param expression
* @param encodedTextToOriginal
* @return
*
* Returns the string of the NOT encoded expression
* allows us to get the expression in its not encoded form from a mapping of the expression to what it looked like originally
*/
public static String recreateOriginalPixelExpression(String expression, List encodingList, Map encodedTextToOriginal) {
if(encodedTextToOriginal == null || encodedTextToOriginal.isEmpty()) {
return expression;
}
// loop through and see if any encodedText portions have been modified
// if they have, try and replace them back so it looks pretty for the FE
Collections.sort(encodingList, new Comparator() {
@Override
public int compare(String o1, String o2) {
int l1 = o1.length();
int l2 = o2.length();
if(l1 > l2) {
return -1;
} else if(l1 < l2) {
return 1;
}
return 0;
}
});
Iterator iterator = encodingList.iterator();
while(iterator.hasNext()) {
String encodedText = iterator.next();
if(expression.contains(encodedText)) {
expression = expression.replaceFirst(Pattern.quote(encodedText), Matcher.quoteReplacement(encodedTextToOriginal.get(encodedText)));
iterator.remove();
}
}
return expression;
}
/**
* Get the data sources within the full expression
* @param expression
* @return
*/
public static List> getDatasourcesMetadata(User user, String expression) {
/*
* Using a translation object to go through and figure out all
* the datasources and how we would want to manipulate
* and change them as people swap the data but want to use the same
* routine / analysis
*/
Insight in = new Insight();
in.setUser(user);
DatasourceTranslation translation = new DatasourceTranslation(in);
try {
expression = PixelPreProcessor.preProcessPixel(expression, new ArrayList(), new HashMap());
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(expression.getBytes("UTF-8")), "UTF-8"), expression.length())));
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
return translation.getDatasourcePixels();
}
/**
*
* @param user
* @param expression
* @return
*/
public static Set getDatabaseIds(User user, List expression) {
StringBuilder finalExpression = new StringBuilder();
expression.forEach(s -> finalExpression.append(s));
return getDatabaseIds(user, finalExpression.toString());
}
/**
* Get the data sources within the full expression
* @param expression
* @return
*/
public static Set getDatabaseIds(User user, String expression) {
/*
* Using a translation object to go through and figure out all
* the datasources and how we would want to manipulate
* and change them as people swap the data but want to use the same
* routine / analysis
*/
Insight in = new Insight();
in.setUser(user);
DatasourceTranslation translation = new DatasourceTranslation(in);
try {
expression = PixelPreProcessor.preProcessPixel(expression, new ArrayList(), new HashMap());
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(expression.getBytes("UTF-8")), "UTF-8"), expression.length())));
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
Set databaseIds = new HashSet<>();
List> datasourcePixels = translation.getDatasourcePixels();
for(Map datasourceMetaMap : datasourcePixels) {
Map paramMap = (Map) datasourceMetaMap.get("params");
List qsList = null;
Object obj = paramMap.get("qs");
if(obj instanceof List) {
qsList = (List) obj;
} else {
qsList = new ArrayList<>();
qsList.add((SelectQueryStruct) obj);
}
if(qsList != null && !qsList.isEmpty()) {
for(SelectQueryStruct qs : qsList) {
if(qs.getQsType() == AbstractQueryStruct.QUERY_STRUCT_TYPE.ENGINE
|| qs.getQsType() == AbstractQueryStruct.QUERY_STRUCT_TYPE.RAW_ENGINE_QUERY) {
databaseIds.add(qs.getEngineId());
}
}
}
}
return databaseIds;
}
/**
* Modify the file datasources in a given recipe
* @param fullRecipe String containing the original recipe to change
* @param replacementOptions List of maps containing "index" and "pixel" which represents the pixel step to change
* and the new pixel to put in its place
* If no index is found and the size of the list is 1, we will replace the first datasource
* @return
*/
public static List modifyInsightDatasource(Insight in, String fullRecipe, List> replacementOptions) {
ReplaceDatasourceTranslation translation = new ReplaceDatasourceTranslation(in);
translation.setReplacements(replacementOptions);
try {
fullRecipe = PixelPreProcessor.preProcessPixel(fullRecipe, translation.encodingList, translation.encodedToOriginal);
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(fullRecipe.getBytes("UTF-8")), "UTF-8"), fullRecipe.length())));
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
return translation.getPixels();
}
/**
* Determine if an operation op type is an error that requires user input and then a re-run of the insight
* @param opTypes
* @return
*/
public static boolean autoExecuteAfterUserInput(List opTypes) {
if(opTypes.contains(PixelOperationType.LOGGIN_REQUIRED_ERROR)) {
return true;
}
return false;
}
/**
*
* @param currentInsight
* @param recipe
* @param recipeIds
* @param params
* @param insightName
* @return
*/
public static List parameterizeRecipe(Insight currentInsight, List recipe, List recipeIds, List params, String insightName) {
Insight in = new Insight();
ParamStructSaveRecipeTranslation translation = new ParamStructSaveRecipeTranslation(in);
translation.setInputsToParameterize(params);
// loop through recipe
int recipeSize = recipe.size();
for(int i = 0; i < recipeSize; i++) {
String expression = recipe.get(i);
String pixelId = recipeIds.get(i);
try {
expression = PixelPreProcessor.preProcessPixel(expression.trim(), translation.encodingList, translation.encodedToOriginal);
Parser p = new Parser(new Lexer(new PushbackReader(new InputStreamReader(new ByteArrayInputStream(expression.getBytes("UTF-8"))), expression.length())));
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
translation.setCurrentPixelId(pixelId);
tree.apply(translation);
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
}
// combine the pixels together into a string
List paramedPixels = translation.getPixels();
StringBuilder fullRecipe = new StringBuilder();
for(String s : paramedPixels) {
fullRecipe.append(s.trim());
}
List setParameters = PixelUtility.getSetParamValuePixels(currentInsight);
for(String s : setParameters) {
fullRecipe.append(s);
}
List> insightJsonObject = ParamStructToJsonGenerator.generateInsightJsonForParameters(insightName, fullRecipe.toString(), params);
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
List paramedRecipe = new Vector<>(currentInsight.getVarStore().getInsightParameterKeys().size()+2);
// if i add parameters here
appendAddInsightParameter(currentInsight, paramedRecipe);
paramedRecipe.add("META | AddPanel(0);");
paramedRecipe.add("META | Panel (0) | SetPanelView(\"param\", \" {\"beFill\":true, \"json\":" + gson.toJson(insightJsonObject) + "} \");");
return paramedRecipe;
}
/**
* Get additional insight meta recipe steps for maintaining additional
* metadata in the insight
* @param in
* @return
*/
public static List getMetaInsightRecipeSteps(Insight in, PixelList pixelList) {
List additionalSteps = new Vector<>();
// add the pipeline positions
appendPositionInsightRecipeStep(pixelList, additionalSteps);
// // add the semoss parameters
// appendAddInsightParameter(in, additionalSteps);
// add the insight config
appendSetInsightConfig(in, additionalSteps);
// add read insight theme
appendReadInsightTheme(additionalSteps);
return additionalSteps;
}
/**
* Append the position insight recipe step to the recipe list
* @param pixelList
* @param additionalSteps
*/
public static void appendPositionInsightRecipeStep(PixelList pixelList, List additionalSteps) {
int size = pixelList.size();
if(size > 0) {
StringBuilder builder = new StringBuilder("META | PositionInsightRecipe(");
for(int i = 0; i < size; i++) {
Pixel pixel = pixelList.get(i);
Map position = pixel.getPositionMap();
if(position == null) {
builder.append("null");
} else {
builder.append(gson.toJson(position));
}
if(i+1 != size) {
builder.append(" , ");
}
}
builder.append(");");
additionalSteps.add(builder.toString());
}
}
/**
* Append add insight parameter to the recipe list
* @param in
* @param additionalSteps
*/
public static void appendAddInsightParameter(Insight in, List additionalSteps) {
VarStore varStore = in.getVarStore();
List params = varStore.getInsightParameterKeys();
// loop through the keys
// and gson it
for(String paramKey : params) {
NounMetadata paramNoun = varStore.get(paramKey);
ParamStruct param = (ParamStruct) paramNoun.getValue();
// also adjust for the new optimized id if it is set
param.swapOptimizedIds();
additionalSteps.add("META | AddInsightParameter(" + gson.toJson(param) + ");");
// swap back for this instance of the insight
// since we make a new pixel list object all together
// during the save process
param.swapOptimizedIds();
}
}
/**
* Append preApplied parameter to the recipe list
* @param in
* @param additionalSteps
*/
public static List appendPreAppliedParameter(Insight in, List additionalSteps) {
VarStore varStore = in.getVarStore();
List params = varStore.getPreDefinedParametersKeys();
// loop through the keys and gson it
for(String paramKey : params) {
NounMetadata paramNoun = varStore.get(paramKey);
ParamStruct param = (ParamStruct) paramNoun.getValue();
// also adjust for the new optimized id if it is set
param.swapOptimizedIds();
additionalSteps.add("META | AddPreDefinedParameter(" + gson.toJson(param) + ");");
// swap back for this instance of the insight
// since we make a new pixel list object all together
// during the save process
param.swapOptimizedIds();
}
return additionalSteps;
}
/**
* Add set insight config to the recipe list
* @param in
* @param additionalSteps
*/
public static void appendSetInsightConfig(Insight in, List additionalSteps) {
VarStore varStore = in.getVarStore();
NounMetadata noun = varStore.get(SetInsightConfigReactor.INSIGHT_CONFIG);
if(noun != null) {
StringBuilder builder = new StringBuilder("META | SetInsightConfig(");
builder.append(gson.toJson(noun.getValue()));
builder.append(");");
additionalSteps.add(builder.toString());
}
}
public static void appendReadInsightTheme(List additionalSteps) {
additionalSteps.add("ReadInsightTheme();");
}
/**
* This will return pixel steps to set the current value for the insight parameters
* NOTE ::: These have placeholders for params to be filled.
* This will NOT compile as proper pixel until they are filled in
* @param in
* @return
*/
public static List getSetParamValuePixels(Insight in) {
List additionalSteps = new Vector<>();
VarStore varStore = in.getVarStore();
List params = varStore.getInsightParameterKeys();
// loop through the keys
// and gson it
for(String paramKey : params) {
NounMetadata paramNoun = varStore.get(paramKey);
ParamStruct param = (ParamStruct) paramNoun.getValue();
String paramName = param.getParamName();
if(ParamStruct.PARAM_FILL_USE_ARRAY_TYPES.contains(param.getModelDisplay())
|| param.getDetailsList().get(0).getQuote() == QUOTE.NO) {
additionalSteps.add("META | SetInsightParamValue(paramName=\"" + paramName
+ "\", paramValue=[<" + paramName + ">]);");
} else {
PixelDataType importType = param.getDetailsList().get(0).getType();
if(importType == PixelDataType.CONST_INT || importType == PixelDataType.CONST_DECIMAL) {
additionalSteps.add("META | SetInsightParamValue(paramName=\"" + paramName
+ "\", paramValue=<" + paramName + ">);");
} else {
additionalSteps.add("META | SetInsightParamValue(paramName=\"" + paramName
+ "\", paramValue=\"<" + paramName + ">\");");
}
}
}
return additionalSteps;
}
/**
* Get the cached recipe to use for this insight
* @param in
* @return
*/
public static List getCachedInsightRecipe(Insight in) {
List cacheRecipe = new Vector<>();
List panelTasks = new Vector<>();
// add sheets
Map sheets = in.getInsightSheets();
for(String sheetId : sheets.keySet()) {
cacheRecipe.add("CachedSheet(\"" + sheetId + "\");");
}
// add panels
Map panels = in.getInsightPanels();
for(String panelId : panels.keySet()) {
cacheRecipe.add("CachedPanel(\"" + panelId + "\");");
InsightPanel panel = panels.get(panelId);
boolean isVisualizaiton = panel.getPanelView() != null
&& panel.getPanelView().equalsIgnoreCase("visualization");
if(isVisualizaiton) {
Map qsMap = panel.getLayerQueryStruct();
Map tOptionsMap = panel.getLayerTaskOption();
Map formatMap = panel.getLayerFormatter();
if(qsMap != null) {
for(String layer : qsMap.keySet()) {
// grab the last pixel for each panel/layer combination
Pixel viewPixel = in.getPixelList().findLastPixelViewNotRefresh(panelId, layer);
if(viewPixel != null) {
panelTasks.add(viewPixel.getPixelString());
} else {
// this is likely a clone of a viz
// recreate the pixel that generated this chart
SelectQueryStruct qs = qsMap.get(layer);
TaskOptions tOptions = tOptionsMap.get(layer);
IFormatter format = formatMap.get(layer);
StringBuffer taskPixel = new StringBuffer(QsToPixelConverter.getPixel(qs, true))
.append(" | Format(type=['").append(format.getFormatType()).append("']");
if(format.getOptionsMap() != null && !format.getOptionsMap().isEmpty()) {
taskPixel.append(", options=[").append(gson.toJson(format.getOptionsMap()))
.append("])");
} else {
taskPixel.append(")");
}
taskPixel.append(" | TaskOptions(").append(gson.toJson(tOptions.getOptions()));
if(tOptions.getLayout(panelId).equals("PivotTable")) {
taskPixel.append(") | CollectPivot();");
} else {
taskPixel.append(") | Collect(").append(panel.getNumCollect()).append(");");
}
panelTasks.add(taskPixel.toString());
}
}
} else {
// TODO: THIS CURRENTLY HAPPENS FOR WHEN YOU HAVE A GRAPH
// AND YOU WROTE A CUSTOM PIXEL RECIPE WHERE YOU NEVER CREATED A GRID
// IT IS STORED IN THE VIEW PIXEL BUT NOT IN THE INSIGHT DEPENDENCY
// try to find the task on layer 0
Pixel viewPixel = in.getPixelList().findLastPixelViewNotRefresh(panelId, "0");
if(viewPixel != null) {
panelTasks.add(viewPixel.getPixelString());
}
}
}
}
// add all the panel tasks to the cached recipe
cacheRecipe.addAll(panelTasks);
// add the color by values at the end of the recipe
for(String panelId : panels.keySet()) {
InsightPanel panel = panels.get(panelId);
List cbvs = panel.getColorByValue();
for(ColorByValueRule cbv : cbvs) {
cacheRecipe.add("Panel(\"" + panelId + "\") | RetrievePanelColorByValue(name=[\"" + cbv.getId() + "\"]) | Collect(2000);");
}
}
// add the insight template
cacheRecipe.add("ReadInsightTheme();");
return cacheRecipe;
}
/**
* Get the optimized pixel recipe steps
* @param in
* @return
*/
public static PixelList getOptimizedPixelList(Insight in) {
PixelList pList = new PixelList();
PixelList insightPixelList = in.getPixelList();
List paramStructs = in.getVarStore().pullParamStructs();
List paramStructDetails = new ArrayList<>();
for(ParamStruct pStruct : paramStructs) {
List paramDetails = pStruct.getDetailsList();
for(ParamStructDetails pDetail : paramDetails) {
paramStructDetails.add(pDetail);
}
}
int numSteps = insightPixelList.size();
int newPixelId = numSteps+1;
// add sheets
Map sheets = in.getInsightSheets();
for(String sheetId : sheets.keySet()) {
pList.addPixel( new Pixel( (newPixelId++) + "", "AddSheet(\"" + sheetId + "\");") );
String sheetState = (String) InsightUtility.getSheetState(sheets.get(sheetId), "string").getValue();
pList.addPixel( new Pixel( (newPixelId++) + "", "SetSheetState(" + sheetState + ");") );
}
// add panels
Map panels = in.getInsightPanels();
for(String panelId : panels.keySet()) {
InsightPanel panel = panels.get(panelId);
pList.addPixel( new Pixel( (newPixelId++) + "", "AddPanel(panel=[\"" + panelId + "\"], sheet=[\"" + panel.getSheetId() + "\"]);") );
String panelState = (String) InsightUtility.getPanelState(panel, "string").getValue();
pList.addPixel( new Pixel( (newPixelId++) + "", "SetPanelState(\"" + panelState + " \");") );
}
// add the main of the recipe - data loading / transformations / code blocks
for(int i = 0; i < numSteps; i++) {
Pixel pixelObject = insightPixelList.get(i);
if(pixelObject.isFrameTransformation() || pixelObject.isCodeExecution()
|| pixelObject.isAssignment() || pixelObject.isSaveDataTransformation()) {
Pixel copy = pixelObject.copy();
pList.addPixel(copy);
// adjust parameters
for(ParamStructDetails pDetail : paramStructDetails) {
if(pDetail.getPixelId().equals(pixelObject.getId())) {
// set to the new copy the optimized id recipe
// which is the new index
// recall its 0 based
pDetail.setOptimizedPixelId( (pList.size()-1) + "");
}
}
}
}
// add frame filters
List refList = new ArrayList<>();
VarStore vStore = in.getVarStore();
List frameNames = vStore.getFrameKeysCopy();
for(String frameKey : frameNames) {
ITableDataFrame frame = (ITableDataFrame) vStore.get(frameKey).getValue();
if(!refList.contains(frame)) {
GenRowFilters grf = frame.getFrameFilters();
if(grf != null && !grf.isEmpty()) {
StringBuffer filter = new StringBuffer();
if(frameKey.equals(Insight.CUR_FRAME_KEY)) {
filter.append(frame.getName());
} else {
filter.append(frameKey);
}
filter.append(" | AddFrameFilter(")
.append(QsToPixelConverter.convertGenRowFilters(grf))
.append(");");
pList.addPixel( new Pixel( (newPixelId++) + "", filter.toString() ));
}
refList.add(frame);
}
}
// add panel tasks
for(String panelId : panels.keySet()) {
InsightPanel panel = panels.get(panelId);
boolean isVisualizaiton = (panel.getPanelView() != null && panel.getPanelView().equalsIgnoreCase("visualization"));
if(isVisualizaiton) {
Map qsMap = panel.getLayerQueryStruct();
Map tOptionsMap = panel.getLayerTaskOption();
Map formatMap = panel.getLayerFormatter();
if(qsMap != null) {
for(String layer : qsMap.keySet()) {
// grab the last pixel for each panel/layer combination
Pixel viewPixel = insightPixelList.findLastPixelViewNotRefresh(panelId, layer);
if(viewPixel != null) {
pList.addPixel( new Pixel( (newPixelId++) + "", viewPixel.getPixelString() ) );
} else {
// this is likely a clone of a viz
// recreate the pixel that generated this chart
SelectQueryStruct qs = qsMap.get(layer);
TaskOptions tOptions = tOptionsMap.get(layer);
IFormatter format = formatMap.get(layer);
StringBuffer taskPixel = new StringBuffer(QsToPixelConverter.getPixel(qs, true))
.append(" | Format(type=['").append(format.getFormatType()).append("']");
if(format.getOptionsMap() != null && !format.getOptionsMap().isEmpty()) {
taskPixel.append(", options=[").append(gson.toJson(format.getOptionsMap()))
.append("])");
} else {
taskPixel.append(")");
}
taskPixel.append(" | TaskOptions(").append(gson.toJson(tOptions.getOptions()));
if(tOptions.getLayout(panelId).equals("PivotTable")) {
taskPixel.append(") | CollectPivot();");
} else {
taskPixel.append(") | Collect(").append(panel.getNumCollect()).append(");");
}
pList.addPixel( new Pixel( (newPixelId++) + "", taskPixel.toString() ) );
}
}
} else {
// TODO: THIS CURRENTLY HAPPENS FOR WHEN YOU HAVE A GRAPH
// AND YOU WROTE A CUSTOM PIXEL RECIPE WHERE YOU NEVER CREATED A GRID
// IT IS STORED IN THE VIEW PIXEL BUT NOT IN THE INSIGHT DEPENDENCY
// try to find the task on layer 0
Pixel viewPixel = insightPixelList.findLastPixelViewNotRefresh(panelId, "0");
if(viewPixel != null) {
pList.addPixel( new Pixel( (newPixelId++) + "", viewPixel.getPixelString() ) );
}
}
// add the color by values at the end of the recipe
List cbvs = panel.getColorByValue();
for(ColorByValueRule cbv : cbvs) {
StringBuffer cbvTask = new StringBuffer("Panel(\"").append(panelId)
.append("\") | RetrievePanelColorByValue(name=[\"")
.append(cbv.getId()).append("\"]) | Collect(2000);");
pList.addPixel( new Pixel( (newPixelId++) + "", cbvTask.toString()) );
}
}
}
// add visualization steps and data exports at the end
for(int i = 0; i < numSteps; i++) {
Pixel pixelObject = insightPixelList.get(i);
if(pixelObject.isSaveDataExport() || pixelObject.isSaveVisualization()) {
pList.addPixel( new Pixel( (newPixelId++) + "", pixelObject.getPixelString() ) );
}
}
return pList;
}
/**
* Remove unnecessary task pixels
* @param in
*/
public static void removeUnnecessaryTaskPixels(Insight in) {
PixelList insightPixelList = in.getPixelList();
List paramStructs = in.getVarStore().pullParamStructs();
Map paramToPixelObj = new HashMap<>();
for(ParamStruct pStruct : paramStructs) {
List paramDetails = pStruct.getDetailsList();
for(ParamStructDetails pDetail : paramDetails) {
paramToPixelObj.put(pDetail, insightPixelList.getPixel(pDetail.getPixelId()));
}
}
Map insightPanels = in.getInsightPanels();
Map panelIsVisualization = new HashMap<>();
for(String panelId : insightPanels.keySet()) {
InsightPanel panel = insightPanels.get(panelId);
boolean isVisualizaiton = panel.getPanelView().equalsIgnoreCase("visualization");
panelIsVisualization.put(panelId, isVisualizaiton);
}
Map panelCloneConsidered = new HashMap<>();
Set panelIsNotClone = new HashSet<>();
Set panelLayer = new HashSet<>();
LinkedList idsToRemove = new LinkedList<>();
// we need to store the last pixel executed for each task
int numSteps = insightPixelList.size();
for(int i = numSteps-1; i >= 0; i--) {
Pixel pixelObject = insightPixelList.get(i);
boolean removeStep = true;
Set panelIds = null;
List tOptions = pixelObject.getTaskOptions();
if(tOptions != null && !tOptions.isEmpty()) {
for(TaskOptions tOption : tOptions) {
if(tOption.isOrnament()) {
removeStep = false;
continue;
}
panelIds = tOption.getPanelIds();
for(String panelId : panelIds) {
// if we are not visualization on this panel
// this step is not needed
InsightPanel panel = insightPanels.get(panelId);
if(panel == null) {
// this is a closed panel
continue;
}
if(!panelIsVisualization.get(panelId)) {
// panel is not visualization
continue;
}
String layerId = tOption.getPanelLayerId(panelId);
if(layerId == null) {
layerId = "0";
}
String panelLayerId = panelId + "__" + layerId;
if(!panelLayer.contains(panelLayerId)) {
if(pixelObject.isRefreshPanel()) {
// need to find the original
Pixel correctPixel = insightPixelList.findLastPixelViewNotRefresh(panelId, layerId);
if(correctPixel != null) {
// set the original pixel value into this object
pixelObject.setPixelString(correctPixel.getPixelString());
pixelObject.setRefreshPanel(false);
}
}
removeStep = false;
panelLayer.add(panelLayerId);
panelIsNotClone.add(panelId);
}
// if another panel depends on this query
if(panelCloneConsidered.containsKey(panelId)) {
if(!panelCloneConsidered.get(panelId)) {
removeStep = false;
panelCloneConsidered.put(panelId, true);
}
}
}
}
if(removeStep) {
idsToRemove.addFirst(pixelObject.getId());
}
}
// store panels that are clones
List> cloneMapList = pixelObject.getCloneMapList();
if(cloneMapList != null && !cloneMapList.isEmpty()) {
for(Map simpleMap : cloneMapList) {
String thisPanel = simpleMap.get("original");
String clonePanel = simpleMap.get("clone");
if(!panelIsNotClone.contains(clonePanel)) {
// we are a clone and this is the view
// we need to keep this panel
panelCloneConsidered.put(thisPanel, false);
}
}
}
}
// remove the ids from the pixel list
if(!idsToRemove.isEmpty()) {
insightPixelList.removeIds(idsToRemove, false);
// update the references for the parameters
for(ParamStructDetails pDetails : paramToPixelObj.keySet()) {
pDetails.setPixelId(paramToPixelObj.get(pDetails).getId());
}
}
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/**
* Get the pipeline operations for a given pixel
* @param in
* @return
*/
public static Map generatePipeline(Insight in) {
long start = System.currentTimeMillis();
PipelineTranslation translation = new PipelineTranslation(in);
List encodingList = new ArrayList<>();
Map encodedTextToOriginal = new HashMap<>();
PixelList pixelList = in.getPixelList();
int size = pixelList.size();
for(int i = 0; i < size; i++) {
Pixel pixel = pixelList.get(i);
String pixelString = pixel.getPixelString();
try {
pixelString = PixelPreProcessor.preProcessPixel(pixelString, encodingList, encodedTextToOriginal);
Parser p = new Parser(new Lexer(new PushbackReader(new InputStreamReader(new ByteArrayInputStream(pixelString.getBytes("UTF-8")), "UTF-8"), pixelString.length())));
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
} catch(SemossPixelException e) {
if(!e.isContinueThreadOfExecution()) {
throw e;
}
} catch (ParserException | LexerException | IOException e) {
// we only need to catch invalid syntax here
// other exceptions are caught in lazy translation
String eMessage = e.getMessage();
if(eMessage.startsWith("[")) {
Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]");
Matcher matcher = pattern.matcher(eMessage);
if(matcher.find()) {
String location = matcher.group(0);
location = location.substring(1, location.length()-1);
int findIndex = Integer.parseInt(location.split(",")[1]);
eMessage += ". Error in syntax around " + pixelString.substring(Math.max(findIndex - 10, 0), Math.min(findIndex + 10, pixelString.length())).trim();
}
}
throw new IllegalArgumentException(eMessage, e);
}
}
long end = System.currentTimeMillis();
logger.debug("Total time to process = " + (end-start));
Map retMap = new HashMap<>();
retMap.put("idMapping", pixelList);
retMap.put("pixelParsing", translation.getAllRoutines());
return retMap;
}
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
/*
* OLD DEPRECATED METHODS - WILL DELETE AFTER A WHILE
* COMMENT DATED - 2021-01-11
*/
/**
* Add parameters into an existing recipe
* @param user
* @param recipe
* @param paramsMap
* @param insightName
* @return
*/
@Deprecated
public static List getParameterizedRecipe(User user, List recipe, List> paramsMap, String insightName) {
int numParams = paramsMap.size();
List params = new ArrayList<>(numParams);
for(Map pMap : paramsMap) {
String pName = (String) pMap.get("paramName");
if(pName == null || pName.isEmpty()) {
throw new IllegalArgumentException("Parameter list must all contain 'paramName'");
}
params.add(pName);
}
Insight in = new Insight();
in.setUser(user);
ParameterizeSaveRecipeTranslation translation = new ParameterizeSaveRecipeTranslation(in);
translation.setInputsToParameterize(params);
// loop through recipe
for(String expression : recipe) {
try {
expression = PixelPreProcessor.preProcessPixel(expression.trim(), translation.encodingList, translation.encodedToOriginal);
Parser p = new Parser(
new Lexer(
new PushbackReader(
new InputStreamReader(
new ByteArrayInputStream(expression.getBytes("UTF-8")), "UTF-8"), expression.length())));
// parsing the pixel - this process also determines if expression is syntactically correct
Start tree = p.parse();
// apply the translation.
tree.apply(translation);
} catch (ParserException | LexerException | IOException e) {
logger.error(Constants.STACKTRACE, e);
}
}
Map> processedParams = translation.getParamToSource();
Map> colToComparators = translation.getColToComparators();
List newRecipe = translation.getPixels();
// since i am already adding a AddPanel(0) at the start of this recipe
// if it is contained as the first index inside newRecipe
// i will remove it
String first = newRecipe.get(0);
first = first.replace(" ", "").trim();
if(first.equals("AddPanel(0);")) {
newRecipe.remove(0);
}
StringBuilder fullRecipe = new StringBuilder();
for(String s : newRecipe) {
fullRecipe.append(s.trim());
}
List> vec = new Vector<>();
Map map = new LinkedHashMap<>();
// recipe is the query
map.put("query", fullRecipe.toString());
map.put("label", insightName);
map.put("description", "Please select paramters for the insight");
// add params
int infiniteScrollCounter = 0;
List> paramList = new Vector<>();
for(int i = 0; i < numParams; i++) {
Map pMap = paramsMap.get(i);
String param = (String) pMap.get("paramName");
// for now
// we will add each comparator once
// based on its occurrence
Set comparators = new HashSet<>(colToComparators.get(param));
boolean keepSearch = keepSearchParameter(pMap);
// we will run this
// for every column-comparator combination
for(String comparator : comparators) {
boolean isNumeric = IQueryFilter.comparatorIsNumeric(comparator);
String jsonParamName = param + "__" + IQueryFilter.getSimpleNameForComparator(comparator);
String comparatorDisplay = IQueryFilter.getDisplayNameForComparator(comparator);
// and now for the param itself
Map paramMap = new LinkedHashMap<>();
paramMap.put("paramName", jsonParamName);
paramMap.put("required", true);
paramMap.put("useSelectedValues", true);
// nested map for view
Map paramViewMap = new LinkedHashMap<>();
if(isNumeric) {
paramViewMap.put("label", "Enter value for : " + param + " " + comparatorDisplay); // + " [user input]");
paramViewMap.put("displayType", "number");
} else {
paramViewMap.put("label", "Select values for : " + param + " " + comparatorDisplay); // + " [user input]");
paramViewMap.put("displayType", "checklist");
}
// nested attributes map within nested view map
Map paramViewAttrMap = new LinkedHashMap<>();
// if numeric - no search and single valued
paramViewAttrMap.put("searchable", !isNumeric);
paramViewAttrMap.put("multiple", !isNumeric);
paramViewAttrMap.put("quickselect", !isNumeric);
paramViewMap.put("attributes", paramViewAttrMap);
// add view
paramMap.put("view", paramViewMap);
// nested map for model
Map modelMap = new LinkedHashMap<>();
Map processedParam = processedParams.get(param);
String physicalQs = processedParam.get("qs");
String infiniteVar = "infinite"+infiniteScrollCounter++;
String paramQ = "(" + infiniteVar + " = " + processedParam.get("source") + " | Select(" + physicalQs
+ ") | Filter(" + physicalQs + " ?like \"<" + jsonParamName + "_Search>\") | Sort(columns=["
+ physicalQs + "], sort=[asc]) | Iterate()) | Collect(20);";
modelMap.put("query", paramQ);
paramMap.put("model", modelMap);
if(keepSearch & !isNumeric) {
// add to model map
modelMap.put("infiniteQuery", infiniteVar + " | Collect(20)");
modelMap.put("searchParam", jsonParamName + "_Search");
modelMap.put("dependsOn", new String[]{jsonParamName + "_Search"});
// create search as well
Map paramSearchMap = new LinkedHashMap<>();
paramSearchMap.put("paramName", jsonParamName + "_Search");
paramSearchMap.put("view", false);
Map paramSearchModel = new LinkedHashMap<>();
paramSearchModel.put("defaultValue", "");
paramSearchMap.put("model", paramSearchModel);
paramList.add(paramSearchMap);
}
// now merge the existing pMap into the default values
recursivelyMergeMaps(paramMap, pMap);
// add to the param list
paramList.add(paramMap);
}
}
// add param list
map.put("params", paramList);
// add execute
map.put("execute", "button");
vec.add(map);
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
List paramedRecipe = new Vector<>(2);
paramedRecipe.add("AddPanel(0);");
paramedRecipe.add("META | Panel (0) | SetPanelView(\"param\", \" {\"json\":" + gson.toJson(vec) + "} \");");
return paramedRecipe;
}
/**
* Recursively join two maps together based on the keys
* @param mainMap
* @param newMap
*/
private static void recursivelyMergeMaps(Map mainMap, Map newMap) {
if(newMap != null) {
for(String key : newMap.keySet()) {
if(mainMap.containsKey(key)) {
// we have an overlap
// lets see if the children are both maps
boolean newKeyIsMap = (newMap.get(key) instanceof Map);
boolean existingKeyIsMap = (mainMap.get(key) instanceof Map);
if(newKeyIsMap && existingKeyIsMap) {
// recursively go through and try to add
recursivelyMergeMaps( (Map) mainMap.get(key), (Map) newMap.get(key));
} else {
// both are not maps
// just override
// with new changes where BE separates
// the comparators out
if(!key.equals("paramName")) {
mainMap.put(key, newMap.get(key));
}
}
} else {
// brand new key
// put all into the main map
mainMap.put(key, newMap.get(key));
}
}
}
}
/**
* Determine if we need the search in the parameter list
* @param map
* @return
*/
private static boolean keepSearchParameter(Map map) {
final String MODEL_KEY = "model";
final String DEFAULT_OPTIONS_KEY = "defaultOptions";
if(map.containsKey(MODEL_KEY)) {
Map innerMap = (Map) map.get(MODEL_KEY);
if(innerMap.containsKey(DEFAULT_OPTIONS_KEY)) {
return false;
}
}
return true;
}
}