from prediction.apis import data_management_engine as dme

def get_code_from_project(project_details,code_options,code_key,class_name):
    if "deployment_step" in project_details:
        version_list = []
        for i in project_details["deployment_step"]:
            if "plugins" in i:
                if f"{code_key}_class_text" in i["plugins"]:
                    if i["plugins"][f"{code_key}_class_text"] == class_name:
                        version_list.append(i["version"])
    else:
        version_list = []
    if version_list:
        max_version = max(version_list)
        logic = ""
        for i in project_details["deployment_step"]:
            if "version" in i:
                if i["version"] == max_version:
                    if "plugins" in i:
                        if f"{code_key}_class_text" in i["plugins"]:
                            if i["plugins"][f"{code_key}_class_text"] == class_name:
                                if f"{code_key}_class_code" in i["plugins"]:
                                    logic = i["plugins"]["post_score_class_code"]
    elif class_name in code_options:
        logic = code_options[class_name]
    else:
        print(
            f"WARNING: {code_key}_class not found in default options. Empty class saved to the deployment. To edit the "
            "class use the ecosystem.Ai plugin for IntelliJ or the ecosystem.Ai workbench")
        logic = ""
    return logic


def get_pre_score_code(pre_score, project_details):
    pre_score_code_options = {
        "": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport org.json.JSONObject;\n\n/**\n * This is the same structure as PrePredictCustomer\n */\npublic class DefaultPreScore extends PreScoreSuper {\n\n    public DefaultPreScore() {\n    }\n\n    /**\n     * Pre-pre predict, after feature store is read and before dynamic and static corpora.\n     */\n    public void getPrePredict() {\n    }\n\n    /**\n     * getPostPredict\n     * @param params\n     * @param session\n     * @return\n     */\n    public static JSONObject getPrePredict(JSONObject params, CqlSession session) {\n\n        /*\n        Manipulate params that will be used by scoring and post-scoring\n         */\n\n        return params;\n    }\n\n}\n',
        "DefaultPreScore": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport org.json.JSONObject;\n\n/**\n * This is the same structure as PrePredictCustomer\n */\npublic class DefaultPreScore extends PreScoreSuper {\n\n    public DefaultPreScore() {\n    }\n\n    /**\n     * Pre-pre predict, after feature store is read and before dynamic and static corpora.\n     */\n    public void getPrePredict() {\n    }\n\n    /**\n     * getPostPredict\n     * @param params\n     * @param session\n     * @return\n     */\n    public static JSONObject getPrePredict(JSONObject params, CqlSession session) {\n\n        /*\n        Manipulate params that will be used by scoring and post-scoring\n         */\n\n        return params;\n    }\n\n}\n',
        "PrePredictAutoDate": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.DateUtilities.DateFormatConverter;\nimport com.ecosystem.utils.DateUtilities.DateUtils;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.time.ZonedDateTime;\n\nimport static com.ecosystem.utils.DateUtilities.DateFormatConverter.convertStringToDate;\nimport static com.ecosystem.utils.DateUtilities.DateFormatConverter.getFormattedDateTime;\n\n/**\n * This is the same structure as PrePredictAutoDate where date and other defaults are added to the lookup store.\n */\npublic class PrePredictAutoDate {\n\n    public static DateUtils dateUtils = new DateUtils();\n    public static DateFormatConverter converter = new DateFormatConverter();\n\n\n    public PrePredictAutoDate() {\n    }\n\n    /**\n     * Pre-pre predict, after feature store is read and before dynamic and static corpora.\n     */\n    public void getPrePredict() {\n    }\n\n    /**\n     * getPostPredict\n     * @param params\n     * @param session\n     * @return\n     */\n    public static JSONObject getPrePredict(JSONObject params, CqlSession session) {\n\n        /*\n        Manipulate params that will be used by scoring and post-scoring\n         */\n\n        JSONObject featuresObj = params.getJSONObject("featuresObj");\n        JSONArray input = params.getJSONArray("input");\n        JSONArray value = params.getJSONArray("value");\n\n        String format = "yyyy-MM-dd\'T\'HH:mm:ssZ";\n        String todayDate = dateUtils.nowDate();\n\n        // datetime: "2024-12-18T09:05:46.019Z"\n        String mongoAttribute = "datetime";\n        ZonedDateTime dateTxDate = convertStringToDate(todayDate, format);\n        featuresObj.put(mongoAttribute + "_day", converter.getDayOfMonth(dateTxDate))\n                    .put(mongoAttribute + "_day_of_week_no", converter.getDayOfWeek(dateTxDate))\n                    .put(mongoAttribute + "_day_of_week", converter.getDayOfWeek(dateTxDate).toString())\n                    .put(mongoAttribute + "_day_weekend", converter.isWeekend(dateTxDate) ? 1 : 0)\n                    .put(mongoAttribute + "_month", converter.getMonthOfYear(dateTxDate).getValue())\n                    .put(mongoAttribute + "_year", converter.getYear(dateTxDate))\n                    .put(mongoAttribute + "_day_of_year", converter.getDayOfYear(dateTxDate))\n                    .put(mongoAttribute + "_year_month",\n                            converter.getYear(dateTxDate) + "-" +\n                                    String.format("%02d", converter.getMonthOfYear(dateTxDate).getValue()))\n                    .put(mongoAttribute + "_date", converter.getDate(dateTxDate))\n                    .put(mongoAttribute + "_public_holiday", converter.isPublicHoliday(dateTxDate, ""))\n                    .put(mongoAttribute + "_date_full", getFormattedDateTime(dateTxDate))\n                    .put(mongoAttribute + "_epoch", converter.getEpoch(dateTxDate))\n                    .put(mongoAttribute + "_week_of_month", converter.getWeekOfMonth(dateTxDate))\n                    .put(mongoAttribute + "_week_of_year", converter.getWeekOfYear(dateTxDate))\n                    .put(mongoAttribute + "_week_and_day",\n                            converter.getWeekOfYear(dateTxDate) + "-" +\n                                    converter.getDayOfWeekString(dateTxDate))\n                    .put(mongoAttribute + "_time", converter.getTime(dateTxDate))\n                    .put(mongoAttribute + "_hour", converter.getHour(dateTxDate))\n                    .put(mongoAttribute + "_minutes", converter.getMinutes(dateTxDate))\n                    .put(mongoAttribute + "_time_of_day", converter.getTimeOfDay(dateTxDate))\n                    .put(mongoAttribute + "_time_eat", converter.getTimeOfDay(dateTxDate));\n\n        for (String key : featuresObj.keySet()) {\n            input.put(key);                       // Add key to input array\n            value.put(featuresObj.get(key));     // Add corresponding value to values array\n        }\n\n        return params;\n\n    }\n\n}\n',
        "PreScoreDynamic": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.mongodb.client.MongoClient;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\n\n/**\n * Add key/value to properties predictor.param.lookup to allow for contextual variable lookup:\n * dynamic_lookup: \'dynamic_lookup_just4u_v1\'\n */\npublic class PreScoreDynamic extends PreScoreSuper {\n\n    public PreScoreDynamic() throws Exception {\n\n    }\n\n    /**\n     * Pre-pre predict\n     */\n    public void getPrePredict() {\n    }\n\n    /**\n     * getPostPredict\n     * example setting in properties file (look for dynamic_lookup: \'dynamic_lookup_just4u\'):\n     * predictor.param.lookup={predictor:\'justforyou\',mojo:1,database:\'mongodb\',db:\'vodacom\',table:\'fs_score_all_estore_gsm_recommender_rel_1\',dynamic_lookup: \'dynamic_lookup_just4u\',lookup:{"value":123,"key":"msisdn"},result:{parm1:\'field1\', parm2:\'field2\'}}\n     * @param params\n     * @param session\n     * @return\n     */\n    public static JSONObject getPrePredict(MongoClient mongoClient, JSONObject params, CqlSession session) throws IOException {\n\n        if (lookupDatabase == null) return params;\n\n        try {\n\n            /** Get dynamic properties and add virtual variables to the feature store. */\n            params = getDynamicSettings(mongoClient, params);\n            params = getVirtualVariables(params);\n\n            /** Pupulate contextual variables by default based on settings. */\n            params = getPrepopulateContextualVariables(params);\n\n        } catch (Exception e) {\n            LOGGER.error("PreScoreDynamic:E001:UUID: " + params.get("uuid") + " Dynamic parameters failed: " + params.toString());\n            e.printStackTrace();\n        }\n\n        return params;\n    }\n\n}\n',
        "PreScoreLookup": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.mongodb.client.MongoClient;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\n\n/**\n * Perform a mongo lookup and store the results in params for subsequent usage\n */\npublic class PreScoreLookup extends PreScoreSuper {\n\n    public PreScoreLookup() throws Exception {\n\n    }\n\n    /**\n     * Pre-pre predict\n     */\n    public void getPrePredict() {\n    }\n\n    /**\n     * getPrePredict\n     * @param mongoClient The mongo connection\n     * @param params The params object used to pass data through the runtime process\n     * @param session The cassandra connection\n     * @return params\n     */\n    public static JSONObject getPrePredict(MongoClient mongoClient, JSONObject params, CqlSession session) throws IOException {\n        try {\n            /* Get the data from the logs */\n            JSONArray resultArrayContacts = getContactsLoggingDetails(mongoClient, params, true, true, false, "100", "", "");\n            JSONArray resultArrayResponses = getResponseLoggingDetails(mongoClient, params, true, true, true, "100", "", "");\n\n            /* Write the results to params to be passed through the runtime */\n            JSONObject loggingDetails = new JSONObject();\n            loggingDetails.put("resultArrayContacts", resultArrayContacts);\n            loggingDetails.put("resultArrayResponses", resultArrayResponses);\n            params.put("prescore_data_lookup",loggingDetails);\n        } catch (Exception e) {\n            LOGGER.error("PreScoreLookup:E001:UUID: Lookup failed, prescore data lookup not written to params." + e.getMessage());\n        }\n        return params;\n    }\n\n}\n',
    }
    pre_score_logic = get_code_from_project(project_details,pre_score_code_options,"pre_score",pre_score)
    return pre_score_logic


def get_post_score_code(post_score, project_details):
    post_score_code_options = {
        "PostScoreBasic": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.DataTypeConversions;\nimport com.ecosystem.utils.JSONArraySort;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.util.ArrayList;\n\nimport static com.ecosystem.EcosystemResponse.obtainBudget;\n\n/**\n * This the ecosystem/Ai generic post-score template.\n * Customer plugin for specialized logic to be added to the runtime engine.\n * This class is loaded through the plugin loader system.\n */\npublic class PostScoreBasic extends PostScoreSuper {\n	private static final Logger LOGGER = LogManager.getLogger(PostScoreBasic.class.getName());\n\n	public PostScoreBasic() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @param models 				 Preloaded H2O Models\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/* Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			JSONObject domainsProbabilityObj = new JSONObject();\n			if (predictModelMojoResult.has("domainsProbabilityObj"))\n				domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n			/* If whitelist settings then only allow offers on list */\n			boolean whitelist = false;\n			ArrayList<String> offerWhiteList = new ArrayList<>();\n			if (params.has("whitelist")) {\n				if (!params.getJSONObject("whitelist").isEmpty()) {\n					offerWhiteList = (ArrayList<String>) params.getJSONObject("whitelist").get("whitelist");\n					params.put("resultcount", offerWhiteList.size());\n					whitelist = DataTypeConversions.getBooleanFromString(params.getJSONObject("whitelist").get("logicin"));\n				}\n			}\n\n			if (params.has("preloadCorpora")) {\n				if (params.getJSONObject("preloadCorpora").has("network")) {\n					JSONObject a = params.getJSONObject("preloadCorpora");\n					JSONObject preloadCorpora = a.getJSONObject("network");\n				}\n			}\n\n			JSONArray finalOffers = new JSONArray();\n			int resultcount = (int) params.get("resultcount");\n			/* For each offer in offer matrix determine eligibility */\n			/* get selector field from properties: predictor.selector.setup */\n			// String s = new JSONObject(settings.getSelectorSetup()).getJSONObject("lookup").getString("fields");\n\n			/** This loop can be used to add number of offers/options to return result */\n			JSONObject finalOffersObject = new JSONObject();\n			int offerIndex = 0;\n			for (int i = 0; i < resultcount; i++) {\n\n				/** Model type based approaches */\n				String type = "";\n				boolean explainability = false;\n				// LOGGER.info("predictModelMojoResult: " + predictModelMojoResult.toString());\n				if (predictModelMojoResult.get("type").getClass().getName().toLowerCase().contains("array")) {\n					type = predictModelMojoResult\n							.getJSONArray("type")\n							.get(0)\n							.toString().toLowerCase().trim();\n					if (predictModelMojoResult.has("shapley_contributions"))\n						explainability = true;\n				} else {\n					type = ((String) predictModelMojoResult.get("type")).toLowerCase().trim();\n				}\n\n				/** Offer name, defaults to type (replace with offer matrix etc) */\n				if (featuresObj.has("offer_name_final"))\n					finalOffersObject.put("offer_name", featuresObj.get("offer_name_final"));\n				else\n					finalOffersObject.put("offer_name", type);\n\n				if (featuresObj.has("offer"))\n					finalOffersObject.put("offer", featuresObj.get("offer"));\n				else\n					finalOffersObject.put("offer", type);\n\n				if (featuresObj.has("offer_id"))\n					finalOffersObject.put("offer", featuresObj.get("offer_id"));\n				else\n					finalOffersObject.put("offer_id", type);\n\n				if (featuresObj.has("price"))\n					finalOffersObject.put("price", featuresObj.get("price"));\n				else\n					finalOffersObject.put("price", 1.0);\n\n				if (featuresObj.has("cost"))\n					finalOffersObject.put("cost", featuresObj.get("cost"));\n				else\n					finalOffersObject.put("cost", 1.0);\n\n				/** Score based on model type */\n				if (type.contains("clustering")) {\n					finalOffersObject.put("cluster", predictModelMojoResult.getJSONArray("cluster").get(0));\n					finalOffersObject.put("score", DataTypeConversions.getDouble(domainsProbabilityObj, "score"));\n					finalOffersObject.put("modified_offer_score", DataTypeConversions.getDouble(domainsProbabilityObj, "score"));\n				} else if (type.contains("anomalydetection")) {\n					double[] score = (double[]) domainsProbabilityObj.get("score");\n					finalOffersObject.put("score", score[0]);\n					finalOffersObject.put("modified_offer_score", score[0]);\n				} else if (type.contains("regression")) {\n					Object score = predictModelMojoResult.getJSONArray("value").get(0);\n					finalOffersObject.put("score", score);\n					finalOffersObject.put("modified_offer_score", score);\n				} else if (type.contains("multinomial")) {\n					Object probability = predictModelMojoResult.getJSONArray("probability").get(0);\n					Object label = null;\n					try {\n						label = predictModelMojoResult.getJSONArray("label").get(0);\n					} catch (Exception e) {\n						LOGGER.error("PostScoreBasic:getPostPredict:E001: Error relates to scoring your model. The model wasn\'t loaded or is not accessible.");\n						e.printStackTrace();\n					}\n					Object response = predictModelMojoResult.getJSONArray("response").get(0);\n					finalOffersObject.put("score", probability);\n					finalOffersObject.put("modified_offer_score", probability);\n					finalOffersObject.put("offer", label);\n					finalOffersObject.put("offer_name", response);\n				} else if (type.contains("coxph")) {\n					Object score = predictModelMojoResult.getJSONArray("value").get(0);\n					finalOffersObject.put("score", score);\n					finalOffersObject.put("modified_offer_score", score);\n				} else if (type.contains("wordembedding")) {\n					float[] score = (float[]) predictModelMojoResult.getJSONArray("_text_word2vec").get(0);\n					finalOffersObject.put("score", Double.valueOf(String.valueOf(score[0])));\n					finalOffersObject.put("embedding", score);\n					finalOffersObject.put("modified_offer_score", 0.0);\n				} else if (type.contains("deeplearning")) {\n					/** From TensorFlow or PyTorch */\n					Object score = domainsProbabilityObj.getDouble("1");\n					finalOffersObject.put("score", score);\n					finalOffersObject.put("modified_offer_score", score);\n					Object response = predictModelMojoResult.getJSONArray("response").get(0);\n					finalOffersObject.put("offer_name", response);\n				} else if (type.contains("empty score")) {\n					/** This is typically used for data lookup only, obtain values from feature store! */\n					if (featuresObj.has("offer_name"))\n						finalOffersObject.put("offer_name", featuresObj.get("offer_name"));\n\n					if (featuresObj.has("offer"))\n						finalOffersObject.put("offer", featuresObj.get("offer"));\n\n					if (featuresObj.has("score"))\n						finalOffersObject.put("score", Double.valueOf(String.valueOf(featuresObj.get("score"))));\n					else\n						finalOffersObject.put("score", 1.0);\n\n					if (featuresObj.has("modified_offer_score"))\n						finalOffersObject.put("modified_offer_score", Double.valueOf(String.valueOf(featuresObj.get("modified_offer_score"))));\n					else\n						finalOffersObject.put("modified_offer_score", 1.0);\n\n					if (featuresObj.has("cost"))\n						finalOffersObject.put("cost", Double.valueOf(String.valueOf(featuresObj.get("cost"))));\n					else\n						finalOffersObject.put("cost", 0.0);\n\n				} else {\n					finalOffersObject.put("score", 1.0);\n					finalOffersObject.put("modified_offer_score", 1.0);\n				}\n\n				finalOffersObject.put("offer_details", domainsProbabilityObj);\n				if (explainability) {\n					finalOffersObject.put("shapley_contributions", predictModelMojoResult.get("shapley_contributions"));\n					finalOffersObject.put("shapley_contributions_names", predictModelMojoResult.get("shapley_contributions_names"));\n				}\n\n				/** Default value, could be replaced by offer matrix or feature store */\n				double offer_value = 1.0;\n				finalOffersObject.put("offer_value", offer_value);\n				finalOffersObject.put("uuid", params.get("uuid"));\n\n				/** Add other structures to the final result */\n				finalOffersObject.put("offer_matrix", featuresObj);\n\n				/** Budget processing option, if it\'s set in the properties */\n				if (settings.getPredictorOfferBudget() != null) {\n					JSONObject budgetItem = obtainBudget(featuresObj, params.getJSONObject("featuresObj"), offer_value);\n					double budgetSpendLimit = budgetItem.getDouble("spend_limit");\n					finalOffersObject.put("spend_limit", budgetSpendLimit);\n				}\n\n				/** Prepare offer array before final sorting */\n				finalOffers.put(offerIndex, finalOffersObject);\n				offerIndex = offerIndex + 1;\n			}\n\n			/** Sort final offer list based on score */\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n		} catch (Exception e) {\n			LOGGER.error(e);\n		}\n\n		/** Get top scores and test for explore/exploit randomization */\n		predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n		double endTimePost = System.nanoTime();\n		LOGGER.info("getPostPredict:I001: execution time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n		return predictModelMojoResult;\n	}\n\n}\n',
        "PostScoreRecommender": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.GlobalSettings;\nimport com.ecosystem.utils.JSONArraySort;\nimport com.ecosystem.utils.MathRandomizer;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\n\n/**\n * ECOSYSTEM.AI INTERNAL PLATFORM SCORING\n * Use this class to perform generic scoring based on model and generic settings with label from scoring.\n */\npublic class PostScoreRecommender {\n	private static final Logger LOGGER = LogManager.getLogger(PostScoreRecommender.class.getName());\n\n	static GlobalSettings settings;\n	static {\n		try {\n			settings = new GlobalSettings();\n		} catch (IOException e) {\n			e.printStackTrace();\n		} catch (Exception e) {\n			e.printStackTrace();\n		}\n	}\n\n	public PostScoreRecommender() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 * Example params:\n	 *    {"contextual_variable_one":"Easy Income Gold|Thin|Senior", "contextual_variable_two":"", "batch": true}\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/* Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			if (predictModelMojoResult.has("ErrorMessage")) {\n				LOGGER.error("getPostPredict:E001a:" + predictModelMojoResult.get("ErrorMessage"));\n				return null;\n			}\n\n			JSONArray offerMatrix = new JSONArray();\n			if (params.has("offerMatrix"))\n				offerMatrix = params.getJSONArray("offerMatrix");\n\n			JSONObject work = params.getJSONObject("in_params");\n\n			JSONObject domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n			String label = predictModelMojoResult.getJSONArray("label").getString(0);\n\n			JSONArray probabilities = new JSONArray();\n			if (predictModelMojoResult.has("probability"))\n				probabilities = predictModelMojoResult.getJSONArray("probability");\n			else\n				probabilities = predictModelMojoResult.getJSONArray("probabilities");\n\n			JSONArray domains = predictModelMojoResult.getJSONArray("domains");\n\n			JSONArray finalOffers = new JSONArray();\n			int resultcount = (int) params.get("resultcount");\n			int offerIndex = 0;\n\n			/** Select top items based on number of offers to present */\n			for (int i = 0; i < resultcount; i++) {\n				int explore = (int) params.get("explore");\n				JSONObject finalOffersObject = new JSONObject();\n\n				finalOffersObject.put("offer", label);\n				finalOffersObject.put("offer_name", label);\n				finalOffersObject.put("offer_name_desc", label + " - " + i);\n\n				/** process final */\n				double p = domainsProbabilityObj.getDouble(label);\n				finalOffersObject.put("score", p);\n				finalOffersObject.put("final_score", p);\n				finalOffersObject.put("modified_offer_score", p);\n				finalOffersObject.put("offer_value", 1.0); // use value from offer matrix\n				finalOffersObject.put("price", 1.0);\n				finalOffersObject.put("cost", 1.0);\n\n				finalOffersObject.put("p", p);\n				finalOffersObject.put("explore", explore);\n\n				/** Prepare array before final sort */\n				finalOffers.put(offerIndex, finalOffersObject);\n				offerIndex = offerIndex + 1;\n			}\n\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n		} catch (Exception e) {\n			e.printStackTrace();\n			LOGGER.error(e);\n		}\n\n		predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n		double endTimePost = System.nanoTime();\n		LOGGER.info("PlatformDynamicEngagement:I001: time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n\n		return predictModelMojoResult;\n\n	}\n\n	private static JSONObject getExplore(JSONObject params, double epsilonIn, String name) {\n		double rand = MathRandomizer.getRandomDoubleBetweenRange(0, 1);\n		double epsilon = epsilonIn;\n		params.put(name + "_epsilon", epsilon);\n		if (rand <= epsilon) {\n			params.put(name, 1);\n		} else {\n			params.put(name, 0);\n		}\n		return params;\n	}\n\n	/**\n	 * Get random results for MAB\n	 * @param predictResult\n	 * @param numberOffers\n	 * @return\n	 */\n	public static JSONArray getSelectedPredictResultRandom(JSONObject predictResult, int numberOffers) {\n		return getSelectedPredictResultExploreExploit(predictResult, numberOffers, 1);\n	}\n\n	/**\n	 * Get result based on score\n	 * @param predictResult\n	 * @param numberOffers\n	 * @return\n	 */\n	public static JSONArray getSelectedPredictResult(JSONObject predictResult, int numberOffers) {\n		return getSelectedPredictResultExploreExploit(predictResult, numberOffers, 0);\n	}\n\n	private static JSONObject setValues(JSONObject work) {\n		JSONObject result = new JSONObject();\n		result.put("score", work.get("score"));\n		if (work.has("price"))\n			result.put("price", work.get("price"));\n		if (work.has("cost"))\n			result.put("cost", work.get("cost"));\n		result.put("final_score", work.get("score"));\n		result.put("offer", work.get("offer"));\n		result.put("offer_name", work.get("offer_name"));\n		result.put("modified_offer_score", work.get("modified_offer_score"));\n		result.put("offer_value", work.get("offer_value"));\n		return result;\n	}\n\n	/**\n	 * Set values JSONObject that will be used in final\n	 * @param work\n	 * @param rank\n	 * @return\n	 */\n	private static JSONObject setValuesFinal(JSONObject work, int rank) {\n		JSONObject offer = new JSONObject();\n\n		offer.put("rank", rank);\n		offer.put("result", setValues(work));\n		offer.put("result_full", work);\n\n		return offer;\n	}\n\n	/**\n	 * Review this: Master version in EcosystemMaster class. {offer_treatment_code: {$regex:"_A"}}\n	 *\n	 * @param predictResult\n	 * @param numberOffers\n	 * @return\n	 */\n	public static JSONArray getSelectedPredictResultExploreExploit(JSONObject predictResult, int numberOffers, int explore) {\n		JSONArray offers = new JSONArray();\n		int resultLength = predictResult.getJSONArray("final_result").length();\n\n		for (int j = 0, k = 0; j < resultLength; j++) {\n			JSONObject work = new JSONObject();\n			if (explore == 1) {\n				int rand = MathRandomizer.getRandomIntBetweenRange(0, resultLength - 1);\n				work = predictResult.getJSONArray("final_result").getJSONObject(rand);\n			} else {\n				work = predictResult.getJSONArray("final_result").getJSONObject(j);\n			}\n\n			/* test if budget is enabled && spend_limit is greater than 0, if budget is disabled, then this will be 1.0 */\n			if (settings.getPredictorOfferBudget() != null) {\n				/* if budget setting and there is budget to spend */\n				if (work.has("spend_limit")) {\n					if ((work.getDouble("spend_limit") > 0.0) | work.getDouble("spend_limit") == -1) {\n						offers.put(k, setValuesFinal(work, k + 1));\n						if ((k + 1) == numberOffers) break;\n						k = k + 1;\n					}\n				} else {\n					break;\n				}\n			} else {\n				/* no budget setting present */\n				offers.put(k, setValuesFinal(work, k + 1));\n				if ((k + 1) == numberOffers) break;\n				k = k + 1;\n			}\n		}\n\n		return offers;\n	}\n\n	/**\n	 * @param params\n	 * @param predictResult\n	 * @return\n	 */\n	private static JSONObject getTopScores(JSONObject params, JSONObject predictResult) {\n		int resultCount = 1;\n		if (params.has("resultcount")) resultCount = params.getInt("resultcount");\n		if (predictResult.getJSONArray("final_result").length() <= resultCount)\n			resultCount = predictResult.getJSONArray("final_result").length();\n\n		/* depending on epsilon and mab settings */\n		if (params.getInt("explore") == 0) {\n			predictResult.put("final_result", getSelectedPredictResult(predictResult, resultCount));\n			predictResult.put("explore", 0);\n		} else {\n			predictResult.put("final_result", getSelectedPredictResultRandom(predictResult, resultCount));\n			predictResult.put("explore", 1);\n		}\n		return predictResult;\n	}\n\n}\n',
        "PlatformDynamicEngagement": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.DataTypeConversions;\nimport com.ecosystem.utils.JSONArraySort;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n * ECOSYSTEM.AI INTERNAL PLATFORM SCORING\n * Use this class to score with dynamic sampling configurations. This class is configured to work with no model.\n */\npublic class PlatformDynamicEngagement extends PostScoreSuper {\n	private static final Logger LOGGER = LogManager.getLogger(PlatformDynamicEngagement.class.getName());\n\n	public PlatformDynamicEngagement() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 * Example params:\n	 *    {"contextual_variable_one":"Easy Income Gold|Thin|Senior", "contextual_variable_two":"", "batch": true}\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/** Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			//JSONObject domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n			JSONObject offerMatrixWithKey = new JSONObject();\n			boolean om = false;\n			if (params.has("offerMatrixWithKey")) {\n				offerMatrixWithKey = params.getJSONObject("offerMatrixWithKey");\n				om = true;\n			}\n\n			JSONObject work = params.getJSONObject("in_params");\n\n			/***************************************************************************************************/\n			/** Standardized approach to access dynamic datasets in plugin.\n			 * The options array is the data set/feature_store that\'s keeping track of the dynamic changes.\n			 * The optionParams is the parameter set that will influence the real-time behavior through param changes.\n			 */\n			/***************************************************************************************************/\n			JSONArray options = (JSONArray) ((\n					(JSONObject) params.getJSONObject("dynamicCorpora")\n							.get("dynamic_engagement_options")).get("data"));\n			JSONObject optionParams = (JSONObject) ((\n					(JSONObject) params.getJSONObject("dynamicCorpora")\n							.get("dynamic_engagement")).get("data"));\n\n			JSONObject contextual_variables = optionParams.getJSONObject("contextual_variables");\n			JSONObject randomisation = optionParams.getJSONObject("randomisation");\n\n			/***************************************************************************************************/\n			/** Test if contextual variable is coming via api or feature store: API takes preference... */\n			if (!work.has("contextual_variable_one")) {\n				if (featuresObj.has(contextual_variables.getString("contextual_variable_one_name")))\n					work.put("contextual_variable_one", featuresObj.get(contextual_variables.getString("contextual_variable_one_name")));\n				else\n					work.put("contextual_variable_one", "");\n			}\n			if (!work.has("contextual_variable_two")) {\n				if (featuresObj.has(contextual_variables.getString("contextual_variable_two_name")))\n					work.put("contextual_variable_two", featuresObj.get(contextual_variables.getString("contextual_variable_two_name")));\n				else\n					work.put("contextual_variable_two", "");\n			}\n			/***************************************************************************************************/\n\n			JSONArray finalOffers = new JSONArray();\n			int offerIndex = 0;\n			int explore;\n			String contextual_variable_one = String.valueOf(work.get("contextual_variable_one"));\n			String contextual_variable_two = String.valueOf(work.get("contextual_variable_two"));\n			for (int j = 0; j < options.length(); j++) {\n				JSONObject option = options.getJSONObject(j);\n				String contextual_variable_one_Option = "";\n				if (option.has("contextual_variable_one") && !contextual_variable_one.equals(""))\n					contextual_variable_one_Option = String.valueOf(option.get("contextual_variable_one"));\n				String contextual_variable_two_Option = "";\n				if (option.has("contextual_variable_two") && !contextual_variable_two.equals(""))\n					contextual_variable_two_Option = String.valueOf(option.get("contextual_variable_two"));\n\n				if (contextual_variable_one_Option.equals(contextual_variable_one) && contextual_variable_two_Option.equals(contextual_variable_two)) {\n\n					double alpha = (double) DataTypeConversions.getDoubleFromIntLong(option.get("alpha"));\n					double beta = (double) DataTypeConversions.getDoubleFromIntLong(option.get("beta"));\n					double accuracy = 0.001;\n					if (option.has("accuracy"))\n						accuracy = (double) DataTypeConversions.getDoubleFromIntLong(option.get("accuracy"));\n\n					/***************************************************************************************************/\n					/* r IS THE RANDOMIZED SCORE VALUE */\n					double p = 0.0;\n					double arm_reward = 0.001;\n					if (randomisation.getString("approach").equals("epsilonGreedy")) {\n						// params.put("explore", 0);\n						explore = 0;\n						p = DataTypeConversions.getDouble(option, "arm_reward");\n						arm_reward = p;\n					} else {\n						/** REMEMBER THAT THIS IS HERE BECAUSE OF BATCH PROCESS, OTHERWISE IT REQUIRES THE TOTAL COUNTS */\n						/* Phase 2: sampling - calculate the arms and rank them */\n						// params.put("explore", 0); // force explore to zero and use Thompson Sampling only!!\n						explore = 0; // set as explore as the dynamic responder is exploration based...\n						p = DataTypeConversions.getDouble(option, "arm_reward");\n						arm_reward = p;\n\n					}\n					/** Check if values are correct */\n					if (p != p) p = 0.0;\n					if (alpha != alpha) alpha = 0.0;\n					if (beta != beta) beta = 0.0;\n					if (arm_reward != arm_reward) arm_reward = 0.0;\n					/***************************************************************************************************/\n\n					String offer = option.getString("optionKey");\n\n					JSONObject singleOffer = new JSONObject();\n					double offer_value = 1.0;\n					double offer_cost = 1.0;\n					double modified_offer_score = p;\n					if (om) {\n						if (offerMatrixWithKey.has(offer)) {\n\n							singleOffer = offerMatrixWithKey.getJSONObject(offer);\n\n							if (singleOffer.has("offer_price"))\n								offer_value = DataTypeConversions.getDouble(singleOffer, "offer_price");\n							if (singleOffer.has("price"))\n								offer_value = DataTypeConversions.getDouble(singleOffer, "price");\n\n							if (singleOffer.has("offer_cost"))\n								offer_cost = singleOffer.getDouble("offer_cost");\n							if (singleOffer.has("cost"))\n								offer_cost = singleOffer.getDouble("cost");\n\n							modified_offer_score = p * ((double) offer_value - offer_cost);\n						}\n					}\n\n					JSONObject finalOffersObject = new JSONObject();\n\n					finalOffersObject.put("offer", offer);\n					finalOffersObject.put("offer_name", offer);\n					finalOffersObject.put("offer_name_desc", option.getString("option"));\n\n					/* process final */\n					finalOffersObject.put("score", p);\n					finalOffersObject.put("final_score", p);\n					finalOffersObject.put("modified_offer_score", modified_offer_score);\n					finalOffersObject.put("offer_value", offer_value);\n					finalOffersObject.put("price", offer_value);\n					finalOffersObject.put("cost", offer_cost);\n\n					finalOffersObject.put("p", p);\n					if (option.has("contextual_variable_one"))\n						finalOffersObject.put("contextual_variable_one", option.getString("contextual_variable_one"));\n					else\n						finalOffersObject.put("contextual_variable_one", "");\n\n					if (option.has("contextual_variable_two"))\n						finalOffersObject.put("contextual_variable_two", option.getString("contextual_variable_two"));\n					else\n						finalOffersObject.put("contextual_variable_two", "");\n\n					finalOffersObject.put("alpha", alpha);\n					finalOffersObject.put("beta", beta);\n					finalOffersObject.put("weighting", (double) DataTypeConversions.getDoubleFromIntLong(option.get("weighting")));\n					finalOffersObject.put("explore", explore);\n					finalOffersObject.put("uuid", params.get("uuid"));\n					finalOffersObject.put("arm_reward", arm_reward);\n\n					/* Debugging variables */\n					if (!option.has("expected_takeup"))\n						finalOffersObject.put("expected_takeup", -1.0);\n					else\n						finalOffersObject.put("expected_takeup", (double) DataTypeConversions.getDoubleFromIntLong(option.get("expected_takeup")));\n\n					if (!option.has("propensity"))\n						finalOffersObject.put("propensity", -1.0);\n					else\n						finalOffersObject.put("propensity", (double) DataTypeConversions.getDoubleFromIntLong(option.get("propensity")));\n\n					if (!option.has("epsilon_nominated"))\n						finalOffersObject.put("epsilon_nominated", -1.0);\n					else\n						finalOffersObject.put("epsilon_nominated", (double) DataTypeConversions.getDoubleFromIntLong(option.get("epsilon_nominated")));\n\n					finalOffers.put(offerIndex, finalOffersObject);\n					offerIndex = offerIndex + 1;\n				}\n			}\n\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "arm_reward", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n			predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n			double endTimePost = System.nanoTime();\n			LOGGER.info("PlatformDynamicEngagement:I001: time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n\n		} catch (Exception e) {\n			e.printStackTrace();\n			LOGGER.error(e);\n		}\n\n		return predictModelMojoResult;\n\n	}\n\n}\n',
        "PostScoreRecommenderOffers": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.DataTypeConversions;\nimport com.ecosystem.utils.JSONArraySort;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n * recommender_smp - Single model for all products with Offermatrix\n * Multiclass classifier trained on offer_name response column, offer matrix need to have all the offers loaded with offer_price.\n */\npublic class PostScoreRecommenderOffers extends PostScoreSuper {\n    private static final Logger LOGGER = LogManager.getLogger(PostScoreRecommenderOffers.class.getName());\n\n    public PostScoreRecommenderOffers() {\n    }\n\n    /**\n     * Pre-post predict logic\n     */\n    public void getPostPredict () {\n    }\n\n    /**\n     * getPostPredict\n     *\n     * @param predictModelMojoResult Result from scoring\n     * @param params                 Params carried from input\n     * @param session                Session variable for Cassandra\n     * @return JSONObject result to further post-scoring logic\n     */\n    public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n        double startTimePost = System.nanoTime();\n        try {\n            /** Value obtained via API params */\n            JSONObject work = params.getJSONObject("in_params");\n            double in_balance = 1000.0;\n            if (work.has("in_balance"))\n                in_balance = DataTypeConversions.getDouble(work, "in_balance");\n            else\n                LOGGER.info("getPostPredict:I001aa: No in_balance specified, default used. (1000.00)");\n\n            JSONArray sortJsonArray = new JSONArray();\n            JSONArray finalOffers = new JSONArray();\n\n            /* Setup JSON objects for specific prediction case */\n            JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n            if (predictModelMojoResult.has("ErrorMessage")) {\n                LOGGER.error("getPostPredict:E001a:" + predictModelMojoResult.get("ErrorMessage"));\n                return null;\n            }\n\n            JSONArray offerMatrix = new JSONArray();\n            if (params.has("offerMatrix"))\n                offerMatrix = params.getJSONArray("offerMatrix");\n\n            JSONObject domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n            try {\n                String label = predictModelMojoResult.getJSONArray("label").getString(0).trim();\n                JSONArray domains = predictModelMojoResult.getJSONArray("domains");\n            } catch (Exception e) {\n                LOGGER.error("getPostPredict:E001b:Model could not be loaded, check deployment path: " + e);\n            }\n\n            int offerIndex = 0;\n            int explore = (int) params.get("explore");\n\n            /** Select top items based on number of offers to present */\n            for (int i = 0; i < offerMatrix.length(); i++) {\n                JSONObject singleOffer = offerMatrix.getJSONObject(i);\n                JSONObject finalOffersObject = new JSONObject();\n\n                double offer_value = 1.0;\n                if (singleOffer.has("offer_price"))\n                    offer_value = DataTypeConversions.getDouble(singleOffer, "offer_price");\n                if (singleOffer.has("price"))\n                    offer_value = DataTypeConversions.getDouble(singleOffer, "price");\n\n                double offer_cost = 1.0;\n                if (singleOffer.has("offer_cost"))\n                    offer_cost = singleOffer.getDouble("offer_cost");\n                if (singleOffer.has("cost"))\n                    offer_cost = singleOffer.getDouble("cost");\n\n                String offer_id = "";\n                String offer_name = "";\n                if (domainsProbabilityObj.has(singleOffer.getString("offer_id").trim())) {\n                    offer_id = singleOffer.getString("offer_id").trim();\n                } else if (domainsProbabilityObj.has(singleOffer.getString("offer").trim())) {\n                    offer_id = singleOffer.getString("offer").trim();\n                } else if (domainsProbabilityObj.has(singleOffer.getString("offer_name").trim())) {\n                    offer_name = singleOffer.getString("offer_name").trim();\n                } else {\n                    LOGGER.error("offerRecommender:E002-1: " + params.get("uuid") + " - Not available (offer_id, offer, offer_name from probabilities): " + singleOffer.getString("offer_name"));\n                }\n\n                double p = 0.0;\n                if (domainsProbabilityObj.has(offer_id.trim())) {\n                    offer_id = singleOffer.getString("offer_id").trim();\n                    p = domainsProbabilityObj.getDouble(offer_id);\n                } else {\n                    LOGGER.error("offerRecommender:E002-1: " + params.get("uuid") + " - Not available: " + singleOffer.getString("offer_name"));\n                }\n\n                double modified_offer_score = 1.0;\n                modified_offer_score = p * ((double) offer_value - offer_cost);\n\n                finalOffersObject.put("offer", offer_id);\n                finalOffersObject.put("offer_name", offer_name);\n                finalOffersObject.put("offer_name_desc", offer_name + " - " + i);\n\n                /** process final */\n                // double p = domainsProbabilityObj.getDouble(label);\n                finalOffersObject.put("score", p);\n                finalOffersObject.put("final_score", p);\n                finalOffersObject.put("modified_offer_score", modified_offer_score);\n                finalOffersObject.put("offer_value", offer_value); // use value from offer matrix\n                finalOffersObject.put("price", offer_value);\n                finalOffersObject.put("cost", offer_cost);\n                finalOffersObject.put("uuid", params.get("uuid"));\n\n                finalOffersObject.put("p", p);\n                finalOffersObject.put("explore", explore);\n\n                /** Prepare array before final sort */\n                finalOffers.put(offerIndex, finalOffersObject);\n                offerIndex = offerIndex + 1;\n            }\n\n            sortJsonArray = JSONArraySort.sortArray(finalOffers, "modified_offer_score", "double", "d");\n            predictModelMojoResult.put("final_result", sortJsonArray);\n\n            /** Select the correct number of offers */\n            predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n        } catch (Exception e) {\n            e.printStackTrace();\n            LOGGER.error(e);\n        }\n\n        /** Top scores from final_result */\n        predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n        double endTimePost = System.nanoTime();\n        LOGGER.info("PostScoreRecommenderOffers:I001: time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n\n        return predictModelMojoResult;\n\n    }\n\n}\n',
        "PostScoreRecommenderMulti": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.plugin.lib.ScoreAsyncItems;\nimport com.ecosystem.utils.DataTypeConversions;\nimport com.ecosystem.utils.GlobalSettings;\nimport com.ecosystem.utils.JSONArraySort;\nimport com.ecosystem.utils.MathRandomizer;\nimport com.ecosystem.worker.h2o.ModelPredictWorkerH2O;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport hex.genmodel.easy.RowData;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\nimport java.util.concurrent.ExecutionException;\n\n/**\n * recommender_smp - Multiple models for per product with Offermatrix\n * Binomial model per product, all loaded into memory, scoring per offerMatrix line item.\n */\npublic class PostScoreRecommenderMulti {\n\n    private static final Logger LOGGER = LogManager.getLogger(PostScoreRecommenderMulti.class.getName());\n\n    ModelPredictWorkerH2O modelPredictWorkerH2O;\n    ScoreAsyncItems scoreAsyncItems;\n\n    static GlobalSettings settings;\n    static {\n        try {\n            settings = new GlobalSettings();\n        } catch (IOException e) {\n            e.printStackTrace();\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public PostScoreRecommenderMulti() {\n        modelPredictWorkerH2O = new ModelPredictWorkerH2O();\n        scoreAsyncItems = new ScoreAsyncItems(modelPredictWorkerH2O);\n    }\n\n    /**\n     * Pre-post predict logic\n     */\n    public void getPostPredict () {\n    }\n\n    /**\n     * getPostPredict\n     *\n     * @param predictModelMojoResult Result from scoring\n     * @param params                 Params carried from input\n     * @param session                Session variable for Cassandra\n     * @return JSONObject result to further post-scoring logic\n     */\n    public JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n\n        double startTimePost = System.nanoTime();\n\n        /** Value obtained via API params */\n        JSONObject work = params.getJSONObject("in_params");\n        double in_balance = 100.0;\n        if (work.has("in_balance"))\n            in_balance = DataTypeConversions.getDouble(work, "in_balance");\n        else\n            LOGGER.info("getPostPredict:I001aa: No in_balance specified, default used. (1000.00)");\n\n        JSONArray finalOffers = new JSONArray();\n\n        /* Setup JSON objects for specific prediction case */\n        JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n        if (predictModelMojoResult.has("ErrorMessage")) {\n            LOGGER.error("getPostPredict:E001a:" + predictModelMojoResult.get("ErrorMessage"));\n            return null;\n        }\n\n        JSONArray offerMatrix = new JSONArray();\n        if (params.has("offerMatrix"))\n            offerMatrix = params.getJSONArray("offerMatrix");\n\n        // JSONObject domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n        // String label = predictModelMojoResult.getJSONArray("label").getString(0);\n        // JSONArray domains = predictModelMojoResult.getJSONArray("domains");\n\n        int resultcount = (int) params.get("resultcount");\n        int offerIndex = 0;\n\n        /** Async processing scoring across all models loaded per offer */\n        JSONObject domainsProbabilityObj = new JSONObject();\n        if (predictModelMojoResult.has("domainsProbabilityObj"))\n            domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n        JSONObject resultScore = new JSONObject();\n        try {\n            double startTimePost1 = System.nanoTime();\n\n            RowData row = modelPredictWorkerH2O.toRowData((JSONObject) predictModelMojoResult.get("features"));\n            resultScore = scoreAsyncItems.allOfAsyncScoring(offerMatrix, params, models, row, domainsProbabilityObj);\n\n            double endTimePost1 = System.nanoTime();\n            LOGGER.info("scoreAsyncItems.allOfAsyncScoring:I0001a: Async process time in ms: ".concat( String.valueOf((double) ((endTimePost1 - startTimePost1) / 1000000)) ));\n        } catch (ExecutionException e) {\n            e.printStackTrace();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        }\n\n        /** All items are excluded that are not active and no scores */\n        offerMatrix = resultScore.getJSONArray("newOfferMatrix");\n\n        /** Select top items based on number of offers to present */\n        for (int i = 0; i < offerMatrix.length(); i++) {\n            JSONObject singleOffer = offerMatrix.getJSONObject(i);\n            String offer_id = String.valueOf(singleOffer.get("offer_id"));\n\n            LOGGER.debug("singleOffer:D001-1: " + singleOffer.toString());\n            LOGGER.debug("singleOffer:offer_id:D001-2: " + offer_id);\n\n            /** Offer matrix needs item "price" for aggregator to work! */\n            double offer_price = 1.0;\n            if (singleOffer.has("offer_price"))\n                offer_price = DataTypeConversions.getDouble(singleOffer, "offer_price");\n            else if (singleOffer.has("price"))\n                offer_price = DataTypeConversions.getDouble(singleOffer, "price");\n            else\n                LOGGER.error("PostScoreRecommenderMultiSafaricom:E0011: price not in offerMatrix, value set to 1");\n\n            double offer_cost = 1.0;\n            if (singleOffer.has("offer_cost"))\n                offer_cost = singleOffer.getDouble("offer_cost");\n            if (singleOffer.has("cost"))\n                offer_cost = singleOffer.getDouble("cost");\n\n            int explore = (int) params.get("explore");\n            JSONObject finalOffersObject = new JSONObject();\n\n            offer_id = DataTypeConversions.getString(singleOffer.getString("offer_id"));\n\n            /*******************************************************************************/\n\n            double p = resultScore.getDouble(offer_id);\n\n            /*******************************************************************************/\n\n            /** Multi-model needs to store the model for logging - DO NOT REMOVE THIS!*/\n            finalOffersObject.put("model_name", offer_id + ".zip");\n            finalOffersObject.put("model_index", resultScore.get(offer_id + "_model_index"));\n\n            finalOffersObject.put("offer", singleOffer.get("offer_id"));\n            finalOffersObject.put("offer_name", singleOffer.get("offer_name"));\n            // finalOffersObject.put("offer_name_desc", offer_name + " - " + i);\n\n            /** process final */\n            // double p = domainsProbabilityObj.getDouble(label);\n            finalOffersObject.put("score", p);\n            finalOffersObject.put("final_score", p);\n            finalOffersObject.put("modified_offer_score", p);\n            finalOffersObject.put("offer_value", offer_price); // use value from offer matrix\n            // finalOffersObject.put("offer_profit_probability", offer_profit * p);\n            finalOffersObject.put("price", offer_price);\n            finalOffersObject.put("cost", offer_cost);\n\n            finalOffersObject.put("p", p);\n            finalOffersObject.put("explore", explore);\n\n            /** Prepare array before final sort */\n            finalOffers.put(offerIndex, finalOffersObject);\n            offerIndex = offerIndex + 1;\n        }\n\n        JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n        predictModelMojoResult.put("final_result", sortJsonArray);\n\n        predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n        /** Multi-model needs to store the model for logging! - DO NOT REMOVE THIS! */\n        if (sortJsonArray.length() > 0) {\n            if (sortJsonArray.getJSONObject(0).has("model_index")) {\n                String model_name = (String) sortJsonArray.getJSONObject(0).get("model_name");\n                params.put("model_selected", model_name);\n            }\n        } else {\n            LOGGER.error("PostScoreRecommenderMulti:E999: No result ");\n        }\n\n        double endTimePost = System.nanoTime();\n        LOGGER.info("PostScoreRecommenderMulti:I001: time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n\n        return predictModelMojoResult;\n\n    }\n\n    private static JSONObject getExplore(JSONObject params, double epsilonIn, String name) {\n        double rand = MathRandomizer.getRandomDoubleBetweenRange(0, 1);\n        double epsilon = epsilonIn;\n        params.put(name + "_epsilon", epsilon);\n        if (rand <= epsilon) {\n            params.put(name, 1);\n        } else {\n            params.put(name, 0);\n        }\n        return params;\n    }\n\n\n    /**\n     * Get random results for MAB\n     * @param predictResult\n     * @param numberOffers\n     * @return\n     */\n    public static JSONArray getSelectedPredictResultRandom(JSONObject predictResult, int numberOffers) {\n        return getSelectedPredictResultExploreExploit(predictResult, numberOffers, 1);\n    }\n\n    /**\n     * Get result based on score\n     * @param predictResult\n     * @param numberOffers\n     * @return\n     */\n    public static JSONArray getSelectedPredictResult(JSONObject predictResult, int numberOffers) {\n        return getSelectedPredictResultExploreExploit(predictResult, numberOffers, 0);\n    }\n\n    private static JSONObject setValues(JSONObject work) {\n        JSONObject result = new JSONObject();\n        result.put("score", work.get("score"));\n        if (work.has("price"))\n            result.put("price", work.get("price"));\n        if (work.has("cost"))\n            result.put("cost", work.get("cost"));\n        result.put("final_score", work.get("score"));\n        result.put("offer", work.get("offer"));\n        result.put("offer_name", work.get("offer_name"));\n        result.put("modified_offer_score", work.get("modified_offer_score"));\n        result.put("offer_value", work.get("offer_value"));\n        return result;\n    }\n\n    /**\n     * Set values JSONObject that will be used in final\n     * @param work\n     * @param rank\n     * @return\n     */\n    private static JSONObject setValuesFinal(JSONObject work, int rank) {\n        JSONObject offer = new JSONObject();\n\n        offer.put("rank", rank);\n        offer.put("result", setValues(work));\n        offer.put("result_full", work);\n\n        return offer;\n    }\n\n\n    /**\n     * Review this: Master version in EcosystemMaster class. {offer_treatment_code: {$regex:"_A"}}\n     *\n     * @param predictResult\n     * @param numberOffers\n     * @return\n     */\n    public static JSONArray getSelectedPredictResultExploreExploit(JSONObject predictResult, int numberOffers, int explore) {\n        JSONArray offers = new JSONArray();\n        int resultLength = predictResult.getJSONArray("final_result").length();\n\n        for (int j = 0, k = 0; j < resultLength; j++) {\n            JSONObject work = new JSONObject();\n            if (explore == 1) {\n                int rand = MathRandomizer.getRandomIntBetweenRange(0, resultLength - 1);\n                work = predictResult.getJSONArray("final_result").getJSONObject(rand);\n            } else {\n                work = predictResult.getJSONArray("final_result").getJSONObject(j);\n            }\n\n            /* test if budget is enabled && spend_limit is greater than 0, if budget is disabled, then this will be 1.0 */\n            if (settings.getPredictorOfferBudget() != null) {\n                /* if budget setting and there is budget to spend */\n                if (work.has("spend_limit")) {\n                    if ((work.getDouble("spend_limit") > 0.0) | work.getDouble("spend_limit") == -1) {\n                        offers.put(k, setValuesFinal(work, k + 1));\n                        if ((k + 1) == numberOffers) break;\n                        k = k + 1;\n                    }\n                } else {\n                    break;\n                }\n            } else {\n                /* no budget setting present */\n                offers.put(k, setValuesFinal(work, k + 1));\n                if ((k + 1) == numberOffers) break;\n                k = k + 1;\n            }\n        }\n\n        return offers;\n    }\n\n    /**\n     * @param params\n     * @param predictResult\n     * @return\n     */\n    private static JSONObject getTopScores(JSONObject params, JSONObject predictResult) {\n        int resultCount = 1;\n        if (params.has("resultcount")) resultCount = params.getInt("resultcount");\n        if (predictResult.getJSONArray("final_result").length() <= resultCount)\n            resultCount = predictResult.getJSONArray("final_result").length();\n\n        /* depending on epsilon and mab settings */\n        if (params.getInt("explore") == 0) {\n            predictResult.put("final_result", getSelectedPredictResult(predictResult, resultCount));\n            predictResult.put("explore", 0);\n        } else {\n            predictResult.put("final_result", getSelectedPredictResultRandom(predictResult, resultCount));\n            predictResult.put("explore", 1);\n        }\n        return predictResult;\n    }\n\n}\n',
        "PostScoreNetwork": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n */\npublic class PostScoreNetwork extends PostScoreNetworkSuper {\n\n    private static final Logger LOGGER = LogManager.getLogger(PostScoreNetwork.class.getName());\n\n    public PostScoreNetwork() {\n    }\n\n    /**\n     * Pre-post predict logic\n     */\n    public void getPostPredict () {\n    }\n\n    /**\n     * getPostPredict\n     *\n     * @param predictModelMojoResult Result from scoring\n     * @param params                 Params carried from input\n     * @param session                Session variable for Cassandra\n     * @param models                  Preloaded H2O Models\n     * @return JSONObject result to further post-scoring logic\n     */\n    public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n        double startTimePost = System.nanoTime();\n        String type = "";\n        try {\n            /* Setup JSON objects for specific prediction case */\n            JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n            System.out.println("\n>>> " + featuresObj.toString());\n\n            /** Final offer list based on score */\n            JSONArray sortJsonArray = new JSONArray();\n\n            /** Execute network based on settings in corpora */\n            /**\n             * Configure a network of client pulse responders by changing configuration based on lookup, scoring and\n             * other criteria. Ensure that the lookup settings coordinate and that defaults have been set or removed.\n             * Example, if there\'s a customer, or other settings in the __network collection, it will use those.\n             * If you want customer to go straight through, then remove that default.\n             *\n             * Additional corpora settings in project:\n             * [\n             * {name:\'network\',database:\'mongodb\',db:\'master\',table:\'bank_full_1__network\', type:\'static\', key:\'value\' },\n             * {name:\'network_config\',database:\'mongodb\',db:\'master\',table:\'bank_full_1__network_config\', type:\'static\', key:\'name\' }\n             * ]\n             * Add this line to "Additional Corpora" in your project:\n             * [{name:\'network\',database:\'mongodb\',db:\'master\',table:\'bank_full_1__network\', type:\'static\', key:\'value\' },{name:\'network_config\',database:\'mongodb\',db:\'master\',table:\'bank_full_1__network_config\', type:\'static\', key:\'name\' }]\n             *\n             * bank_full_1__network_config, ensure that this document contains this: "name": "network_config":\n             * {\n             *   "switch_key": "marital",\n             *   "name": "network_config"\n             *   "type": "model_selector",\n             * }\n             *\n             *\n             * bank_full_1__network, all options will be setup here. Ensure that "value": "" contains a valid value as per switch_key:\n             * {\n             *   "numberoffers": 4,\n             *   "subcampaign": "recommender_dynamic_bayes",\n             *   "channel": "app",\n             *   "campaign": "recommender_dynamic_bayes",\n             *   "params": "{}",\n             *   "value": "married",\n             *   "userid": "ecosystem_network",\n             *   "url": "http://customer.ecosystem.ai:8091",\n             *   "customer": "281db655-d667-4671-a715-8402c29d7d11"\n             * }\n             */\n\n            sortJsonArray = handlePreloadCorpora(params, featuresObj);\n            predictModelMojoResult.put("final_result", sortJsonArray);\n\n        } catch (Exception e) {\n            LOGGER.error("PostScoreNetwork:E001: " + e);\n        }\n\n        /** Get top scores and test for explore/exploit randomization */\n        type = getType(params);\n        if (!type.equals("lookup_passthrough")) {\n            predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n        }\n        //predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n        double endTimePost = System.nanoTime();\n        LOGGER.info("PostScoreNetwork:I001: execution time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n        return predictModelMojoResult;\n    }\n\n}\n',
        "PostScoreSpendingPersonality": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.JSONArraySort;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n * This is the post-score class to obtain spending personality.\n */\npublic class PostScoreSpendingPersonality extends PostScoreSuper {\n	private static final Logger LOGGER = LogManager.getLogger(PostScoreSpendingPersonality.class.getName());\n\n	public PostScoreSpendingPersonality() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @param models 				 Preloaded H2O Models\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/* Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			JSONObject domainsProbabilityObj = new JSONObject();\n			if (predictModelMojoResult.has("domainsProbabilityObj"))\n				domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n			if (params.has("preloadCorpora")) {\n				if (params.getJSONObject("preloadCorpora").has("network")) {\n					JSONObject a = params.getJSONObject("preloadCorpora");\n					JSONObject preloadCorpora = a.getJSONObject("network");\n				}\n			}\n\n			JSONArray finalOffers = new JSONArray();\n\n			/** This loop can be used to add number of offers/options to return result */\n			JSONObject finalOffersObject = new JSONObject();\n			int offerIndex = 0;\n\n			/** Model type based approaches */\n			/*\n			String type = "";\n			if (predictModelMojoResult.get("type").getClass().getName().toLowerCase().contains("array")) {\n				type = predictModelMojoResult\n						.getJSONArray("type")\n						.get(0)\n						.toString().toLowerCase().trim();\n			} else {\n				type = ((String) predictModelMojoResult.get("type")).toLowerCase().trim();\n			}\n			*/\n\n			if (featuresObj.has("price"))\n				finalOffersObject.put("price", featuresObj.get("price"));\n			else\n				finalOffersObject.put("price", 1.0);\n\n			if (featuresObj.has("cost"))\n				finalOffersObject.put("cost", featuresObj.get("cost"));\n			else\n				finalOffersObject.put("cost", 1.0);\n\n			if (featuresObj.has("Experiential"))\n				finalOffersObject.put("experiential", featuresObj.get("Experiential"));\n			if (featuresObj.has("Extrovert"))\n				finalOffersObject.put("extrovert", featuresObj.get("Extrovert"));\n			if (featuresObj.has("Enthusiastic"))\n				finalOffersObject.put("enthusiastic", featuresObj.get("Enthusiastic"));\n			if (featuresObj.has("Industrious"))\n				finalOffersObject.put("industrious", featuresObj.get("Industrious"));\n			if (featuresObj.has("Intentional"))\n				finalOffersObject.put("intentional", featuresObj.get("Intentional"));\n			if (featuresObj.has("Introvert"))\n				finalOffersObject.put("introvert", featuresObj.get("Introvert"));\n\n			if (featuresObj.has("personality"))\n				finalOffersObject.put("personality", featuresObj.get("personality"));\n			if (featuresObj.has("trait"))\n				finalOffersObject.put("trait", featuresObj.get("trait"));\n			if (featuresObj.has("count"))\n				finalOffersObject.put("transaction_count", featuresObj.get("count"));\n\n			finalOffersObject.put("offer", featuresObj.getString("personality"));\n			finalOffersObject.put("offer_id", featuresObj.getString("personality"));\n			finalOffersObject.put("offer_name", featuresObj.getString("personality"));\n			finalOffersObject.put("score", featuresObj.get(featuresObj.getString("personality")));\n			finalOffersObject.put("modified_offer_score", featuresObj.get(featuresObj.getString("trait")));\n\n			finalOffersObject.put("offer_details", domainsProbabilityObj);\n\n			/** Default value, could be replaced by offer matrix or feature store */\n			double offer_value = 1.0;\n			finalOffersObject.put("offer_value", offer_value);\n			finalOffersObject.put("uuid", params.get("uuid"));\n\n			/** Add other structures to the final result */\n			finalOffersObject.put("offer_matrix", new JSONObject());\n\n			/** Prepare offer array before final sorting */\n			finalOffers.put(offerIndex, finalOffersObject);\n			offerIndex = offerIndex + 1;\n\n			/** Sort final offer list based on score */\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n		} catch (Exception e) {\n			LOGGER.error(e);\n		}\n\n		/** Get top scores and test for explore/exploit randomization */\n		predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n		double endTimePost = System.nanoTime();\n		LOGGER.info("getPostPredict:I001: execution time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n		return predictModelMojoResult;\n	}\n\n}\n',
        "PostScoreMoneyPersonality": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.JSONArraySort;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n * This is the post-score class to obtain spending personality2.\n */\npublic class PostScoreMoneyPersonality extends PostScoreSuper {\n	private static final Logger LOGGER = LogManager.getLogger(PostScoreMoneyPersonality.class.getName());\n\n	public PostScoreMoneyPersonality() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @param models 				 Preloaded H2O Models\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/* Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			JSONObject domainsProbabilityObj = new JSONObject();\n			if (predictModelMojoResult.has("domainsProbabilityObj"))\n				domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n			if (params.has("preloadCorpora")) {\n				if (params.getJSONObject("preloadCorpora").has("network")) {\n					JSONObject a = params.getJSONObject("preloadCorpora");\n					JSONObject preloadCorpora = a.getJSONObject("network");\n				}\n			}\n\n			JSONArray finalOffers = new JSONArray();\n\n			/** This loop can be used to add number of offers/options to return result */\n			JSONObject finalOffersObject = new JSONObject();\n			int offerIndex = 0;\n\n			/** Model type based approaches */\n			/*\n			String type = "";\n			if (predictModelMojoResult.get("type").getClass().getName().toLowerCase().contains("array")) {\n				type = predictModelMojoResult\n						.getJSONArray("type")\n						.get(0)\n						.toString().toLowerCase().trim();\n			} else {\n				type = ((String) predictModelMojoResult.get("type")).toLowerCase().trim();\n			}\n			 */\n\n			if (featuresObj.has("price"))\n				finalOffersObject.put("price", featuresObj.get("price"));\n			else\n				finalOffersObject.put("price", 1.0);\n\n			if (featuresObj.has("cost"))\n				finalOffersObject.put("cost", featuresObj.get("cost"));\n			else\n				finalOffersObject.put("cost", 1.0);\n\n			if (featuresObj.has("conservative"))\n				finalOffersObject.put("conservative", featuresObj.get("conservative"));\n			if (featuresObj.has("carefree"))\n				finalOffersObject.put("carefree", featuresObj.get("carefree"));\n			if (featuresObj.has("adventurous"))\n				finalOffersObject.put("adventurous", featuresObj.get("adventurous"));\n			if (featuresObj.has("borrower"))\n				finalOffersObject.put("borrower", featuresObj.get("borrower"));\n			if (featuresObj.has("cautious"))\n				finalOffersObject.put("cautious", featuresObj.get("cautious"));\n			if (featuresObj.has("indulger"))\n				finalOffersObject.put("indulger", featuresObj.get("indulger"));\n			if (featuresObj.has("prudent"))\n				finalOffersObject.put("prudent", featuresObj.get("prudent"));\n			if (featuresObj.has("adventurer"))\n				finalOffersObject.put("adventurer", featuresObj.get("adventurer"));\n\n			if (featuresObj.has("personality"))\n				finalOffersObject.put("personality", featuresObj.get("personality"));\n			else\n				finalOffersObject.put("personality", "none");\n\n			if (featuresObj.has("trait")) {\n				finalOffersObject.put("trait", featuresObj.get("trait"));\n			} else {\n				finalOffersObject.put("trait", "none");\n				featuresObj.put("trait", "none");\n			}\n\n			if (featuresObj.has("count"))\n				finalOffersObject.put("transaction_count", featuresObj.get("count"));\n\n			finalOffersObject.put("offer", featuresObj.getString("personality"));\n			finalOffersObject.put("offer_id", featuresObj.getString("personality"));\n			finalOffersObject.put("offer_name", featuresObj.getString("personality"));\n			finalOffersObject.put("score", featuresObj.get(featuresObj.getString("personality")));\n			finalOffersObject.put("modified_offer_score", featuresObj.get(featuresObj.getString("personality")));\n\n			finalOffersObject.put("offer_details", domainsProbabilityObj);\n\n			/** Default value, could be replaced by offer matrix or feature store */\n			double offer_value = 1.0;\n			finalOffersObject.put("offer_value", offer_value);\n			finalOffersObject.put("uuid", params.get("uuid"));\n\n			/** Add other structures to the final result */\n			finalOffersObject.put("offer_matrix", new JSONObject());\n\n			/** Prepare offer array before final sorting */\n			finalOffers.put(offerIndex, finalOffersObject);\n			offerIndex = offerIndex + 1;\n\n			/** Sort final offer list based on score */\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n		} catch (Exception e) {\n			LOGGER.error(e);\n		}\n\n		/** Get top scores and test for explore/exploit randomization */\n		predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n		double endTimePost = System.nanoTime();\n		LOGGER.info("getPostPredict:I001: execution time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n		return predictModelMojoResult;\n	}\n\n}\n',
        "PostScoreSentimentalEquilibrium": 'package com.ecosystem.plugin.customer;\n\nimport com.datastax.oss.driver.api.core.CqlSession;\nimport com.ecosystem.utils.JSONArraySort;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport hex.genmodel.easy.EasyPredictModelWrapper;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\n/**\n * This is the post-score class to obtain spending personality.\n */\npublic class PostScoreSentimentalEquilibrium extends PostScoreSuper {\n	private static final Logger LOGGER = LogManager.getLogger(PostScoreSentimentalEquilibrium.class.getName());\n\n	public PostScoreSentimentalEquilibrium() {\n	}\n\n	/**\n	 * Pre-post predict logic\n	 */\n	public void getPostPredict () {\n	}\n\n	/**\n	 * getPostPredict\n	 *\n	 * @param predictModelMojoResult Result from scoring\n	 * @param params                 Params carried from input\n	 * @param session                Session variable for Cassandra\n	 * @param models 				 Preloaded H2O Models\n	 * @return JSONObject result to further post-scoring logic\n	 */\n	public static JSONObject getPostPredict(JSONObject predictModelMojoResult, JSONObject params, CqlSession session, EasyPredictModelWrapper[] models) {\n		double startTimePost = System.nanoTime();\n		try {\n			/* Setup JSON objects for specific prediction case */\n			JSONObject featuresObj = predictModelMojoResult.getJSONObject("featuresObj");\n			JSONObject domainsProbabilityObj = new JSONObject();\n			if (predictModelMojoResult.has("domainsProbabilityObj"))\n				domainsProbabilityObj = predictModelMojoResult.getJSONObject("domainsProbabilityObj");\n\n			if (params.has("preloadCorpora")) {\n				if (params.getJSONObject("preloadCorpora").has("network")) {\n					JSONObject a = params.getJSONObject("preloadCorpora");\n					JSONObject preloadCorpora = a.getJSONObject("network");\n				}\n			}\n\n			JSONArray finalOffers = new JSONArray();\n\n			/** This loop can be used to add number of offers/options to return result */\n			JSONObject finalOffersObject = new JSONObject();\n			int offerIndex = 0;\n\n			/** Model type based approaches */\n			/*\n			String type = "";\n			if (predictModelMojoResult.get("type").getClass().getName().toLowerCase().contains("array")) {\n				type = predictModelMojoResult\n						.getJSONArray("type")\n						.get(0)\n						.toString().toLowerCase().trim();\n			} else {\n				type = ((String) predictModelMojoResult.get("type")).toLowerCase().trim();\n			}\n			*/\n			\n			if (featuresObj.has("price"))\n				finalOffersObject.put("price", featuresObj.get("price"));\n			else\n				finalOffersObject.put("price", 1.0);\n\n			if (featuresObj.has("cost"))\n				finalOffersObject.put("cost", featuresObj.get("cost"));\n			else\n				finalOffersObject.put("cost", 1.0);\n\n			if (featuresObj.has("equilibriumViable"))\n				finalOffersObject.put("equilibriumViable", featuresObj.get("equilibriumViable"));\n			if (featuresObj.has("requiredEffort"))\n				finalOffersObject.put("requiredEffort", featuresObj.get("requiredEffort"));\n			if (featuresObj.has("sentimental_equilibrium"))\n				finalOffersObject.put("sentimental_equilibrium", featuresObj.get("sentimental_equilibrium"));\n			if (featuresObj.has("engagementScore"))\n				finalOffersObject.put("engagementScore", featuresObj.get("engagementScore"));\n\n			if (featuresObj.has("effort"))\n				finalOffersObject.put("effort", featuresObj.get("effort"));\n			if (featuresObj.has("responses"))\n				finalOffersObject.put("responses", featuresObj.get("responses"));\n\n			finalOffersObject.put("offer", featuresObj.getString("sentimental_equilibrium"));\n			finalOffersObject.put("offer_id", featuresObj.getString("sentimental_equilibrium"));\n			finalOffersObject.put("offer_name", featuresObj.getString("sentimental_equilibrium"));\n			finalOffersObject.put("score", featuresObj.get(featuresObj.getString("engagementScore")));\n			finalOffersObject.put("modified_offer_score", featuresObj.get(featuresObj.getString("sentimental_equilibrium")));\n\n			finalOffersObject.put("offer_details", domainsProbabilityObj);\n\n			/** Default value, could be replaced by offer matrix or feature store */\n			double offer_value = 1.0;\n			finalOffersObject.put("offer_value", offer_value);\n			finalOffersObject.put("uuid", params.get("uuid"));\n\n			/** Add other structures to the final result */\n			finalOffersObject.put("offer_matrix", new JSONObject());\n\n			/** Prepare offer array before final sorting */\n			finalOffers.put(offerIndex, finalOffersObject);\n			offerIndex = offerIndex + 1;\n\n			/** Sort final offer list based on score */\n			JSONArray sortJsonArray = JSONArraySort.sortArray(finalOffers, "score", "double", "d");\n			predictModelMojoResult.put("final_result", sortJsonArray);\n\n		} catch (Exception e) {\n			LOGGER.error(e);\n		}\n\n		/** Get top scores and test for explore/exploit randomization */\n		predictModelMojoResult = getTopScores(params, predictModelMojoResult);\n\n		double endTimePost = System.nanoTime();\n		LOGGER.info("getPostPredict:I001: execution time in ms: ".concat( String.valueOf((endTimePost - startTimePost) / 1000000) ));\n		return predictModelMojoResult;\n	}\n\n}\n',
    }
    post_score_logic = get_code_from_project(project_details, post_score_code_options, "post_score", post_score)
    return post_score_logic


def get_reward_class_code(reward_class, project_details):
    reward_code_options = {
        "BusinessLogicReward": 'package com.ecosystem.plugin.reward;\n\nimport com.ecosystem.plugin.business.BusinessLogic;\nimport com.ecosystem.utils.log.LogManager;\nimport com.ecosystem.utils.log.Logger;\nimport org.json.JSONObject;\n\nimport java.security.KeyManagementException;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * Dynamically loaded class to calculate the reward during algorithm loop.\n * Each iteration of the algorithm loop will call this class to calculate the reward.\n * Access to all parameters is available via the params JSONObject.\n */\npublic class BusinessLogicReward extends RewardSuper {\n\n    private static final Logger LOGGER = LogManager.getLogger(BusinessLogicReward.class.getName());\n\n    public BusinessLogicReward() {\n        // Constructor\n    }\n\n    public static void reward() {\n        // Dynamic loader\n    }\n\n    /**\n     * This method is called to calculate the reward.\n     * @param params Runtime json object with all execution path variables.\n     * @return Updated params\n     */\n    public static JSONObject reward(JSONObject params) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {\n\n        double reward = 1.0;\n        double learning_reward = 1.0;\n\n        JSONObject rewards = new JSONObject();\n\n        try {\n            /* Check if the business logic configuration is present*/\n            boolean business_Logic_configuration_check = false;\n            if (params.has("preloadCorpora")) {\n                if (params.getJSONObject("preloadCorpora").has("rewards_business_logic")) {\n                    business_Logic_configuration_check = true;\n                }\n            }\n\n            /* Return the default rewards if the business logic configuration is not present*/\n            if (!business_Logic_configuration_check) {\n                LOGGER.error("BusinessLogicReward:E002: rewards_business_logic not found in additional corpora. Returning default rewards.");\n                rewards.put("reward", reward);\n                rewards.put("learning_reward", learning_reward);\n                params.put("rewards", rewards);\n                return params;\n            }\n\n            /* Get the business logic configuration and check if rewards and/or learning reward are configured */\n            JSONObject business_logic_configuration = params.getJSONObject("preloadCorpora").getJSONObject("rewards_business_logic");\n            boolean rewards_configuration_check = false;\n            if (business_logic_configuration.has("reward")) {\n                rewards_configuration_check = true;\n            }\n            boolean learning_reward_configuration_check = false;\n            if (business_logic_configuration.has("learning_reward")) {\n                learning_reward_configuration_check = true;\n            }\n\n            /* If neither rewards nor learning_reward are configured return the default values*/\n            if (!rewards_configuration_check && !learning_reward_configuration_check) {\n                LOGGER.error("BusinessLogicReward:E002: Neither reward nor learning_reward found in rewards_business_logic. Returning default rewards.");\n                rewards.put("reward", reward);\n                rewards.put("learning_reward", learning_reward);\n                params.put("rewards", rewards);\n                return params;\n            }\n\n            /* Get the reward value from the business logic function */\n            if (rewards_configuration_check) {\n                JSONObject rewards_configuration = business_logic_configuration.getJSONObject("reward");\n                String function_name = rewards_configuration.getString("function_name");\n                String output = rewards_configuration.getString("output");\n                params.put("business_logic", function_name);\n                params = BusinessLogic.getValues(params);\n                reward = params.getJSONObject("business_logic_results").optDouble(output);\n            }\n\n            /* Get the learning_reward value from the business logic function */\n            if (learning_reward_configuration_check) {\n                JSONObject learning_reward_configuration = business_logic_configuration.getJSONObject("learning_reward");\n                String function_name = learning_reward_configuration.getString("function_name");\n                String output = learning_reward_configuration.getString("output");\n                params.put("business_logic", function_name);\n                params = BusinessLogic.getValues(params);\n                learning_reward = params.getJSONObject("business_logic_results").optDouble(output);\n            }\n        } catch (Exception e) {\n            LOGGER.error("BusinessLogicReward:E001: Error calculating reward using business logic, using default rewards: "+e.getMessage());\n        }\n\n        rewards.put("reward", reward);\n        rewards.put("learning_reward", learning_reward);\n        params.put("rewards", rewards);\n        return params;\n    }\n\n}\n',
        "DefaultReward": 'package com.ecosystem.plugin.reward;\n\nimport org.json.JSONObject;\n\n/**\n * Dynamically loaded class to calculate the reward during algorithm loop.\n * Each iteration of the algorithm loop will call this class to calculate the reward.\n * Access to all parameters is available via the params JSONObject.\n */\npublic class DefaultReward extends RewardSuper {\n\n    public DefaultReward () {\n        // Constructor\n    }\n\n    public static void reward() {\n        // Dynamic loader\n    }\n\n    /**\n     * This method is called to calculate the reward.\n     * @param params Runtime json object with all execution path variables.\n     * @return Updated params\n     */\n    public static JSONObject reward(JSONObject params) {\n\n        double reward = 1.0;\n        double learning_reward = 1.0;\n\n        JSONObject rewards = params.optJSONObject("rewards");\n        if (rewards == null)\n            rewards = new JSONObject();\n\n        rewards.put("reward", reward);\n        rewards.put("learning_reward", learning_reward);\n        params.put("rewards", rewards);\n        return params;\n    }\n\n}\n',
        "ExampleReward": 'package com.ecosystem.plugin.reward;\n\nimport org.json.JSONObject;\n\n/**\n * Dynamically loaded class to calculate the reward during algorithm loop.\n */\npublic class ExampleReward extends RewardSuper {\n\n    public ExampleReward () {\n        // Constructor\n    }\n\n    public static void reward() {\n        // dynamic loader\n    }\n\n    /**\n     *\n     * @return params\n     */\n    public static JSONObject reward(JSONObject params) {\n\n        double reward = 0.0;\n\n        /** Obtain these values from offer matrix, feature store or corpora */\n        double cost = 0;\n        double category = 0;\n        int state = 0;\n\n        if (cost > category && state == 1) {\n            reward = 10;\n        } else if (cost == category && state == 1) {\n            reward = 8;\n        } else if (cost < category && state == 1) {\n            reward = 0;\n        } else if (cost > category && state == 0) {\n            reward = -10;\n        } else if (cost == category && state == 0) {\n            reward = -10;\n        } else if (cost < category && state == 0) {\n            reward = -10;\n        } else {\n            reward = 0;\n        }\n\n        JSONObject rewards = params.optJSONObject("rewards");\n        if (rewards == null)\n            rewards = new JSONObject();\n        rewards.put("reward", reward);\n        return params;\n    }\n\n}\n',
    }
    reward_logic = get_code_from_project(project_details, reward_code_options, "reward_class", reward_class)
    return reward_logic


def get_budget_tracker_default():
    return {
        "budget_parameters_database": "",
        "budget_parameters_datasource": "mongodb",
        "budget_id": "",
        "description": "",
        "budget_parameters_table_collection": "",
        "x_axis_datasource": "offer_matrix",
        "x_axis_name": "",
        "acc_namesource": "",
        "y_axis_name": "",
        "y_axis_namesource": "",
        "acc_name": "",
        "budget_strategy": "",
        "x_axis_namesource": "",
        "acc_datasource": "offer_matrix",
        "y_axis_datasource": "offer_matrix"
    }


def get_model_configuration_default():
    return {}


def get_setup_offer_matrix_default():
    return {
        "offer_lookup_id": "",
        "database": "",
        "table_collection": "",
        "datasource": "mongodb"
    }


def get_multi_armed_bandit_default():
    return {
        "epsilon": "",
        "duration": 0,
        "pulse_responder_uuid": ""
    }


def get_whitelist_default():
    return {
        "table_collection": "",
        "datasource": "mongodb",
        "database": ""
    }


def get_model_selector_default():
    return {
        "selector_column": "",
        "lookup": "",
        "database": "",
        "selector": "",
        "table_collection": "",
        "datasource": "mongodb"
    }


def get_pattern_selector_default():
    return {
        "pattern": "",
        "duration": ""
    }


def get_corpora_default():
    return {
        "corpora": ""
    }


def get_parameter_access_default():
    return {}


def get_api_endpoint_code_default():
    return ""

def check_value_validity(data,check_rules,data_name):
    """
    Check the data validity for deployment_management module. Does not return but raises an error if the input is invalid.

    param: data: The data to check.
    param: check_rules: The rules to check the data against.
    param: data_name: The name of the data to check for error message purposes
    """
    try:
        if "field" in check_rules:
            field = check_rules["field"]
            types = check_rules["type"]
            valid_values = check_rules["values"]
            if field not in data:
                raise KeyError(f"{data_name} must contain {field}")
            if not isinstance(data[field], types):
                raise TypeError(f"{field} in {data_name} should be of type {types}")
            if valid_values:
                if isinstance(data[field], str):
                    if data[field] not in valid_values:
                        raise ValueError(f"{field} in {data_name} must be in {valid_values}")
                else:
                    for valid_iter in valid_values:
                        if valid_iter not in data[field]:
                            raise ValueError(f"{field} in {data_name} must contain {valid_iter}")
    except Exception as error:
        print(f"Unexpected error occurred while checking input validity: {error}")
        raise

def check_parameter_access_dict(parameter_access, auth, test_mongo_connection, test_client, test_cassandra_connection, test_presto_connection):
    """
    Check the parameter access dictionary for deployment_management module. Does not return but raises an error if the input is invalid.

    param: parameter_access: The parameter access input to check.
    param: auth: Token for accessing the ecosystem-server. Created using the jwt_access package.
    param: test_mongo_connection: Boolean to test the mongo connection.
    param: test_client: MongoDB client to test the connection.
    param: test_cassandra_connection: Boolean to test the cassandra connection.
    param: test_presto_connection: Boolean to test the presto connection.
    """
    required_fields = [
        {"field":"datasource","type":str,"values":["mongodb", "cassandra", "presto"]}
        ,{"field":"database","type":str,"values":None}
        ,{"field":"table_collection","type":str,"values":None}
        ,{"field":"lookup","type":dict,"values":["key","value"]}
        ,{"field":"lookup_defaults","type":str,"values":None}
        ,{"field":"lookup_fields","type":(list,type(None)),"values":None}
        ,{"field":"create_virtual_variables","type":bool,"values":None}
        ,{"field":"virtual_variables","type":list,"values":None}
    ]
    for field_iter in required_fields:
        check_value_validity(parameter_access,field_iter,"parameter_access")

    if parameter_access["datasource"] == "mongodb" and test_mongo_connection:
        db_parameter_access = test_client[parameter_access["database"]]
        if db_parameter_access[parameter_access["table_collection"]].estimated_document_count() == 0:
            print("WARNING: It looks like the parameter_access collection is empty")
        else:
            test_parameter_access_row = db_parameter_access[parameter_access["table_collection"]].find().next()
            if parameter_access["lookup"]["key"] not in test_parameter_access_row:
                print("WARNING: It looks like the specified key is not a field in the parameter_access collection")
            if type(test_parameter_access_row[parameter_access["lookup"]["key"]]) != type(
                    parameter_access["lookup"]["value"]):
                print(
                    "WARNING: It looks like value specified in the lookup in parameter_access does not match the type found in the collection")
    elif parameter_access["datasource"] == "cassandra" and test_cassandra_connection:
        data_check = dme.get_cassandra_sql(auth, "SELECT * FROM {} LIMIT 1".format(parameter_access["table_collection"]))
        type_sql = "SELECT type FROM system_schema.columns WHERE keyspace_name = '{}' AND table_name = '{}' AND column_name = '{}'".format(
            parameter_access["table_collection"].split(".")[0], parameter_access["table_collection"].split(".")[1],
            parameter_access["lookup"]["key"])
        if not data_check["data"]:
            print("WARNING: It looks like the parameter_access collection is empty")
        elif parameter_access["lookup"]["key"] not in data_check["data"][0]:
            print("WARNING: It looks like the specified key is not a field in the parameter_access collection")
        else:
            cassandra_type = dme.get_cassandra_sql(auth, type_sql)["data"][0]["type"]
            if cassandra_type == "text" and not isinstance(parameter_access["lookup"]["value"], str):
                print(
                    "WARNING: It looks like value specified in the lookup in parameter_access does not match the type found in the collection")
            elif cassandra_type != "text" and isinstance(parameter_access["lookup"]["value"], str):
                print(
                    "WARNING: It looks like value specified in the lookup in parameter_access does not match the type found in the collection")
    elif parameter_access["datasource"] == "presto" and test_presto_connection:
        # TODO: Ask Jay how Presto connection works and figure our how to implement test on Presto Connection
        print("WARNING: presto connection could not be tested")


def check_parameter_access_input(parameter_access,auth,test_mongo_connection,test_client,test_cassandra_connection,test_presto_connection):
    """
    Check the parameter access input for deployment_management module. Does not return but raises an error if the input is invalid.

    param: parameter_access: The parameter access input to check.
    param: auth: Token for accessing the ecosystem-server. Created using the jwt_access package.
    param: test_mongo_connection: Boolean to test the mongo connection.
    param: test_client: MongoDB client to test the connection.
    param: test_cassandra_connection: Boolean to test the cassandra connection.
    param: test_presto_connection: Boolean to test the presto connection.
    """
    if not isinstance(parameter_access, (dict,list)):
        raise TypeError("parameter_access should be a dictionary or list of dictionaries")
    if isinstance(parameter_access, dict):
        check_parameter_access_dict(parameter_access, auth, test_mongo_connection, test_client,
                                    test_cassandra_connection, test_presto_connection)
    else:
        for parameter_access_dict in parameter_access:
            check_parameter_access_dict(parameter_access_dict, auth, test_mongo_connection, test_client,
                                        test_cassandra_connection, test_presto_connection)
