ABTesting.java

package sk.iway.iwcm.components.abtesting;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sk.iway.iwcm.Constants;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.doc.DocDB;
import sk.iway.iwcm.doc.DocDetails;
import sk.iway.iwcm.doc.GroupsDB;
import sk.iway.iwcm.stat.BrowserDetector;
import sk.iway.iwcm.users.UsersDB;

/**
 *  ABTesting.java
 *
 *@Title        webjet8
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2016
 *@author       $Author: jeeff rzapach $
 *@version      $Revision: 1.3 $
 *@created      Date: 25.1.2016 12:12:32
 *@modified     $Date: 2004/08/16 06:26:11 $
 */
public class ABTesting
{
	private ABTesting() {

	}

	public static int getVirtualPathDocId(String path, String domain, HttpServletRequest request, HttpServletResponse response)
	{
		DocDB docDB = DocDB.getInstance();

		boolean abTestAvailable = Constants.getBoolean("ABTesting");
		Identity user = UsersDB.getCurrentUser(request);

		//pre prihlaseneho usera test nepouzijeme, lebo sa neda spravit preview verzie
		if(abTestAvailable == false || (user != null && user.isAdmin()) )
			return docDB.getVirtualPathDocId(path, domain);


		String variant;
		BrowserDetector browser = BrowserDetector.getInstance(request);
		if(browser.isStatUserAgentAllowed()==false) {
			//In case, user agent is crawler, we will always return variant A
			variant = "a";
		} else {

			if ("true".equals(request.getAttribute("ABTestingPrefferVariantUrl"))) {
				//if we are in AB testing variant URL, we will not use cookie
				variant = getVariantFromUrl(path);
			} else {
				variant = Tools.getCookieValue(request.getCookies(), Constants.getString("ABTestingCookieName"), null);
			}

			if(variant==null)
			{
				variant = generateVariant();
				Cookie cookie = new Cookie(Constants.getString("ABTestingCookieName"), variant);
				cookie.setMaxAge(Constants.getInt("ABTestingCookieDays") *24*60*60);
				cookie.setPath("/");
				//response.addCookie(cookie);
				Tools.addCookie(cookie, response, request);
			}
		}

		//Set variant into request - used in Ninja
		request.setAttribute("ABTestingVariant", variant);

		if("a".equals(variant))
			return docDB.getVirtualPathDocId(path, domain);
		else
		{
			String varName = Constants.getString("ABTestingName") + variant;
			String newPath = getNewPath(path, varName);

			//ak taka stranka existuje
			int bPathDocId = docDB.getVirtualPathDocId(newPath, domain);
			String bPathUrl = docDB.getDocLink(bPathDocId);
			if (Tools.isEmpty(bPathUrl) || bPathUrl.contains(varName)==false)
			{
				//ak URL stranky co sa nasla neobsahuje variantu nemozeme pouzit, je to asi * url
				bPathDocId = -1;
			}

			if (bPathDocId > 0) {
				// [#36895] zistujeme, ci je stranka dostupna
				DocDetails basicDocDetails = docDB.getBasicDocDetails(bPathDocId, false);
				if (!basicDocDetails.isAvailable()) {
					bPathDocId = -1;
				}
			}

			if(bPathDocId>0)
				return docDB.getVirtualPathDocId(newPath, domain);
			else
				return docDB.getVirtualPathDocId(path, domain);
		}
	}

	public static String generateVariant()
	{
		String variantLetter = "a";
		try
		{
			String ratio = Constants.getString("ABTestingRatio");
			int sum = 0;

			String[] items = (ratio + "").replace(",", ".").split(":");
			double[] results = new double[items.length];
			for (int i = 0; i < items.length; i++)
			{
				results[i] = Double.parseDouble(items[i]);
				sum += results[i];
			}

			int picked = ThreadLocalRandom.current().nextInt(1, sum + 1);

			double check = 0;
			for(int i=0; i<results.length; i++)
			{
				check += results[i];
				if(picked<=check)
				{
					variantLetter = "" + ((char) ('a'+i));
					break;
				}
			}
		}
		catch(Exception e)
		{
			sk.iway.iwcm.Logger.error(e);
		}
		return variantLetter;
	}

	private static String getNewPath(String path, String varName)
	{
		String result = "";

		if(path.contains("."))
		{
			int lastDot = path.lastIndexOf('.');
			result = path.subSequence(0, lastDot) + "-"+varName+path.substring(lastDot);
		}
		else
			result = path+varName+".html";

		return result;
	}

	public static List<Integer> getAllVariantsDocIds(int doc_id)
	{
		return getAllVariantsDocIds(DocDB.getInstance().getBasicDocDetails(doc_id, false), GroupsDB.getInstance().getAllDomainsList(), DocDB.getInstance());
	}

	public static List<Integer> getAllVariantsDocIds(DocDetails doc, List<String> allDomains, DocDB docDB)
	{
		List<Integer> result = new ArrayList<>();

		if(doc==null)
			return result;

		try
		{
			String path = doc.getVirtualPath();
			String newPath;
			int variantDocId;
			for(int i = 0; i< Constants.getString("ABTestingRatio").split(":").length; i++)
			{
				newPath = getNewPath(path, Constants.getString("ABTestingName") + ("" + ((char) ('a'+i))));
				variantDocId = docDB.getVirtualPathDocId(newPath, (String) null);

				if(variantDocId>0) {
					result.add(variantDocId);
				}
				else
				{
					for(String domain : allDomains)
					{
						variantDocId = docDB.getVirtualPathDocId(newPath, domain);
						if(variantDocId>0)
						{
							DocDetails variantDoc = docDB.getBasicDocDetails(variantDocId, false);
							String virtualPath = variantDoc != null ? variantDoc.getVirtualPath() : null;
							if (virtualPath != null && Tools.isNotEmpty(virtualPath) && (virtualPath.endsWith("*") || virtualPath.endsWith("*/"))) {
								continue;
							}

							result.add(variantDocId);
							break;
						}
					}
				}
			}
		}
		catch(Exception e){sk.iway.iwcm.Logger.error(e);}

		return result;
	}

	/**
	 * Get AB testing variant from URL address
	 * @param url - URL in format /invest/abtestvariantb.html for B variant
	 * @return - variant from URL or "a" if not found
	 */
	public static String getVariantFromUrl(String url) {
		//return AB testing variant from url in format /path/to/page/abtestvariantb.html where variant could be after abtestvariant keyword
		String variant = "a";
		if (url != null) {
			int index = url.indexOf(Constants.getString("ABTestingName"));
			if (index > 0) {
				variant = url.substring(index + Constants.getString("ABTestingName").length(), index + Constants.getString("ABTestingName").length() + 1);
			}
		}
		return variant;
	}
}