提交 a4486c17 编写于 作者: Y yong.you

Merge branch 'biz' of github.com:dianping/cat into biz

......@@ -14,7 +14,7 @@
<dependencies>
<dependency>
<groupId>com.dianping.cat</groupId>
<artifactId>cat-core</artifactId>
<artifactId>cat-client</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
......
......@@ -34,7 +34,7 @@ public class ABTestSampleServlet extends HttpServlet {
}
public static enum MyABTestId implements ABTestName {
CASE1("demo1");
CASE1("SampleTest");
private String m_id;
......
......@@ -5,40 +5,37 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.unidal.helper.Splitters;
import org.unidal.lookup.annotation.Inject;
import org.unidal.lookup.util.StringUtils;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class IPDistributionStrategy implements ABTestGroupStrategy {
public static final String ID = "ip-distribution";
public IPDistributionStrategy(){
public IPDistributionStrategy() {
System.out.println("new " + ID + " created");
}
@Inject("IP")
private String m_ipAddress;
private List<String> m_ips;
@Override
public void apply(ABTestContext ctx) {
ABTestEntity entity = ctx.getEntity();
String config = entity.getGroupStrategyConfiguration();
List<String> ips = Splitters.by(',').trim().noEmptyItem().split(config);
HttpServletRequest req = ctx.getHttpServletRequest();
String address = getRemoteAddr(req);
String group = ctx.getCookielet("ab");
if(group != null && group.equals("A")){
ctx.setGroupName("A");
}else{
for (String ip : ips) {
for (String ip : m_ips) {
if (ip.equals(address)) {
ctx.setGroupName("A");
ctx.setCookielet("ab", "A");
ctx.setCookielet("hit", "1");
return;
}
}
}
ctx.setGroupName("B");
}
public String getRemoteAddr(HttpServletRequest req) {
......@@ -58,7 +55,7 @@ public class IPDistributionStrategy implements ABTestGroupStrategy {
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = req.getRemoteAddr();
if(ip.equals("127.0.0.1") || ip.startsWith("0:0:0:0:0:0:0:1")){
if (ip.equals("127.0.0.1") || ip.startsWith("0:0:0:0:0:0:0:1")) {
ip = IPUtils.getFirstNoLoopbackIP4Address();
}
}
......@@ -67,6 +64,7 @@ public class IPDistributionStrategy implements ABTestGroupStrategy {
}
@Override
public void init(ABTestEntity entity) {
public void init() {
m_ips = Splitters.by(',').trim().noEmptyItem().split(m_ipAddress);
}
}
<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<domain id="Cat"/>
<domain id="TuanGouWeb"/>
</config>
......@@ -15,6 +15,11 @@
<groupId>org.unidal.framework</groupId>
<artifactId>foundation-service</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>org.unidal.framework</groupId>
<artifactId>java-fragment</artifactId>
<version>0.1.0</version>
</dependency>
<dependency>
<groupId>org.jboss.netty</groupId>
......@@ -55,8 +60,7 @@
<manifest>
${basedir}/src/main/resources/META-INF/dal/model/client-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/model/status-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/model/abtest-manifest.xml,
</manifest>
${basedir}/src/main/resources/META-INF/dal/model/abtest-manifest.xml,</manifest>
</configuration>
</execution>
<execution>
......@@ -69,6 +73,16 @@
<className>com.dianping.cat.build.ComponentsConfigurator</className>
</configuration>
</execution>
<execution>
<id>generate dal model files</id>
<phase>generate-sources</phase>
<goals>
<goal>dal-model</goal>
</goals>
<configuration>
<manifest><![CDATA[${basedir}/src/main/resources/META-INF/dal/model/abtest-manifest.xml,]]></manifest>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
......
......@@ -3,6 +3,8 @@ package com.dianping.cat.abtest.repository;
import java.util.Map;
import java.util.Set;
import javax.script.Invocable;
import com.dianping.cat.abtest.spi.ABTestEntity;
public interface ABTestEntityRepository {
......@@ -10,5 +12,7 @@ public interface ABTestEntityRepository {
public Set<String> getActiveRuns();
public Invocable getInvocable(int runID);
public String getAbtestModel();
}
package com.dianping.cat.abtest.repository;
import com.dianping.cat.abtest.model.entity.Field;
import com.dianping.cat.abtest.model.entity.GroupstrategyDescriptor;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class FieldInjectUtil {
public void inject(ABTestGroupStrategy targetGroupStrategy, GroupstrategyDescriptor descriptor) throws Exception {
for (Field field : descriptor.getFields()) {
java.lang.reflect.Field modifiersField = targetGroupStrategy.getClass().getDeclaredField(
field.getModifierName());
modifiersField.setAccessible(true);
if (field.getType().equals("String")) {
modifiersField.set(targetGroupStrategy, field.getValue());
} else if (field.getType().equals("int") || field.getType().equals("Integer")) {
modifiersField.setInt(targetGroupStrategy, Integer.parseInt(field.getValue()));
} else if (field.getType().equals("boolean") || field.getType().equals("Boolean")) {
modifiersField.setBoolean(targetGroupStrategy, Boolean.parseBoolean(field.getValue()));
} else if (field.getType().equals("long") || field.getType().equals("Long")) {
modifiersField.setLong(targetGroupStrategy, Long.parseLong(field.getValue()));
} else if (field.getType().equals("double") || field.getType().equals("Double")) {
modifiersField.setDouble(targetGroupStrategy, Double.parseDouble(field.getValue()));
} else if (field.getType().equals("float") || field.getType().equals("Float")) {
modifiersField.setFloat(targetGroupStrategy, Float.parseFloat(field.getValue()));
} else {
modifiersField.set(targetGroupStrategy, field.getValue());
}
}
}
}
package com.dianping.cat.abtest.repository;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.LockSupport;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.unidal.helper.Files;
......@@ -32,18 +36,28 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
@Inject
private ClientConfigManager m_configManager;
private String m_domain;
@Inject
private int m_refreshTimeInSeconds = 60; // seconds
private Map<String, ABTestEntity> m_entities = new HashMap<String, ABTestEntity>();
private Map<String, ABTestEntity> m_entities = new ConcurrentHashMap<String, ABTestEntity>();
private Set<String> m_activeRuns = new HashSet<String>();
private Set<String> m_activeRuns = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
@Inject
private int m_refreshTimeInSeconds = 60; // seconds
private Map<Integer, ABTestGroupStrategy> m_strategies = new ConcurrentHashMap<Integer, ABTestGroupStrategy>();
private String m_abtestModel;
private Map<Integer, Invocable> m_invokeMap = new ConcurrentHashMap<Integer, Invocable>();
private FieldInjectUtil m_fieldInjector = new FieldInjectUtil();
private ScriptEngineManager m_mgr;
private ScriptEngine m_engine;
private Map<String, ABTestGroupStrategy> m_strategies = new HashMap<String, ABTestGroupStrategy>();
private String m_domain;
private long m_lastUpdateTime = -1;
private String m_abtestModel;
@Override
public Map<String, ABTestEntity> getCurrentEntities() {
......@@ -55,10 +69,15 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
return getClass().getSimpleName();
}
public Invocable getInvocable(int runID) {
return m_invokeMap.get(runID);
}
@Override
public void initialize() throws InitializationException {
m_domain = m_configManager.getDomain().getId();
m_mgr = new ScriptEngineManager();
m_engine = m_mgr.getEngineByExtension("java");
}
private void refresh() {
......@@ -67,13 +86,15 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
for (Server server : m_configManager.getServers()) {
String ip = server.getIp();
int port = server.getHttpPort();
String url = String.format("http://%s:%s/cat/s/abtest?op=model", ip, port);
String url = String.format("http://%s:%s/cat/s/abtest?op=model&lastUpdateTime=%s", ip, port, m_lastUpdateTime);
Transaction t = Cat.newTransaction("ABTest", url);
try {
InputStream inputStream = Urls.forIO().connectTimeout(300).readTimeout(2000).openStream(url);
String content = Files.forIO().readFrom(inputStream, "utf-8");
AbtestModel abtest = DefaultSaxParser.parse(content);
if (abtest.getCases() != null && abtest.getCases().size() > 0) {
ABTestVisitor visitor = new ABTestVisitor(m_domain);
abtest.accept(visitor);
......@@ -83,6 +104,7 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
m_activeRuns = visitor.getActiveRuns();
m_abtestModel = abtest.toString();
break;
}
} catch (Throwable e) {
t.setStatus(e);
Cat.logError(e);
......@@ -124,8 +146,6 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
public ABTestVisitor(String domain) {
m_domain = domain;
m_entities = new HashMap<String, ABTestEntity>();
m_activeRuns = new HashSet<String>();
}
public Map<String, ABTestEntity> getEntities() {
......@@ -138,44 +158,62 @@ public class HttpABTestEntityRepository extends ContainerHolder implements ABTes
private void prepareEntity(Case _case, Run run) {
ABTestEntity entity = new ABTestEntity(_case, run);
String strategyKey = String.format("%s:%s:%s", _case.getId(), entity.getGroupStrategyName(),
entity.getGroupStrategyConfiguration());
ABTestGroupStrategy strategy = m_strategies.get(strategyKey);
if (strategy != null) {
entity.setGroupStrategy(strategy);
} else {
try {
strategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
strategy.init(entity);
if (m_strategies.get(run.getId()) != null && m_lastUpdateTime >= run.getLastModifiedDate().getTime()) {
entity.setGroupStrategy(m_strategies.get(run.getId()));
} else {
ABTestGroupStrategy strategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
m_fieldInjector.inject(strategy, run.getGroupstrategyDescriptor());
strategy.init();
entity.setGroupStrategy(strategy);
m_strategies.put(run.getId(), strategy);
}
m_strategies.put(strategyKey, strategy);
} catch (Exception e) {
Cat.logError(e);
if (m_invokeMap.get(run.getId()) != null && m_lastUpdateTime >= run.getLastModifiedDate().getTime()) {
entity.setInvocable(m_invokeMap.get(run.getId()));
} else {
String javaFragement = run.getConditionsFragement();
Invocable inv = (Invocable) m_engine.eval(javaFragement);
entity.setInvocable(inv);
m_invokeMap.put(run.getId(), inv);
}
ABTestEntity origin = HttpABTestEntityRepository.this.m_entities.get(_case.getId());
} catch (Throwable e) {
Cat.logError(e);
ABTestEntity origin = m_entities.get(_case.getName());
if (origin != null) {
entity = origin;
} else {
entity.setDisabled(true);
}
}
}
} finally {
m_entities.put(entity.getName(), entity);
}
}
@Override
public void visitCase(Case _case) {
long maxUpdateTime = -1;
for (Run run : _case.getRuns()) {
m_activeRuns.add(String.valueOf(run.getId()));
if (run.getDomains() != null && run.getDomains().contains(m_domain)) {
prepareEntity(_case, run);
if (run.getLastModifiedDate().getTime() > maxUpdateTime) {
maxUpdateTime = run.getLastModifiedDate().getTime();
}
}
}
if (maxUpdateTime > m_lastUpdateTime) {
m_lastUpdateTime = maxUpdateTime;
}
}
}
......
......@@ -4,7 +4,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface ABTestContext {
public final String DEFAULT_GROUP = "";
public final String DEFAULT_GROUP = "Control";
public ABTestEntity getEntity();
......
package com.dianping.cat.abtest.spi;
import java.util.Date;
import java.util.List;
import javax.script.Invocable;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Condition;
import com.dianping.cat.abtest.model.entity.ConversionRule;
import com.dianping.cat.abtest.model.entity.GroupstrategyDescriptor;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.spi.internal.ABTestCodec;
import com.dianping.cat.message.spi.MessageManager;
public class ABTestEntity {
......@@ -13,6 +21,12 @@ public class ABTestEntity {
private ABTestGroupStrategy m_groupStrategy;
private Invocable m_invocable;
private MessageManager m_messageManager;
private ABTestCodec m_cookieCodec;
private String m_groupStrategyName;
public ABTestEntity() {
......@@ -58,6 +72,22 @@ public class ABTestEntity {
return true;
}
public List<Condition> getConditions() {
return m_run.getConditions() != null ? m_run.getConditions() : null;
}
public String getConditionsFragement() {
return m_run.getConditionsFragement();
}
public List<ConversionRule> getConversionRules() {
return m_run.getConversionRules();
}
public ABTestCodec getCookieCodec() {
return m_cookieCodec;
}
public Date getEndDate() {
return m_run.getEndDate();
}
......@@ -66,14 +96,22 @@ public class ABTestEntity {
return m_groupStrategy;
}
public String getGroupStrategyConfiguration() {
return m_run.getGroupStrategyConfiguration() != null ? m_run.getGroupStrategyConfiguration() : null;
public GroupstrategyDescriptor getGroupStrategyDescriptor() {
return m_run.getGroupstrategyDescriptor() != null ? m_run.getGroupstrategyDescriptor() : null;
}
public String getGroupStrategyName() {
return m_groupStrategyName != null ? m_groupStrategyName : null;
}
public Invocable getInvocable() {
return m_invocable;
}
public MessageManager getMessageManager() {
return m_messageManager;
}
public String getName() {
return m_name;
}
......@@ -123,6 +161,10 @@ public class ABTestEntity {
return true;
}
public void setCookieCodec(ABTestCodec cookieCodec) {
m_cookieCodec = cookieCodec;
}
public void setDisabled(boolean disabled) {
m_run.setDisabled(disabled);
}
......@@ -131,6 +173,14 @@ public class ABTestEntity {
m_groupStrategy = groupStrategy;
}
public void setInvocable(Invocable invocable) {
m_invocable = invocable;
}
public void setMessageManager(MessageManager messageManager) {
m_messageManager = messageManager;
}
public void setName(String name) {
m_name = name;
}
......
......@@ -3,5 +3,5 @@ package com.dianping.cat.abtest.spi;
public interface ABTestGroupStrategy {
public void apply(ABTestContext ctx);
public void init(ABTestEntity entity);
public void init();
}
\ No newline at end of file
......@@ -6,7 +6,10 @@ import java.util.Set;
public interface ABTestCodec {
public String encode(Map<String, Map<String, String>> map);
public String encode(String runId, Map<String, String> map);
public Map<String, String> decode(String value);
public Map<String, Map<String, String>> decode(String value, Set<String> keys);
}
......@@ -12,13 +12,18 @@ public class DefaultABTestCodec implements ABTestCodec {
@Override
protected boolean removeEldestEntry(Entry<String, Map<String, String>> arg0) {
return true;
return size() >= 1000;
}
};
public Map<String, Map<String, String>> decode(String value, Set<String> keys) {
int len = value.length();
Map<String, Map<String, String>> map = new LinkedHashMap<String, Map<String, String>>();
if (len == 0) {
return map;
}
StringBuilder key = new StringBuilder();
StringBuilder name = new StringBuilder();
StringBuilder val = new StringBuilder();
......@@ -120,13 +125,30 @@ public class DefaultABTestCodec implements ABTestCodec {
sb.append('&');
}
boolean first2 = true;
String part = encode(runId, cookielets);
sb.append(part);
}
return sb.toString();
}
@Override
public String encode(String runId, Map<String, String> map) {
StringBuilder sb;
if (map != null && map.size() > 0) {
sb = new StringBuilder(32);
sb.append(runId).append('=');
} else {
return "";
}
boolean first = true;
for (Map.Entry<String, String> e : cookielets.entrySet()) {
if (first2) {
first2 = false;
for (Map.Entry<String, String> e : map.entrySet()) {
if (first) {
first = false;
} else {
sb.append('|');
}
......@@ -134,13 +156,12 @@ public class DefaultABTestCodec implements ABTestCodec {
String key = e.getKey();
String value = e.getValue();
if(value == null){
if (value == null) {
sb.append(key).append(':');
}else{
} else {
sb.append(key).append(':').append(value);
}
}
}
return sb.toString();
}
......
......@@ -2,17 +2,21 @@ package com.dianping.cat.abtest.spi.internal;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.script.Invocable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.dianping.cat.Cat;
import com.dianping.cat.abtest.model.entity.ConversionRule;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.internal.DefaultMessageManager;
public class DefaultABTestContext implements ABTestContext {
private String m_groupName = DEFAULT_GROUP;
......@@ -80,24 +84,35 @@ public class DefaultABTestContext implements ABTestContext {
@Override
public void setGroupName(String groupName) {
m_groupName = groupName;
setCookielet("ab", groupName);
}
public void setGroupStrategy(ABTestGroupStrategy groupStrategy) {
m_groupStrategy = groupStrategy;
}
public void setCookielets(Map<String, String> cookielets) {
m_cookielets = cookielets;
}
public void setup(HttpServletRequest request, HttpServletResponse response, Map<String, String> cookielets) {
m_request = request;
m_response = response;
m_cookielets = cookielets;
if (m_entity.isEligible(new Date())) {
Invocable inv = m_entity.getInvocable();
if (inv != null && m_entity.isEligible(new Date())) {
boolean isAccept = false;
Transaction t = Cat.newTransaction("GroupStrategy", m_entity.getGroupStrategyName());
try {
m_groupStrategy.apply(this);
isAccept = (Boolean) inv.invokeFunction("isEligible", request);
if (isAccept) {
m_groupStrategy.apply(this);
t.setStatus(Message.SUCCESS);
}
} catch (Throwable e) {
t.setStatus(e);
Cat.logError(e);
......@@ -105,5 +120,31 @@ public class DefaultABTestContext implements ABTestContext {
t.complete();
}
}
String actual = (String) request.getAttribute("url-rewrite-original-url");
List<ConversionRule> conversionRules = m_entity.getConversionRules();
if (conversionRules != null) {
for (ConversionRule rule : conversionRules) {
if (actual.equalsIgnoreCase(rule.getText())) {
String key = String.valueOf(m_entity.getRun().getId());
String appendMetricType = m_entity.getCookieCodec().encode(key, m_cookielets);
if (appendMetricType != null && appendMetricType.length() > 0) {
// DefaultMessageManager manager = (DefaultMessageManager) Cat.getManager();
DefaultMessageManager defaultMessageManager = (DefaultMessageManager) m_entity.getMessageManager();
String metricType = defaultMessageManager.getMetricType();
if (metricType != null && metricType.length() > 0) {
defaultMessageManager.setMetricType(metricType + "&" + appendMetricType);
} else {
defaultMessageManager.setMetricType(appendMetricType);
}
}
break;
}
}
}
}
}
......@@ -16,8 +16,6 @@ import com.dianping.cat.abtest.ABTestName;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.message.internal.DefaultMessageManager;
import com.dianping.cat.message.spi.MessageManager;
public class DefaultABTestContextManager extends ContainerHolder implements ABTestContextManager {
private static final String ABTEST_COOKIE_NAME = "ab";
......@@ -25,9 +23,6 @@ public class DefaultABTestContextManager extends ContainerHolder implements ABTe
@Inject
private ABTestEntityManager m_entityManager;
@Inject
private MessageManager m_messageManager;
@Inject
private ABTestCodec m_cookieCodec;
......@@ -49,9 +44,12 @@ public class DefaultABTestContextManager extends ContainerHolder implements ABTe
@Override
public void onRequestBegin(HttpServletRequest request, HttpServletResponse response) {
Entry entry = m_threadLocal.get();
String requestUrl = (String) request.getAttribute("url-rewrite-original-url");
if (requestUrl == null || !requestUrl.contains("ajax")) {
entry.setup(request, response);
}
}
@Override
public void onRequestEnd() {
......@@ -145,10 +143,6 @@ public class DefaultABTestContextManager extends ContainerHolder implements ABTe
String newValue = m_cookieCodec.encode(map);
setCookie(request, response, ABTEST_COOKIE_NAME, newValue);
if (newValue != null && newValue.length() > 0) {
((DefaultMessageManager) m_messageManager).setMetricType(newValue);
}
}
}
}
......@@ -13,13 +13,19 @@ import com.dianping.cat.Cat;
import com.dianping.cat.abtest.ABTestName;
import com.dianping.cat.abtest.repository.ABTestEntityRepository;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.spi.MessageManager;
public class DefaultABTestEntityManager extends ContainerHolder implements ABTestEntityManager, Initializable {
@Inject
private ABTestEntityRepository m_repository;
@Inject
private MessageManager m_messageManager;
@Inject
private ABTestCodec m_cookieCodec;
@Override
public ABTestEntity getEntity(ABTestName name) {
String id = name.getValue();
......@@ -42,27 +48,24 @@ public class DefaultABTestEntityManager extends ContainerHolder implements ABTes
List<ABTestEntity> entitiesList = new ArrayList<ABTestEntity>();
for (ABTestEntity entity : m_repository.getCurrentEntities().values()) {
entity.setMessageManager(m_messageManager);
entity.setCookieCodec(m_cookieCodec);
entitiesList.add(entity);
}
return entitiesList;
}
public Set<String> getActiveRun(){
public Set<String> getActiveRun() {
return m_repository.getActiveRuns();
}
@Override
public void initialize() throws InitializationException {
for (ABTestEntity entity : m_repository.getCurrentEntities().values()) {
try {
ABTestGroupStrategy groupStrategy = lookup(ABTestGroupStrategy.class, entity.getGroupStrategyName());
entity.setGroupStrategy(groupStrategy);
} catch (Exception e) {
Cat.logError(e);
entity.setDisabled(true);
}
entity.setMessageManager(m_messageManager);
entity.setCookieCodec(m_cookieCodec);
}
}
}
\ No newline at end of file
package com.dianping.cat.abtest.spi.internal.conditions;
import javax.servlet.http.HttpServletRequest;
public interface ABTestCondition {
public boolean accept(HttpServletRequest request);
}
package com.dianping.cat.abtest.spi.internal.groupstrategy;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class TrafficDistributionGroupStrategy implements ABTestGroupStrategy {
public static final String ID = "OneVariationStrategy";
@Inject("Control")
private int m_percentControl = 50;
@Inject("Variation-A")
private int m_percentA = 50;
private final int m_total = 100;
private int m_scoreControl = 0;
private int m_scoreA = 0;
@Override
public void apply(ABTestContext ctx) {
m_scoreControl += m_percentControl;
m_scoreA += m_percentA;
if (m_scoreA >= m_scoreControl) {
ctx.setGroupName("A");
m_scoreA -= m_total;
} else {
ctx.setGroupName(ABTestContext.DEFAULT_GROUP);
m_scoreControl -= m_total;
}
}
@Override
public void init() {
if ((m_percentA + m_percentControl) > 100) {
m_percentControl = 50;
m_percentA = 50;
}
}
}
......@@ -8,12 +8,14 @@ import org.unidal.lookup.configuration.Component;
import com.dianping.cat.abtest.repository.ABTestEntityRepository;
import com.dianping.cat.abtest.repository.HttpABTestEntityRepository;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.abtest.spi.internal.ABTestCodec;
import com.dianping.cat.abtest.spi.internal.ABTestContextManager;
import com.dianping.cat.abtest.spi.internal.ABTestEntityManager;
import com.dianping.cat.abtest.spi.internal.DefaultABTestCodec;
import com.dianping.cat.abtest.spi.internal.DefaultABTestContextManager;
import com.dianping.cat.abtest.spi.internal.DefaultABTestEntityManager;
import com.dianping.cat.abtest.spi.internal.groupstrategy.TrafficDistributionGroupStrategy;
import com.dianping.cat.configuration.ClientConfigManager;
import com.dianping.cat.message.spi.MessageManager;
......@@ -23,14 +25,16 @@ public class ABTestComponentConfigurator extends AbstractResourceConfigurator {
List<Component> all = new ArrayList<Component>();
all.add(C(ABTestContextManager.class, DefaultABTestContextManager.class) //
.req(ABTestEntityManager.class, MessageManager.class, ABTestCodec.class));
.req(ABTestEntityManager.class, ABTestCodec.class));
all.add(C(ABTestCodec.class, DefaultABTestCodec.class));
all.add(C(ABTestEntityRepository.class, HttpABTestEntityRepository.class) //
.req(ClientConfigManager.class).config(E("refreshTimeInSeconds").value("60")));
all.add(C(ABTestEntityManager.class, DefaultABTestEntityManager.class) //
.req(ABTestEntityRepository.class));
.req(ABTestEntityRepository.class, MessageManager.class,ABTestCodec.class));
all.add(C(ABTestGroupStrategy.class, TrafficDistributionGroupStrategy.ID, TrafficDistributionGroupStrategy.class));
return all;
}
......
......@@ -37,7 +37,8 @@ public class CatFilter implements Filter {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
Context ctx = new Context((HttpServletRequest) request, (HttpServletResponse) response, chain, m_handlers);
ctx.handle();
......@@ -247,15 +248,27 @@ public class CatFilter implements Filter {
@Override
public void handle(Context ctx) throws IOException, ServletException {
if (ctx.isTop()) {
HttpServletRequest req = ctx.getRequest();
HttpServletResponse res = ctx.getResponse();
ABTestManager.onRequestBegin(req, res);
DefaultMessageManager manager = (DefaultMessageManager) Cat.getManager();
String metricType = manager.getMetricType();
if (metricType != null && metricType.length() > 0) {
Cat.logEvent(ctx.getType(), "ABTest", Message.SUCCESS, metricType);
}
}
try {
ctx.handle();
} finally {
ABTestManager.onRequestEnd();
}
} else {
ctx.handle();
}
}
},
......
......@@ -18,10 +18,39 @@
<attribute name="id" value-type="int" />
<attribute name="start-date" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="end-date" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="created-date" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="last-modified-date" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="disabled" value-type="boolean" />
<element name="creator" value-type="String" />
<element name="domain" value-type="String" type="list" names="domains" />
<element name="group-strategy-configuration" value-type="String" />
<element name="conditions-fragement" value-type="String" />
<entity-ref name="conversion-rule" type="list" names="conversion-rules" xml-indent="true" />
<entity-ref name="condition" type="list" names="conditions" xml-indent="true" />
<entity-ref name="groupstrategy-descriptor" />
</entity>
<entity name="conversion-rule">
<attribute name="name" value-type="String" />
<element name="text" value-type="String" text="true" />
</entity>
<entity name="condition">
<attribute name="name" value-type="String" />
<attribute name="seq" value-type="int" />
<attribute name="operator" value-type="String" />
<attribute name="comparator" value-type="int" />
<element name="text" value-type="String" text="true" />
</entity>
<entity name="groupstrategy-descriptor">
<attribute name="name" value-type="String" />
<attribute name="className" value-type="String" />
<attribute name="fullyQualifiedName" value-type="String" />
<entity-ref name="field" type="list" names="fields" xml-indent="true" />
</entity>
<entity name="field">
<attribute name="name" value-type="String" />
<attribute name="type" value-type="String" />
<attribute name="input-type" value-type="String" />
<attribute name="modifier-name" value-type="String" />
<element name="text" value-type="String" text="true" />
</entity>
</model>
<?xml version="1.0" encoding="UTF-8"?>
<model model-package="com.dianping.cat.abtest.model" enable-sax-parser="true" enable-base-visitor="true">
<model model-package="com.dianping.cat.abtest.model" enable-sax-parser="true" enable-base-visitor="true" enable-json-builder="true">
<entity name="case">
<attribute name="id" value-type="int" key="true" primitive="true"/>
<element name="domain" value-type="String" type="set" names="domains" />
......@@ -8,5 +8,16 @@
<attribute name="id" value-type="int" key="true" primitive="true"/>
<element name="domain" value-type="String" type="set" names="domains" />
</entity>
<entity name="field">
<attribute name="name" value-type="String" />
<attribute name="type" value-type="String" default-value="String" />
<attribute name="input-type" value-type="String" default-value="input" />
<element name="text" value-type="String" text="true" alias="value"/>
</entity>
<entity name="condition">
<attribute name="seq" value-type="int" key="true" primitive="true"/>
<attribute name="comparator" value-type="int" default-value="1" />
<attribute name="operator" value-type="String" default-value="and" />
</entity>
</model>
......@@ -137,9 +137,6 @@
<requirement>
<role>com.dianping.cat.abtest.spi.internal.ABTestEntityManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.message.spi.MessageManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.abtest.spi.internal.ABTestCodec</role>
</requirement>
......@@ -168,7 +165,18 @@
<requirement>
<role>com.dianping.cat.abtest.repository.ABTestEntityRepository</role>
</requirement>
<requirement>
<role>com.dianping.cat.message.spi.MessageManager</role>
</requirement>
<requirement>
<role>com.dianping.cat.abtest.spi.internal.ABTestCodec</role>
</requirement>
</requirements>
</component>
<component>
<role>com.dianping.cat.abtest.spi.ABTestGroupStrategy</role>
<role-hint>OneVariationStrategy</role-hint>
<implementation>com.dianping.cat.abtest.spi.internal.groupstrategy.TrafficDistributionGroupStrategy</implementation>
</component>
</components>
</plexus>
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<?xml version="1.0" encoding="utf-8"?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="status" type="StatusType"/>
<xs:complexType name="StatusType">
<xs:sequence>
......
......@@ -3,7 +3,6 @@ package com.dianping.cat.abtest.demo.roundrobin;
import java.util.concurrent.atomic.AtomicInteger;
import com.dianping.cat.abtest.spi.ABTestContext;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
public class RoundRobinGroupStrategy implements ABTestGroupStrategy {
......@@ -23,6 +22,6 @@ public class RoundRobinGroupStrategy implements ABTestGroupStrategy {
}
@Override
public void init(ABTestEntity entity) {
public void init() {
}
}
......@@ -15,6 +15,9 @@ public class ABTestContextManagerTest extends ComponentTestCase {
check("1=ab:A|cd:B&2=ab:A|cd:B", "1=ab:A|cd:B&2=ab:A|cd:B");
check("1=ab:|cd:B&2=ab:A|cd:B", "1=ab:|cd:B&2=ab:A|cd:B");
check("1=ab:A|cd:B&2=ab:A|cd:", "1=ab:A|cd:B&2=ab:A|cd:");
check("1=ab:|cd:B&2=ab:A|cd:", "1=ab:|cd:B&2=ab:A|cd:");
check("1=ab:|cd:", "1=ab:|cd:");
check("1=ab:|cd:A", "1=ab:|cd:A");
check("", "");
check("1=ab:A|cd:B&2=ab:A|cd:B", "1=ab:A|cd:B&2=ab:A|cd:B", "1", "2");
......@@ -35,6 +38,9 @@ public class ABTestContextManagerTest extends ComponentTestCase {
check2("1=ab:A&2=ab:A|cd:B", "{1=A, 2=A}");
check2("1=ab:A|cd:B&2=ab:|cd:B", "{1=A, 2=}");
check2("1=cd:B&2=ab:A|cd:B", "{2=A}");
check2("30=l:A|ab:A", "{30=A}");
check2("1=ab:|cd:", "{1=}");
check2("1=ab:|cd:A", "{1=}");
check2("", "{}");
}
......@@ -44,4 +50,20 @@ public class ABTestContextManagerTest extends ComponentTestCase {
Assert.assertEquals(expected, map.toString());
}
@Test
public void testCodec3() throws Exception{
check3("1", "1=ab:A");
}
private void check3(String runId, String source) throws Exception {
ABTestCodec codec = lookup(ABTestCodec.class);
Map<String, Map<String, String>> map = codec.decode(source,null);
System.out.println(map.get(runId));
String actual = codec.encode(runId,map.get(runId));
Assert.assertEquals(source, actual);
}
}
......@@ -45,7 +45,7 @@ public class ABTestEntityManagerTest extends ComponentTestCase {
}
if (expectedGroupStrategy != null) {
Assert.assertEquals(expectedGroupStrategyConfiguration, entity.getGroupStrategyConfiguration());
Assert.assertEquals(expectedGroupStrategyConfiguration, entity.getGroupStrategyDescriptor());
}
Assert.assertEquals(expectedEntityName, entity.getName());
......
package com.dianping.cat.abtest.spi.internal;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import org.xml.sax.SAXException;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.transform.DefaultSaxParser;
public class AbtestModelTest {
@Test
public void test() throws SAXException, IOException {
InputStream in = getClass().getResourceAsStream("abtest.xml");
AbtestModel abtest = DefaultSaxParser.parse(in);
System.out.println(abtest);
}
}
<plexus>
<components>
<component>
<role>com.dianping.cat.abtest.spi.interanl.conditions.ABTestCondition</role>
<role-hint>true</role-hint>
<implementation>com.dianping.cat.abtest.spi.internal.ABTestFilterManagerTest$TestCondition</implementation>
<configuration>
<march>true</march>
</configuration>
</component>
<component>
<role>com.dianping.cat.abtest.spi.interanl.conditions.ABTestCondition</role>
<role-hint>false</role-hint>
<implementation>com.dianping.cat.abtest.spi.internal.ABTestFilterManagerTest$TestCondition</implementation>
<configuration>
<march>false</march>
</configuration>
</component>
</components>
</plexus>
......@@ -7,11 +7,33 @@
<domain>TuanGouWeb</domain>
<domain>TuanGouRemote</domain>
<runs>
<run id="1" start-date="2013-04-10 17:00:00" end-date="2013-12-30 18:00:00" disabled="false">
<run id="1" start-date="2013-04-10 17:00:00" end-date="2013-12-30 18:00:00" created-date="2013-04-10 17:00:00" last-modified-date="2013-04-10 17:00:00" disabled="false">
<creator>hao.zhu@dianping.com</creator>
<domain>TuanGouWeb</domain>
<domain>TuanGouRemote</domain>
<group-strategy-configuration><![CDATA[This is the configuration]]></group-strategy-configuration>
<conditions-fragement><![CDATA[This is value]]></conditions-fragement>
<conversion-rules>
<conversion-rule name="Goal-1"><![CDATA[This is value]]></conversion-rule>
<conversion-rule name="Goal-2"><![CDATA[This is value]]></conversion-rule>
<conversion-rule name="Goal-3"><![CDATA[This is value]]></conversion-rule>
</conversion-rules>
<conditions>
<condition name="url" seq="1" operator="and" comparator="1"><![CDATA[This is value]]></condition>
<condition name="url" seq="1" operator="and" comparator="1"><![CDATA[This is value]]></condition>
</conditions>
<groupstrategy-descriptor name="roundrobin" className="GroupStrategyParser" fullyQualifiedName="com.dianping.cat.system.page.abtest.GroupStrategyParser">
<fields>
<field name="package" type="String" input-type="input" modifier-name="m_package">
<![CDATA[This is value]]>
</field>
<field name="class" type="String" input-type="textarea" modifier-name="m_class">
<![CDATA[This is value]]>
</field>
<field name="inject" type="String" input-type="input" modifier-name="m_inject">
<![CDATA[This is value]]>
</field>
</fields>
</groupstrategy-descriptor>
</run>
<run />
</runs>
......
<?xml version="1.0" encoding="UTF-8"?>
<model>
<entity name="abtest-report" root="true">
<attribute name="runId" value-type="int" />
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<entity-ref name="group" type="list" names="groups" />
</entity>
<entity name="group">
<attribute name="name" value-type="String" />
<entity-ref name="goal" type="list" names="goals" />
</entity>
<entity name="goal">
<attribute name="name" value-type="String" />
<attribute name="type" value-type="String" />
<attribute name="count" value-type="int" />
<attribute name="sum" value-type="int" />
<attribute name="avg" value-type="double" />
</entity>
</model>
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<file path="abtest-report-codegen.xml" />
<file path="abtest-report-model.xml" />
</manifest>
<?xml version="1.0" encoding="UTF-8"?>
<model model-package="com.dianping.cat.advanced.abtest.report"
enable-merger="true" enable-sax-parser="true" enable-native-parser="true"
enable-native-builder="true" enable-base-visitor="true">
<entity name="abtest-report" root="true">
<attribute name="runId" value-type="int" />
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<entity-ref name="group" type="list" names="groups" method-find-or-create="true" />
</entity>
<entity name="group">
<attribute name="name" value-type="String" key="true" />
<entity-ref name="goal" type="list" names="goals" method-find-or-create="true"/>
</entity>
<entity name="goal">
<attribute name="name" value-type="String" key="true" />
<attribute name="type" value-type="String" />
<attribute name="count" value-type="int" />
<attribute name="sum" value-type="int" />
<attribute name="avg" value-type="double" />
</entity>
</model>
\ No newline at end of file
......@@ -20,4 +20,6 @@ public class Constants {
public static final String REPORT_UTILIZATION = "utilization";
public static final String REPORT_HEAVY = "heavy";
public static final String ABTEST = "abtest";
}
package com.dianping.cat.abtest.mockit;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.unidal.lookup.ComponentTestCase;
import org.unidal.test.mock.HttpServletRequestMock;
import org.unidal.test.mock.HttpServletResponseMock;
import org.xml.sax.SAXException;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.model.entity.Case;
import com.dianping.cat.abtest.model.entity.Run;
import com.dianping.cat.abtest.model.transform.BaseVisitor;
import com.dianping.cat.abtest.model.transform.DefaultSaxParser;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.abtest.spi.ABTestGroupStrategy;
import com.dianping.cat.abtest.spi.internal.DefaultABTestContext;
public class GroupStrategyTester extends ComponentTestCase {
protected int m_id = 1;
protected String m_url = "abtest.xml";
private ABTestEntity m_entity;
private ABTestGroupStrategy m_groupStrategy;
private Map<String, String> m_cookielets = new LinkedHashMap<String, String>();
private boolean isFirst = true;
public void assertGroupStrategy(HttpServletRequest req, HttpServletResponse res,
String expectedGroup) throws IOException, SAXException,Exception{
DefaultABTestContext context = initContext();
if (m_entity.isEligible(new Date())) {
if(isFirst){
m_groupStrategy.init();
isFirst = false;
}
m_groupStrategy.apply(context);
}
Assert.assertEquals(expectedGroup, context.getGroupName());
}
private DefaultABTestContext initContext() throws IOException, SAXException {
ABTestEntity entity = buildEntity();
DefaultABTestContext context = new DefaultABTestContext(entity);
context.setCookielets(m_cookielets);
if (!entity.isDisabled()) {
ABTestGroupStrategy groupStrategy = entity.getGroupStrategy();
context.setGroupStrategy(groupStrategy);
m_groupStrategy = groupStrategy;
}
m_entity = context.getEntity();
return context;
}
private ABTestEntity buildEntity() throws IOException, SAXException {
InputStream in = getClass().getResourceAsStream(m_url);
AbtestModel abtest = DefaultSaxParser.parse(in);
ABTestVisitor visitor = new ABTestVisitor();
abtest.accept(visitor);
return visitor.getEntity();
}
protected void setId(int id) {
m_id = id;
}
protected void setUrl(String url) {
m_url = url;
}
protected HttpServletRequestMock mockHttpRequest(){
return null;
}
protected HttpServletResponseMock mockHttpResponse(){
return null;
}
private class ABTestVisitor extends BaseVisitor {
private ABTestEntity m_entity;
public ABTestEntity getEntity() {
return m_entity;
}
@Override
public void visitCase(Case _case) {
for (Run run : _case.getRuns()) {
if (run.getId() == m_id) {
m_entity = new ABTestEntity(_case, run);
try {
ABTestGroupStrategy strategy = lookup(
ABTestGroupStrategy.class,
m_entity.getGroupStrategyName());
strategy.init();
m_entity.setGroupStrategy(strategy);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
}
}
......@@ -6,4 +6,7 @@
<model package="com.dianping.cat.configuration.server" name="server">
<sample-model>src/test/resources/com/dianping/cat/message/configuration/server.xml</sample-model>
</model>
<model package="com.dianping.cat.abtest.model" name="abtest">
<sample-model>src/test/resources/com/dianping/cat/abtest/spi/internal/abtest.xml</sample-model>
</model>
</wizard>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>com.dianping.cat</groupId>
<artifactId>parent</artifactId>
......@@ -10,6 +11,12 @@
<name>cat-home</name>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.unidal.eunit</groupId>
<artifactId>EunitTestFwk</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.unidal.framework</groupId>
<artifactId>foundation-service</artifactId>
......@@ -109,6 +116,11 @@
<artifactId>mail</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>com.google.code.javaparser</groupId>
<artifactId>javaparser</artifactId>
<version>1.0.8</version>
</dependency>
</dependencies>
<build>
<finalName>cat</finalName>
......@@ -185,7 +197,8 @@
<configuration>
<manifest><![CDATA[${basedir}/src/main/resources/META-INF/dal/jdbc/report-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/jdbc/alarm-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/jdbc/user-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/jdbc/user-manifest.xml,]]> <![CDATA[${basedir}/src/main/resources/META-INF/dal/jdbc/report-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/jdbc/alarm-manifest.xml,
${basedir}/src/main/resources/META-INF/dal/jdbc/abtest-manifest.xml,]]></manifest>
</configuration>
</execution>
......
......@@ -6,8 +6,18 @@ import java.util.List;
import org.unidal.lookup.configuration.AbstractResourceConfigurator;
import org.unidal.lookup.configuration.Component;
import com.dianping.cat.consumer.advanced.MetricConfigManager;
import com.dianping.cat.core.dal.ProjectDao;
import com.dianping.cat.home.dal.abtest.AbtestDao;
import com.dianping.cat.home.dal.abtest.AbtestReportDao;
import com.dianping.cat.home.dal.abtest.AbtestRunDao;
import com.dianping.cat.home.dal.abtest.GroupStrategyDao;
import com.dianping.cat.system.page.abtest.GroupStrategyParser;
import com.dianping.cat.system.page.abtest.GsonBuilderManager;
import com.dianping.cat.system.page.abtest.ListViewHandler;
import com.dianping.cat.system.page.abtest.ReportHandler;
import com.dianping.cat.system.page.abtest.advisor.ABTestAdvisor;
import com.dianping.cat.system.page.abtest.advisor.DefaultABTestAdvisor;
import com.dianping.cat.system.page.abtest.service.ABTestService;
import com.dianping.cat.system.page.abtest.service.ABTestServiceImpl;
......@@ -18,10 +28,21 @@ public class ABTestComponentConfigurator extends AbstractResourceConfigurator {
List<Component> all = new ArrayList<Component>();
all.add(C(ABTestService.class, ABTestServiceImpl.class)
.req(AbtestDao.class).req(AbtestRunDao.class));
all.add(C(GroupStrategyParser.class));
all.add(C(GsonBuilderManager.class));
all.add(C(ABTestAdvisor.class, DefaultABTestAdvisor.class));
all.add(C(ListViewHandler.class).req(AbtestDao.class).req(AbtestRunDao.class).config(E("pageSize").value("10")));
all.add(C(ReportHandler.class).req(AbtestDao.class).req(AbtestRunDao.class).req(AbtestReportDao.class)
.req(MetricConfigManager.class));
all.add(C(ABTestService.class, ABTestServiceImpl.class).req(AbtestDao.class).req(AbtestRunDao.class)
.req(GroupStrategyDao.class).req(ProjectDao.class).req(GsonBuilderManager.class)
.config(E("refreshTimeInSeconds").value("60")));
return all;
}
}
......@@ -11,8 +11,7 @@ final class CatDatabaseConfigurator extends AbstractJdbcResourceConfigurator {
public List<Component> defineComponents() {
List<Component> all = new ArrayList<Component>();
all.add(defineJdbcDataSourceComponent("cat", "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/cat", "root", "password", "<![CDATA[useUnicode=true&autoReconnect=true]]>"));
all.add(defineJdbcDataSourceComponent("cat", "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/cat", "root", "", "<![CDATA[useUnicode=true&autoReconnect=true]]>"));
defineSimpleTableProviderComponents(all, "cat", com.dianping.cat.home.dal.report._INDEX.getEntityClasses());
defineDaoComponents(all, com.dianping.cat.home.dal.report._INDEX.getDaoClasses());
......
......@@ -13,6 +13,7 @@ import com.dianping.cat.consumer.advanced.ProductLineConfigManager;
import com.dianping.cat.core.dal.DailyGraphDao;
import com.dianping.cat.core.dal.GraphDao;
import com.dianping.cat.core.dal.TaskDao;
import com.dianping.cat.home.dal.abtest.AbtestReportDao;
import com.dianping.cat.home.dal.report.BaselineDao;
import com.dianping.cat.home.dal.report.TopologyGraphDao;
import com.dianping.cat.report.baseline.BaselineConfigManager;
......@@ -25,6 +26,7 @@ import com.dianping.cat.report.page.model.spi.ModelService;
import com.dianping.cat.report.page.transaction.TransactionMergeManager;
import com.dianping.cat.report.service.ReportService;
import com.dianping.cat.report.task.DefaultTaskConsumer;
import com.dianping.cat.report.task.abtest.ABTestReportBuilder;
import com.dianping.cat.report.task.bug.BugReportBuilder;
import com.dianping.cat.report.task.cross.CrossReportBuilder;
import com.dianping.cat.report.task.dependency.DependencyReportBuilder;
......@@ -49,6 +51,7 @@ import com.dianping.cat.report.task.state.StateReportBuilder;
import com.dianping.cat.report.task.transaction.TransactionGraphCreator;
import com.dianping.cat.report.task.transaction.TransactionMerger;
import com.dianping.cat.report.task.transaction.TransactionReportBuilder;
import com.dianping.cat.system.page.abtest.service.ABTestService;
import com.dianping.cat.report.task.utilization.UtilizationReportBuilder;
public class TaskComponentConfigurator extends AbstractResourceConfigurator {
......@@ -120,12 +123,15 @@ public class TaskComponentConfigurator extends AbstractResourceConfigurator {
all.add(C(DependencyReportBuilder.class).req(ReportService.class, TopologyGraphBuilder.class,
TopologyGraphDao.class));
all.add(C(ABTestReportBuilder.class).req(ReportService.class, AbtestReportDao.class,
ProductLineConfigManager.class, ABTestService.class));
all.add(C(ReportFacade.class)//
.req(TransactionReportBuilder.class, EventReportBuilder.class, ProblemReportBuilder.class,
HeartbeatReportBuilder.class, MatrixReportBuilder.class, CrossReportBuilder.class,
SqlReportBuilder.class, StateReportBuilder.class, DependencyReportBuilder.class,
BugReportBuilder.class, ServiceReportBuilder.class, MetricBaselineReportBuilder.class,
HeavyReportBuilder.class, UtilizationReportBuilder.class));
HeavyReportBuilder.class, UtilizationReportBuilder.class, ABTestReportBuilder.class));
return all;
}
......
......@@ -81,7 +81,7 @@ public class MetricDisplayMerger extends BaseVisitor {
private com.dianping.cat.home.dal.abtest.Abtest findAbTest(int id) {
com.dianping.cat.home.dal.abtest.Abtest abtest = null;
if (id >= 0) {
abtest = m_abtestService.getABTestNameByRunId(id);
abtest = m_abtestService.getABTestByRunId(id);
}
if (abtest == null) {
abtest = new com.dianping.cat.home.dal.abtest.Abtest();
......
package com.dianping.cat.report.task.abtest;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.unidal.dal.jdbc.DalException;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.Cat;
import com.dianping.cat.consumer.advanced.ProductLineConfigManager;
import com.dianping.cat.consumer.metric.model.entity.MetricReport;
import com.dianping.cat.helper.TimeUtil;
import com.dianping.cat.home.dal.abtest.AbtestReportDao;
import com.dianping.cat.home.dal.abtest.AbtestReportEntity;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.report.abtest.entity.AbtestReport;
import com.dianping.cat.report.service.ReportService;
import com.dianping.cat.report.task.spi.ReportTaskBuilder;
import com.dianping.cat.system.page.abtest.AbtestStatus;
import com.dianping.cat.system.page.abtest.service.ABTestService;
public class ABTestReportBuilder implements ReportTaskBuilder, Initializable {
@Inject
protected ReportService m_reportService;
@Inject
private AbtestReportDao m_abtestReportDao;
@Inject
private ProductLineConfigManager m_productLineConfigManager;
@Inject
private ABTestService m_abtestService;
private Calendar m_calendar = Calendar.getInstance();
@Override
public boolean buildHourlyTask(String name, String domain, Date period) {
List<AbtestRun> runs = m_abtestService.getAbtestRunByStatus(AbtestStatus.RUNNING);
for (AbtestRun run : runs) {
Set<String> productLineSet = getProductLinesByRunID(run);
buildHourlyTaskInternal(period, productLineSet);
}
return true;
}
private Date resetTime(String period, Date time) {
m_calendar.setTime(time);
m_calendar.set(Calendar.MINUTE, 0);
m_calendar.set(Calendar.SECOND, 0);
m_calendar.set(Calendar.MILLISECOND, 0);
if (period.equals("day")) {
m_calendar.set(Calendar.HOUR_OF_DAY, 0);
}
return m_calendar.getTime();
}
@Override
public boolean buildDailyTask(String name, String domain, Date period) {
throw new UnsupportedOperationException("ABTest report don't support daily report!");
}
@Override
public boolean buildMonthlyTask(String name, String domain, Date period) {
throw new UnsupportedOperationException("ABTest line report don't support monthly report!");
}
@Override
public boolean buildWeeklyTask(String name, String domain, Date period) {
throw new UnsupportedOperationException("ABTest line report don't support weekly report!");
}
@Override
public void initialize() throws InitializationException {
Date now = resetTime("hour", new Date());
List<AbtestRun> runs = m_abtestService.getAbtestRunByStatus(AbtestStatus.RUNNING);
for (AbtestRun run : runs) {
Set<String> productLineSet = getProductLinesByRunID(run);
Date period = getLatestPeriod(now, run.getId());
m_calendar.setTime(period);
while (!period.after(now)) {
buildHourlyTaskInternal(period, productLineSet);
m_calendar.add(Calendar.HOUR, 1);
period = m_calendar.getTime();
}
}
}
private Date getLatestPeriod(Date now, int runId) {
com.dianping.cat.home.dal.abtest.AbtestReport latestReport = null;
Date period = null;
try {
latestReport = m_abtestReportDao.findLatestReportByRunId(runId, AbtestReportEntity.READSET_FULL);
} catch (Exception e) {
// ignore it
}
if (latestReport == null) {
m_calendar.setTime(now);
m_calendar.add(Calendar.DAY_OF_MONTH, -14);
period = m_calendar.getTime();
} else {
period = latestReport.getPeriod();
}
return period;
}
private Set<String> getProductLinesByRunID(AbtestRun run) {
String[] domains = run.getDomains().split(",");
Set<String> productLineSet = new HashSet<String>();
for (String _domain : domains) {
String productLine = m_productLineConfigManager.queryProductLineByDomain(_domain);
if (!productLine.equals("Default")) {
productLineSet.add(productLine);
}
}
return productLineSet;
}
private void buildHourlyTaskInternal(Date period, Set<String> productLineSet) {
MetricReportForABTestVisitor visitor = new MetricReportForABTestVisitor();
for (String productLine : productLineSet) {
MetricReport metricReport = m_reportService.queryMetricReport(productLine, period, new Date(period.getTime()
+ TimeUtil.ONE_HOUR));
metricReport.accept(visitor);
}
Map<Integer, AbtestReport> result = visitor.getReportMap();
for (AbtestReport report : result.values()) {
if (report.getRunId() != -1) {
com.dianping.cat.home.dal.abtest.AbtestReport _report = new com.dianping.cat.home.dal.abtest.AbtestReport();
_report.setRunId(report.getRunId());
_report.setPeriod(period);
_report.setContent(report.toString());
try {
m_abtestReportDao.insert(_report);
} catch (DalException e) {
Cat.logError(e);
}
}
}
}
}
package com.dianping.cat.report.task.abtest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.dianping.cat.consumer.metric.model.entity.Abtest;
import com.dianping.cat.consumer.metric.model.entity.Group;
import com.dianping.cat.consumer.metric.model.entity.MetricItem;
import com.dianping.cat.consumer.metric.model.entity.MetricReport;
import com.dianping.cat.consumer.metric.model.entity.Point;
import com.dianping.cat.consumer.metric.model.transform.BaseVisitor;
import com.dianping.cat.report.abtest.entity.AbtestReport;
import com.dianping.cat.report.abtest.entity.Goal;
import com.dianping.cat.report.abtest.entity.Variation;
public class MetricReportForABTestVisitor extends BaseVisitor {
private Map<Integer, AbtestReport> m_reportMap;
private Map<Integer, HashMap<String, String>> m_metrics;
private String m_id;
private String m_type;
private int m_runId;
private String m_variation;
private Date m_startDate;
private Date m_endDate;
public MetricReportForABTestVisitor() {
m_reportMap = new HashMap<Integer, AbtestReport>();
m_metrics = new HashMap<Integer, HashMap<String, String>>();
}
private AbtestReport findOrCreateAbtestReport(int runId) {
AbtestReport report = m_reportMap.get(runId);
if (report == null) {
report = new AbtestReport();
report.setRunId(runId);
report.setStartTime(m_startDate);
report.setEndTime(m_endDate);
m_reportMap.put(runId, report);
}
return report;
}
public Map<Integer, AbtestReport> getReportMap() {
for (AbtestReport report : m_reportMap.values()) {
HashMap<String, String> map = m_metrics.get(report.getRunId());
for (String metric : map.keySet()) {
for (Variation variation : report.getVariations().values()) {
Goal goal = variation.findOrCreateGoal(metric);
goal.setType(map.get(metric));
}
Goal goal = new Goal();
goal.setName(metric);
report.getGoals().add(goal);
}
}
return m_reportMap;
}
@Override
public void visitAbtest(Abtest abtest) {
try {
m_runId = Integer.parseInt(abtest.getRunId());
} catch (Exception e) {
m_runId = -1;
}
HashMap<String, String> map = m_metrics.get(m_runId);
if (map == null) {
map = new HashMap<String, String>();
m_metrics.put(m_runId, map);
}
map.put(m_id, m_type);
super.visitAbtest(abtest);
}
@Override
public void visitGroup(Group group) {
m_variation = group.getName();
if(m_variation.length() == 0){
m_variation = "Control";
}
super.visitGroup(group);
}
@Override
public void visitMetricItem(MetricItem metricItem) {
m_id = metricItem.getId();
m_type = metricItem.getType();
super.visitMetricItem(metricItem);
}
@Override
public void visitMetricReport(MetricReport metricReport) {
m_startDate = metricReport.getStartTime();
m_endDate = metricReport.getEndTime();
super.visitMetricReport(metricReport);
}
@Override
public void visitPoint(Point point) {
AbtestReport report = findOrCreateAbtestReport(m_runId);
Variation variation = report.findOrCreateVariation(m_variation);
Goal goal = variation.findOrCreateGoal(m_id);
int count = goal.getCount() + point.getCount();
double sum = goal.getSum() + point.getSum();
goal.setType(m_type);
goal.setCount(count);
goal.setSum(sum);
// avg?
}
}
......@@ -24,6 +24,7 @@ import com.dianping.cat.consumer.sql.SqlAnalyzer;
import com.dianping.cat.consumer.state.StateAnalyzer;
import com.dianping.cat.consumer.transaction.TransactionAnalyzer;
import com.dianping.cat.core.dal.Task;
import com.dianping.cat.report.task.abtest.ABTestReportBuilder;
import com.dianping.cat.report.task.bug.BugReportBuilder;
import com.dianping.cat.report.task.cross.CrossReportBuilder;
import com.dianping.cat.report.task.dependency.DependencyReportBuilder;
......@@ -85,6 +86,9 @@ public class ReportFacade implements LogEnabled, Initializable {
@Inject
private MetricBaselineReportBuilder m_metricBaselineReportBuilder;
@Inject
private ABTestReportBuilder m_abtestReportBuilder;
@Inject
private HeavyReportBuilder m_heavyReportBuilder;
......@@ -165,6 +169,7 @@ public class ReportFacade implements LogEnabled, Initializable {
m_reportBuilders.put(Constants.REPORT_SERVICE, m_serviceReportBuilder);
m_reportBuilders.put(Constants.REPORT_HEAVY, m_heavyReportBuilder);
m_reportBuilders.put(Constants.REPORT_UTILIZATION, m_utilizationReportBuilder);
m_reportBuilders.put(Constants.ABTEST, m_abtestReportBuilder);
}
}
package com.dianping.cat.system.page.abtest;
import java.util.Date;
import java.util.Map;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestRun;
public class ABTestReport {
private Abtest m_entity;
private AbtestRun m_run;
private AbtestStatus m_status;
private Map<String, String> m_items;
public ABTestReport(Abtest entity, AbtestRun run) {
m_entity = entity;
m_run = run;
}
public ABTestReport(Abtest entity, AbtestRun run, Date now) {
m_entity = entity;
m_run = run;
setStatus(now);
// TODO m_items setting
}
private void setStatus(Date now) {
m_status = AbtestStatus.calculateStatus(m_run, now);
}
public Abtest getEntity() {
return m_entity;
}
public AbtestRun getRun() {
return m_run;
}
public void setStatus(AbtestStatus status) {
m_status = status;
}
public AbtestStatus getStatus() {
return m_status;
}
public Map<String, String> getItems() {
return m_items;
}
}
......@@ -67,7 +67,6 @@ public enum AbtestStatus {
}
}
public String getStatus() {
return name().toLowerCase();
}
......
......@@ -9,7 +9,17 @@ public enum Action implements org.unidal.web.mvc.Action {
MODEL("model"),
REPORT("report");
REPORT("report"),
AJAX_CREATE("ajax_create"),
AJAX_DETAIL("ajax_detail"),
AJAX_ADDGROUPSTRATEGY("ajax_addGs"),
AJAX_PARSEGROUPSTRATEGY("ajax_parseGs"),
ABTEST_CACULATOR("caculator");
private String m_name;
......
package com.dianping.cat.system.page.abtest;
import java.util.List;
import com.dianping.cat.system.SystemContext;
import com.dianping.cat.system.page.abtest.advisor.ABTestAdvice;
public class Context extends SystemContext<Payload> {
private String m_responseJson;
private List<ABTestAdvice> m_advices;
public String getResponseJson() {
return m_responseJson;
}
public void setResponseJson(String responseJson) {
m_responseJson = responseJson;
}
public void setAdvice(List<ABTestAdvice> advices) {
m_advices = advices;
}
public List<ABTestAdvice> getAdvice(){
return m_advices;
}
}
package com.dianping.cat.system.page.abtest;
import japa.parser.JavaParser;
import japa.parser.ParseException;
import japa.parser.ast.CompilationUnit;
import japa.parser.ast.ImportDeclaration;
import japa.parser.ast.body.FieldDeclaration;
import japa.parser.ast.body.TypeDeclaration;
import japa.parser.ast.body.VariableDeclarator;
import japa.parser.ast.expr.AnnotationExpr;
import japa.parser.ast.visitor.VoidVisitorAdapter;
import java.io.InputStream;
import java.util.List;
import com.dianping.cat.abtest.model.entity.Field;
import com.dianping.cat.abtest.model.entity.GroupstrategyDescriptor;
public class GroupStrategyParser {
public GroupstrategyDescriptor parse(InputStream input) throws ParseException {
final CompilationUnit result = JavaParser.parse(input);
TypeDeclaration type = (TypeDeclaration) result.getTypes().get(0);
final GroupstrategyDescriptor descriptor = new GroupstrategyDescriptor();
String name = type.getName();
descriptor.setClassName(name);
descriptor.setFullyQualifiedName(result.getPackage().getName() + "." + name);
new AnnotationVisitor(descriptor).visit(result, null);
return descriptor;
}
class AnnotationVisitor extends VoidVisitorAdapter<Object> {
private GroupstrategyDescriptor m_descriptor;
private boolean m_isImportInjectClass = false;
public AnnotationVisitor(GroupstrategyDescriptor descriptor) {
m_descriptor = descriptor;
}
public void visit(FieldDeclaration node, Object arg) {
if (m_isImportInjectClass) {
List<AnnotationExpr> annotations = node.getAnnotations();
if (annotations != null) {
for (AnnotationExpr expr : annotations) {
String annotation = expr.toString();
int pos = annotation.indexOf("@Inject");
System.out.println(expr.getData());
if (pos >= 0) {
int begin = annotation.indexOf('"');
int end = annotation.lastIndexOf('"');
String name = annotation.substring(begin + 1, end).trim();
String type = node.getType().toString();
List<VariableDeclarator> modifierName = node.getVariables();
VariableDeclarator firstVar = modifierName.get(0);
Field field = new Field();
field.setName(name);
field.setType(type);
field.setModifierName(firstVar.getId().getName());
if (firstVar.getInit() != null) {
field.setValue(firstVar.getInit().toString());
}
m_descriptor.getFields().add(field);
}
}
}
}
}
public void visit(ImportDeclaration n, Object arg) {
if (n.getName().toString().equals("org.unidal.lookup.annotation.Inject")) {
m_isImportInjectClass = true;
}
}
}
}
package com.dianping.cat.system.page.abtest;
import com.google.gson.FieldNamingStrategy;
import com.google.gson.GsonBuilder;
public class GsonBuilderManager {
private static GsonBuilder s_gsonBuilder = new GsonBuilder();
static {
s_gsonBuilder.setFieldNamingStrategy(new NonPrexFieldNamingStrategy());
}
public GsonBuilder getGsonBuilder() {
return s_gsonBuilder;
}
public static class NonPrexFieldNamingStrategy implements FieldNamingStrategy {
@Override
public String translateName(java.lang.reflect.Field f) {
String name = f.getName();
int pos = name.indexOf('_');
return name.substring(pos + 1);
}
}
}
......@@ -2,15 +2,19 @@ package com.dianping.cat.system.page.abtest;
public enum JspFile {
VIEW("/jsp/system/abtest/abtestAllTest.jsp"),
CREATE("/jsp/system/abtest/abtestCreate.jsp"),
DETAIL("/jsp/system/abtest/abtestDetail.jsp"),
VIEW("/jsp/system/abtest/abtestAllTest.jsp"),
REPORT("/jsp/system/abtest/abtestReport.jsp"),
MODEL("/jsp/system/abtest/abtestModel.jsp");
MODEL("/jsp/system/abtest/abtestModel.jsp"),
AJAX("/jsp/system/abtest/abtestAjax.jsp"),
ABTEST_CACULATOR("/jsp/system/abtest/abtestCaculator.jsp");
private String m_path;
......
......@@ -14,13 +14,22 @@ public class JspViewer extends BaseJspViewer<SystemPage, Action, Context, Model>
return JspFile.VIEW.getPath();
case CREATE:
return JspFile.CREATE.getPath();
case AJAX_ADDGROUPSTRATEGY:
return JspFile.AJAX.getPath();
case AJAX_PARSEGROUPSTRATEGY:
return JspFile.AJAX.getPath();
case AJAX_CREATE:
return JspFile.AJAX.getPath();
case AJAX_DETAIL:
return JspFile.AJAX.getPath();
case DETAIL:
return JspFile.DETAIL.getPath();
case REPORT:
return JspFile.REPORT.getPath();
case MODEL:
return JspFile.MODEL.getPath();
case ABTEST_CACULATOR:
return JspFile.ABTEST_CACULATOR.getPath();
}
throw new RuntimeException("Unknown action: " + action);
......
package com.dianping.cat.system.page.abtest;
import java.util.ArrayList;
import java.util.List;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.Cat;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestDao;
import com.dianping.cat.home.dal.abtest.AbtestEntity;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.home.dal.abtest.AbtestRunDao;
import com.dianping.cat.home.dal.abtest.AbtestRunEntity;
import com.dianping.cat.system.page.abtest.ListViewModel.AbtestItem;
public class ListViewHandler implements SubHandler {
@Inject
private AbtestDao m_abtestDao;
@Inject
private AbtestRunDao m_abtestRunDao;
@Inject
private int m_pageSize = 10;
@Override
public void handle(Context ctx, Model model, Payload payload) {
ListViewModel listViewModel = new ListViewModel();
AbtestStatus status = AbtestStatus.getByName(payload.getStatus(), null);
List<AbtestItem> filterItems = new ArrayList<AbtestItem>();
List<AbtestItem> totalItems = new ArrayList<AbtestItem>();
int createdCount = 0, readyCount = 0, runningCount = 0, terminatedCount = 0, suspendedCount = 0;
List<AbtestRun> runs = new ArrayList<AbtestRun>();
try {
runs = m_abtestRunDao.findAll(AbtestRunEntity.READSET_FULL);
for (AbtestRun run : runs) {
Abtest abtest = m_abtestDao.findByPK(run.getCaseId(), AbtestEntity.READSET_FULL);
AbtestItem item = new AbtestItem(abtest, run);
totalItems.add(item);
if (status != null && item.getStatus() == status) {
filterItems.add(item);
}
switch (item.getStatus()) {
case CREATED:
createdCount++;
break;
case READY:
readyCount++;
break;
case RUNNING:
runningCount++;
break;
case TERMINATED:
terminatedCount++;
break;
case SUSPENDED:
suspendedCount++;
break;
}
}
} catch (Throwable e) {
Cat.logError(e);
}
listViewModel.setCreatedCount(createdCount);
listViewModel.setReadyCount(readyCount);
listViewModel.setRunningCount(runningCount);
listViewModel.setTerminatedCount(terminatedCount);
listViewModel.setSuspendedCount(suspendedCount);
if (status != null) {
totalItems = null;
totalItems = filterItems;
}
int totalSize = totalItems.size();
int totalPages = totalSize % m_pageSize == 0 ? (totalSize / m_pageSize) : (totalSize / m_pageSize + 1);
// safe guarder for pageNum
if (payload.getPageNum() >= totalPages) {
if (totalPages == 0) {
payload.setPageNum(1);
} else {
payload.setPageNum(totalPages);
}
} else if (payload.getPageNum() <= 0) {
payload.setPageNum(1);
}
int fromIndex = (payload.getPageNum() - 1) * m_pageSize;
int toIndex = (fromIndex + m_pageSize) <= totalSize ? (fromIndex + m_pageSize) : totalSize;
listViewModel.setTotalPages(totalPages);
listViewModel.setItems(totalItems.subList(fromIndex, toIndex));
model.setListViewModel(listViewModel);
}
public void setAbtestDao(AbtestDao abtestDao) {
m_abtestDao = abtestDao;
}
public void setAbtestRunDao(AbtestRunDao abtestRunDao) {
m_abtestRunDao = abtestRunDao;
}
public void setPageSize(int pageSize) {
m_pageSize = pageSize;
}
}
package com.dianping.cat.system.page.abtest;
import java.util.Date;
import java.util.List;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestRun;
public class ListViewModel {
private int m_totalPages;
private int m_createdCount;
private int m_readyCount;
private int m_runningCount;
private int m_terminatedCount;
private int m_suspendedCount;
private List<AbtestItem> m_items;
public int getCreatedCount() {
return m_createdCount;
}
public List<AbtestItem> getItems() {
return m_items;
}
public int getReadyCount() {
return m_readyCount;
}
public int getRunningCount() {
return m_runningCount;
}
public int getSuspendedCount() {
return m_suspendedCount;
}
public int getTerminatedCount() {
return m_terminatedCount;
}
public int getTotalPages() {
return m_totalPages;
}
public void setCreatedCount(int createdCount) {
m_createdCount = createdCount;
}
public void setItems(List<AbtestItem> item) {
m_items = item;
}
public void setReadyCount(int readyCount) {
m_readyCount = readyCount;
}
public void setRunningCount(int runningCount) {
m_runningCount = runningCount;
}
public void setSuspendedCount(int suspendedCount) {
m_suspendedCount = suspendedCount;
}
public void setTerminatedCount(int terminatedCount) {
m_terminatedCount = terminatedCount;
}
public void setTotalPages(int totalPages) {
m_totalPages = totalPages;
}
public static class AbtestItem {
private Abtest m_abtest;
private AbtestRun m_run;
public AbtestItem(Abtest abtest, AbtestRun run) {
m_abtest = abtest;
m_run = run;
}
public Abtest getAbtest() {
return m_abtest;
}
public int getCaseId() {
return m_run.getCaseId();
}
public String getConditions() {
return m_run.getConditions();
}
public String getConversionGoals() {
return m_run.getConversionGoals();
}
public String getDescription() {
return m_abtest.getDescription();
}
public String getDomains() {
return m_run.getDomains();
}
public Date getEndDate() {
return m_run.getEndDate();
}
public int getGroupStrategy() {
return m_abtest.getGroupStrategy();
}
public int getId() {
return m_run.getId();
}
public String getName() {
return m_abtest.getName();
}
public String getOwner() {
return m_abtest.getOwner();
}
public AbtestRun getRun() {
return m_run;
}
public Date getStartDate() {
return m_run.getStartDate();
}
public AbtestStatus getStatus() {
return AbtestStatus.calculateStatus(m_run, new Date());
}
public String getStrategyConfiguration() {
return m_run.getStrategyConfiguration();
}
public void setAbtest(Abtest abtest) {
m_abtest = abtest;
}
public void setRun(AbtestRun run) {
m_run = run;
}
}
}
......@@ -6,12 +6,15 @@ import java.util.Map;
import org.unidal.web.mvc.ViewModel;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.abtest.spi.ABTestEntity;
import com.dianping.cat.advanced.metric.config.entity.MetricItemConfig;
import com.dianping.cat.core.dal.Project;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.home.dal.abtest.GroupStrategy;
import com.dianping.cat.report.abtest.entity.AbtestReport;
import com.dianping.cat.system.SystemPage;
import com.dianping.cat.system.page.abtest.ListViewModel.AbtestItem;
import com.dianping.cat.system.page.abtest.ReportHandler.DataSets;
public class Model extends ViewModel<SystemPage, Action, Context> {
private String m_domain;
......@@ -20,27 +23,21 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
private ABTestEntity m_entity;
private List<ABTestReport> m_reports;
private ListViewModel m_listViewModel;
private int m_totalPages;
private Map<String, List<Project>> m_projectMap;
private int m_createdCount;
private Map<String, MetricItemConfig> m_metricConfigItem;
private int m_readyCount;
private List<GroupStrategy> m_groupStrategyList;
private int m_runningCount;
private AbtestItem m_abtest;
private int m_terminatedCount;
private AbtestModel m_abtestModel;
private int m_suspendedCount;
private AbtestReport m_report;
private Map<String, List<Project>> m_projectMap;
private List<GroupStrategy> m_groupStrategyList;
private AbtestDaoModel m_abtest;
private String m_abtestModel;
private List<DataSets> m_dataSets;
private String m_ipAddress;
......@@ -48,16 +45,16 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
super(ctx);
}
public AbtestDaoModel getAbtest() {
public AbtestItem getAbtest() {
return m_abtest;
}
public String getAbtestModel() {
public AbtestModel getAbtestModel() {
return m_abtestModel;
}
public int getCreatedCount() {
return m_createdCount;
public List<DataSets> getDataSets() {
return m_dataSets;
}
public Date getDate() {
......@@ -85,48 +82,36 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
return m_ipAddress;
}
public Map<String, List<Project>> getProjectMap() {
return m_projectMap;
public ListViewModel getListViewModel() {
return m_listViewModel;
}
public int getReadyCount() {
return m_readyCount;
public Map<String, MetricItemConfig> getMetricConfigItem() {
return m_metricConfigItem;
}
public List<ABTestReport> getReports() {
return m_reports;
}
public String getReportType(){
return "";
}
public int getRunningCount() {
return m_runningCount;
}
public int getSuspendedCount() {
return m_suspendedCount;
public Map<String, List<Project>> getProjectMap() {
return m_projectMap;
}
public int getTerminatedCount() {
return m_terminatedCount;
public AbtestReport getReport() {
return m_report;
}
public int getTotalPages() {
return m_totalPages;
public String getReportType() {
return "";
}
public void setAbtest(AbtestDaoModel abtest) {
public void setAbtest(AbtestItem abtest) {
m_abtest = abtest;
}
public void setAbtestModel(String abtestModel) {
public void setAbtestModel(AbtestModel abtestModel) {
m_abtestModel = abtestModel;
}
public void setCreatedCount(int createdCount) {
m_createdCount = createdCount;
public void setDataSets(List<DataSets> dataSets) {
m_dataSets = dataSets;
}
public void setDate(Date date) {
......@@ -149,100 +134,19 @@ public class Model extends ViewModel<SystemPage, Action, Context> {
m_ipAddress = ipAddress;
}
public void setProjectMap(Map<String, List<Project>> projectMap) {
m_projectMap = projectMap;
}
public void setReadyCount(int readyCount) {
m_readyCount = readyCount;
}
public void setReports(List<ABTestReport> reports) {
m_reports = reports;
}
public void setRunningCount(int runningCount) {
m_runningCount = runningCount;
}
public void setSuspendedCount(int suspendedCount) {
m_suspendedCount = suspendedCount;
}
public void setTerminatedCount(int terminatedCount) {
m_terminatedCount = terminatedCount;
}
public void setTotalPages(int totalPages) {
m_totalPages = totalPages;
public void setListViewModel(ListViewModel listViewModel) {
m_listViewModel = listViewModel;
}
public static class AbtestDaoModel {
private Abtest m_abtest;
private AbtestRun m_run;
public AbtestDaoModel(Abtest abtest, AbtestRun abtestRun) {
super();
m_abtest = abtest;
m_run = abtestRun;
}
public Abtest getAbtest() {
return m_abtest;
}
public int getCaseId() {
return m_run.getCaseId();
}
public String getDescription() {
return m_abtest.getDescription();
}
public String getDomains() {
return m_run.getDomains();
}
public Date getEndDate() {
return m_run.getEndDate();
}
public int getGroupStrategy() {
return m_abtest.getGroupStrategy();
public void setMetricConfigItem(Map<String, MetricItemConfig> metricItemConfig) {
m_metricConfigItem = metricItemConfig;
}
public int getId() {
return m_run.getId();
}
public String getName() {
return m_abtest.getName();
}
public String getOwner() {
return m_abtest.getOwner();
}
public AbtestRun getRun() {
return m_run;
}
public Date getStartDate() {
return m_run.getStartDate();
}
public String getStrategyConfiguration() {
return m_run.getStrategyConfiguration();
}
public void setAbtest(Abtest abtest) {
m_abtest = abtest;
public void setProjectMap(Map<String, List<Project>> projectMap) {
m_projectMap = projectMap;
}
public void setRun(AbtestRun run) {
m_run = run;
}
public void setReport(AbtestReport report) {
m_report = report;
}
}
......@@ -4,8 +4,8 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.codehaus.plexus.util.StringUtils;
import org.unidal.web.mvc.ActionContext;
import org.unidal.web.mvc.ActionPayload;
import org.unidal.web.mvc.payload.annotation.FieldMeta;
......@@ -19,11 +19,7 @@ public class Payload implements ActionPayload<SystemPage, Action> {
@FieldMeta("op")
private Action m_action;
@FieldMeta("status")
private String m_status;
@FieldMeta("pageNum")
private int m_pageNum;
/* ===============Abtest================ */
@FieldMeta("name")
private String m_name;
......@@ -43,11 +39,13 @@ public class Payload implements ActionPayload<SystemPage, Action> {
@FieldMeta("domains")
private String[] m_domains;
@FieldMeta("strategyId")
private int m_strategyId;
@FieldMeta("conditions")
private String m_conditions;
@FieldMeta("strategyConfig")
private String m_strategyConfig;
@FieldMeta("goals")
private String m_conversionGoals;
/* ===============Abtest Controls================ */
@FieldMeta("enable")
private boolean m_enableAbtest;
......@@ -58,50 +56,150 @@ public class Payload implements ActionPayload<SystemPage, Action> {
@FieldMeta("ids")
private String m_ids;
@FieldMeta("status")
private String m_status;
@FieldMeta("pageNum")
private int m_pageNum;
@FieldMeta("id")
private int id;
@FieldMeta("lastUpdateTime")
private long m_lastUpdateTime;
/* ===============GroupStrategy================ */
@FieldMeta("strategyId")
private int m_strategyId;
@FieldMeta("strategyConfig")
private String m_strategyConfig;
@FieldMeta("groupStrategyName")
private String m_groupStrategyName;
@FieldMeta("groupStrategyClassName")
private String m_groupStrategyClassName;
@FieldMeta("groupStrategyFullName")
private String m_groupStrategyFullName;
@FieldMeta("groupStrategyDescriptor")
private String m_groupStrategyDescriptor;
@FieldMeta("groupStrategyDescription")
private String m_groupStrategyDescription;
@FieldMeta("srcCode")
private String m_srcCode;
/* ===============Caculator================ */
@FieldMeta("pv")
private int m_pv = 0;
@FieldMeta("conversionRate")
private int m_conversionRate;
/* ===============Report================ */
@FieldMeta("selectMetricType")
private String m_selectMetricType;
@FieldMeta("period")
private String m_period;
private boolean m_addGs;
private String m_startDateStr;
private String m_endDateStr;
private SimpleDateFormat m_sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm");
public void setAction(String action) {
if (action.equalsIgnoreCase(Action.REPORT.getName())) {
m_action = Action.getByName(action, Action.REPORT);
} else if (action.equalsIgnoreCase(Action.CREATE.getName())) {
m_action = Action.getByName(action, Action.CREATE);
} else {
m_action = Action.getByName(action, Action.VIEW);
}
}
private SimpleDateFormat m_dataFormater = new SimpleDateFormat("yyyy-MM-dd hh:mm");
@Override
public Action getAction() {
return m_action;
}
public String getStatus() {
return m_status;
public boolean getAddGs() {
return m_addGs;
}
public void setStatus(String status) {
AbtestStatus abstatus = AbtestStatus.getByName(status, null);
public String getConditions() {
return m_conditions;
}
if (abstatus != null) {
m_status = abstatus.name().toLowerCase();
public String getConversionGoals() {
return m_conversionGoals;
}
public int getConversionRate() {
return m_conversionRate;
}
public String getDescription() {
return m_description;
}
public int getDisableAbtest() {
return m_disableAbtest;
}
public String[] getDomains() {
return m_domains;
}
public Date getEndDate() {
return m_endDate;
}
public String getEndDateStr() {
return m_endDateStr;
}
public String getGroupStrategyClassName() {
return m_groupStrategyClassName;
}
public String getGroupStrategyDescription() {
return m_groupStrategyDescription;
}
public String getGroupStrategyDescriptor() {
return m_groupStrategyDescriptor;
}
public String getGroupStrategyFullName() {
return m_groupStrategyFullName;
}
public String getGroupStrategyName() {
return m_groupStrategyName;
}
public int getId() {
return id;
}
public String[] getIds() {
if (m_ids != null) {
String[] ids = m_ids.split("-");
return ids;
} else {
m_status = "all";
return null;
}
}
public int getPageNum() {
return m_pageNum;
public long getLastUpdateTime() {
return m_lastUpdateTime;
}
public void setPageNum(int pageNum) {
m_pageNum = pageNum;
public String getName() {
return m_name;
}
public String getOwner() {
return m_owner;
}
@Override
......@@ -109,128 +207,199 @@ public class Payload implements ActionPayload<SystemPage, Action> {
return m_page;
}
public int getPageNum() {
return m_pageNum;
}
public String getPeriod() {
return m_period;
}
public int getPv() {
return m_pv;
}
public String getReportType() {
return "";
}
@Override
public void setPage(String page) {
m_page = SystemPage.getByName(page, SystemPage.ABTEST);
public String getSelectMetricType() {
return m_selectMetricType;
}
public String getName() {
return m_name;
public String getSrcCode() {
return m_srcCode;
}
public void setName(String name) {
this.m_name = name;
public Date getStartDate() {
return m_startDate;
}
public String getOwner() {
return m_owner;
public String getStartDateStr() {
return m_startDateStr;
}
public void setOwner(String owner) {
m_owner = owner;
public String getStatus() {
return m_status;
}
public String getDescription() {
return m_description;
public String getStrategyConfig() {
return m_strategyConfig;
}
public void setDescription(String description) {
this.m_description = description;
public int getStrategyId() {
return m_strategyId;
}
public Date getStartDate() {
return m_startDate;
public boolean isEnableAbtest() {
return m_enableAbtest;
}
public Date getEndDate() {
return m_endDate;
public void setAction(String action) {
if (action.equalsIgnoreCase(Action.REPORT.getName())) {
m_action = Action.getByName(action, Action.REPORT);
} else if (action.equalsIgnoreCase(Action.CREATE.getName())) {
m_action = Action.getByName(action, Action.CREATE);
} else {
m_action = Action.getByName(action, Action.VIEW);
}
}
public void setStartDate(String startDate) {
try {
m_startDateStr = startDate;
m_startDate = m_sdf.parse(startDate);
} catch (ParseException e) {
Cat.logError(e);
public void setAddGs(boolean addGs) {
m_addGs = addGs;
}
public void setConditions(String conditions) {
m_conditions = conditions;
}
public void setConversionRate(int conversionRate) {
m_conversionRate = conversionRate;
}
public void setDescription(String description) {
this.m_description = description;
}
public void setDisableAbtest(int disableAbtest) {
m_disableAbtest = disableAbtest;
}
public void setDomains(String[] domains) {
this.m_domains = domains;
}
public void setEndDate(String endDate) {
try {
m_endDateStr = endDate;
m_endDate = m_sdf.parse(endDate);
m_endDate = m_dataFormater.parse(endDate);
} catch (ParseException e) {
Cat.logError(e);
}
}
public String getStartDateStr() {
return m_startDateStr;
public void setEndDate2(Date endDate) {
m_endDate = endDate;
}
public String getEndDateStr() {
return m_endDateStr;
public void setGoals(String goals) {
m_conversionGoals = goals;
}
public String[] getDomains() {
return m_domains;
public void setGroupStrategyClassName(String groupStrategyClassName) {
m_groupStrategyClassName = groupStrategyClassName;
}
public void setDomains(String[] domains) {
this.m_domains = domains;
public void setGroupStrategyDescription(String groupStrategyDescription) {
m_groupStrategyDescription = groupStrategyDescription;
}
public int getStrategyId() {
return m_strategyId;
public void setGroupStrategyDescriptor(String groupStrategyDescriptor) {
m_groupStrategyDescriptor = groupStrategyDescriptor;
}
public void setStrategyId(int strategyId) {
this.m_strategyId = strategyId;
public void setGroupStrategyFullName(String groupStrategyFullName) {
m_groupStrategyFullName = groupStrategyFullName;
}
public String getStrategyConfig() {
return m_strategyConfig;
public void setGroupStrategyName(String groupStrategyName) {
m_groupStrategyName = groupStrategyName;
}
public void setStrategyConfig(String strategyConfig) {
this.m_strategyConfig = strategyConfig;
public void setId(int id) {
this.id = id;
}
public boolean isEnableAbtest() {
return m_enableAbtest;
public void setIds(String ids) {
m_ids = ids;
}
public int getDisableAbtest() {
return m_disableAbtest;
public void setLastUpdateTime(long lastUpdateTime) {
m_lastUpdateTime = lastUpdateTime;
}
public void setDisableAbtest(int disableAbtest) {
m_disableAbtest = disableAbtest;
public void setName(String name) {
this.m_name = name;
}
public String[] getIds() {
if (m_ids != null) {
String[] ids = m_ids.split("-");
return ids;
} else {
return null;
public void setOwner(String owner) {
m_owner = owner;
}
@Override
public void setPage(String page) {
m_page = SystemPage.getByName(page, SystemPage.ABTEST);
}
public void setIds(String ids) {
m_ids = ids;
public void setPageNum(int pageNum) {
m_pageNum = pageNum;
}
public int getId() {
return id;
public void setPeriod(String period) {
m_period = period;
}
public void setId(int id) {
this.id = id;
public void setPv(int pv) {
m_pv = pv;
}
public void setSelectMetricType(String selectMetricType) {
m_selectMetricType = selectMetricType;
}
public void setSrcCode(String srcCode) {
m_srcCode = srcCode;
}
public void setStartDate(String startDate) {
try {
m_startDateStr = startDate;
m_startDate = m_dataFormater.parse(startDate);
} catch (ParseException e) {
Cat.logError(e);
}
}
public void setStartDate2(Date startDate) {
m_startDate = startDate;
}
public void setStatus(String status) {
AbtestStatus abstatus = AbtestStatus.getByName(status, null);
if (abstatus != null) {
m_status = abstatus.name().toLowerCase();
} else {
m_status = "all";
}
}
public void setStrategyConfig(String strategyConfig) {
this.m_strategyConfig = strategyConfig;
}
public void setStrategyId(int strategyId) {
this.m_strategyId = strategyId;
}
@Override
......@@ -238,12 +407,19 @@ public class Payload implements ActionPayload<SystemPage, Action> {
if (m_action == null) {
m_action = Action.VIEW;
}
// 验证doCreate的参数
if (m_action == Action.CREATE && ctx.getHttpServletRequest().getMethod().equalsIgnoreCase("post")) {
if (m_status == null) {
m_status = "all";
}
if (m_disableAbtest != -1 && m_disableAbtest != 1) {
m_disableAbtest = 0;
}
if (ctx.getHttpServletRequest().getMethod().equalsIgnoreCase("post")) {
if (m_action == Action.CREATE) {
try {
Validate.isTrue(StringUtils.isNotBlank(m_name), "'ABTest Name' is required");
// Validate.isTrue(m_startDate != null, "'Start Time' is required, and formated 'yyyy-MM-dd hh:mm'");
// Validate.isTrue(m_endDate != null, "'End Time' is required, and formated 'yyyy-MM-dd hh:mm'");
Validate.isTrue(m_domains != null && m_domains.length > 0, "'Domains' is required, choose one at least");
for (String domain : m_domains) {
Validate.isTrue(StringUtils.isNotBlank(domain), "'Domains' should not be blank");
......@@ -252,13 +428,15 @@ public class Payload implements ActionPayload<SystemPage, Action> {
} catch (IllegalArgumentException e) {
ctx.setException(e);
}
} else if (m_action == Action.AJAX_ADDGROUPSTRATEGY) {
try {
Validate.isTrue(StringUtils.isNotBlank(m_groupStrategyName), "'GroupStrategy Name' is required");
Validate.isTrue(StringUtils.isNotBlank(m_groupStrategyClassName),
"'GroupStrategy ClassName' is required");
} catch (IllegalArgumentException e) {
ctx.setException(e);
}
if (m_status == null) {
m_status = "all";
}
if (m_disableAbtest != -1 && m_disableAbtest != 1) {
m_disableAbtest = 0;
}
}
}
package com.dianping.cat.system.page.abtest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.unidal.lookup.ContainerLoader;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.Cat;
import com.dianping.cat.advanced.metric.config.entity.MetricItemConfig;
import com.dianping.cat.consumer.advanced.MetricConfigManager;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestDao;
import com.dianping.cat.home.dal.abtest.AbtestEntity;
import com.dianping.cat.home.dal.abtest.AbtestReportDao;
import com.dianping.cat.home.dal.abtest.AbtestReportEntity;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.home.dal.abtest.AbtestRunDao;
import com.dianping.cat.home.dal.abtest.AbtestRunEntity;
import com.dianping.cat.report.abtest.entity.AbtestReport;
import com.dianping.cat.report.abtest.entity.Chart;
import com.dianping.cat.report.abtest.entity.Goal;
import com.dianping.cat.report.abtest.entity.Variation;
import com.dianping.cat.report.abtest.transform.BaseVisitor;
import com.dianping.cat.report.abtest.transform.DefaultSaxParser;
import com.dianping.cat.report.task.abtest.ABTestReportBuilder;
import com.dianping.cat.system.page.abtest.ListViewModel.AbtestItem;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class ReportHandler implements SubHandler, Initializable {
@Inject
private AbtestDao m_abtestDao;
@Inject
private AbtestRunDao m_abtestRunDao;
@Inject
private AbtestReportDao m_abtestReportDao;
@Inject
private MetricConfigManager m_configManager;
private static GsonBuilderManager m_gsonBuilderManager = new GsonBuilderManager();
private Calendar m_calendar = Calendar.getInstance();
private DateFormat m_dateFormatForHour = new SimpleDateFormat("MM-dd HH:00");
private DateFormat m_dateFormatForDay = new SimpleDateFormat("MM-dd");
private AbtestReport buildDailyReport(AbtestReport query, String goal, Model model) {
Date startTime = query.getStartTime();
Date endTime = query.getEndTime();
m_calendar.setTime(startTime);
long deltaTime = endTime.getTime() - startTime.getTime();
long day = deltaTime / (24 * 60 * 60 * 1000L);
int step = ((int) day / 7 > 0) ? ((int) day / 7) : 1;
int count = 0;
List<AbtestReport> datas = new ArrayList<AbtestReport>();
List<String> labels = new ArrayList<String>();
m_calendar.add(Calendar.DAY_OF_MONTH, 1);
endTime = m_calendar.getTime();
while (startTime.before(query.getEndTime())) {
if (count % step == 0) {
List<AbtestReport> reports = queryReport(query.getRunId(), startTime, endTime);
AbtestReport report = mergeReport(reports);
datas.add(report);
labels.add(m_dateFormatForDay.format(endTime));
} else {
labels.add("");
}
count++;
startTime = endTime;
m_calendar.add(Calendar.DAY_OF_MONTH, 1);
endTime = m_calendar.getTime();
}
AbtestReport report = mergeReport(datas);
Chart chart = new Chart();
if (goal.length() == 0) {
for (Goal _goal : report.getGoals()) {
goal = _goal.getName();
if (goal.length() > 0) {
break;
}
}
}
String datasets = buildDateSets(datas, goal, report.getVariations().keySet(), model);
Gson gson = m_gsonBuilderManager.getGsonBuilder().create();
String label = gson.toJson(labels, new TypeToken<List<String>>() {
}.getType());
chart.setType("day");
chart.setLabels(label);
chart.setDatasets(datasets);
chart.setGoal(goal);
report.setChart(chart);
report.setRunId(query.getRunId());
report.setStartTime(query.getStartTime());
report.setEndTime(query.getEndTime());
return report;
}
private String buildDateSets(List<AbtestReport> reports, String goal, Set<String> set, Model model) {
List<DataSets> dataSets = new ArrayList<DataSets>();
for (String key : set) {
List<Number> data = new ArrayList<Number>();
for (AbtestReport report : reports) {
if (report.getStartTime() != null) {
Variation variation = report.findVariation(key);
if (variation != null) {
Goal tmp = variation.findGoal(goal);
if (tmp != null) {
if (tmp.getType().equals("C")) {
data.add(tmp.getCount());
} else if (tmp.getType().equals("S")) {
data.add(tmp.getSum());
} else {
data.add(tmp.getAvg());
}
} else {
data.add(0);
}
} else {
data.add(0);
}
} else {
data.add(0);
}
}
DataSets dataSet = DataSetsBuilder.buildDataSets(DataSetColor.getDataSetColor(key), data);
dataSets.add(dataSet);
}
model.setDataSets(dataSets);
Gson gson = m_gsonBuilderManager.getGsonBuilder().create();
return gson.toJson(dataSets, new TypeToken<List<DataSets>>() {
}.getType());
}
private AbtestReport buildHourlyReport(AbtestReport query, String goal, Model model) {
Date startTime = query.getStartTime();
Date endTime = query.getEndTime();
List<AbtestReport> reports = queryReport(query.getRunId(), startTime, endTime);
List<AbtestReport> datas = new ArrayList<AbtestReport>();
List<String> labels = new ArrayList<String>();
long deltaTime = endTime.getTime() - startTime.getTime();
long hour = deltaTime / (60 * 60 * 1000L);
int step = ((int) hour / 12 > 0) ? ((int) hour / 12) : 1;
int size = reports.size();
int count = 0;
int i = 0;
m_calendar.setTime(startTime);
m_calendar.add(Calendar.HOUR, 1);
endTime = m_calendar.getTime();
while (startTime.before(query.getEndTime())) {
if (count % step == 0) {
labels.add(m_dateFormatForHour.format(startTime));
} else {
labels.add("");
}
if (i < size) {
AbtestReport re = reports.get(i);
if (re.getStartTime().equals(startTime)) {
datas.add(re);
i++;
} else {
datas.add(new AbtestReport());
}
}
count++;
startTime = endTime;
m_calendar.add(Calendar.HOUR, 1);
endTime = m_calendar.getTime();
}
AbtestReport report = mergeReport(datas);
Chart chart = new Chart();
if (goal.length() == 0) {
for (Goal _goal : report.getGoals()) {
goal = _goal.getName();
if (goal.length() > 0) {
break;
}
}
}
String datasets = buildDateSets(datas, goal, report.getVariations().keySet(), model);
Gson gson = m_gsonBuilderManager.getGsonBuilder().create();
String label = gson.toJson(labels, new TypeToken<List<String>>() {
}.getType());
chart.setType("hour");
chart.setLabels(label);
chart.setDatasets(datasets);
chart.setGoal(goal);
report.setChart(chart);
report.setRunId(query.getRunId());
report.setStartTime(query.getStartTime());
report.setEndTime(query.getEndTime());
return report;
}
private AbtestReport buildQuery(int runId, Date startTime, Date endTime, String period) {
AbtestReport query = new AbtestReport();
Date now = new Date();
Date newStartTime = null;
Date newEndTime = null;
query.setRunId(runId);
if (startTime == null && endTime == null) {
m_calendar.setTime(now);
if (period.equals("day")) {
m_calendar.add(Calendar.DAY_OF_MONTH, -7);
} else {
m_calendar.add(Calendar.HOUR_OF_DAY, -24);
}
newStartTime = m_calendar.getTime();
newEndTime = now;
} else if (endTime == null) {
m_calendar.setTime(startTime);
if (period.equals("day")) {
m_calendar.add(Calendar.DAY_OF_MONTH, 7);
} else {
m_calendar.add(Calendar.HOUR_OF_DAY, 24);
}
newStartTime = startTime;
newEndTime = m_calendar.getTime();
} else if (startTime == null) {
m_calendar.setTime(endTime);
if (period.equals("day")) {
m_calendar.add(Calendar.DAY_OF_MONTH, -7);
} else {
m_calendar.add(Calendar.HOUR_OF_DAY, -24);
}
newStartTime = m_calendar.getTime();
newEndTime = endTime;
} else {
newStartTime = startTime;
newEndTime = endTime;
}
query.setStartTime(resetTime(period, newStartTime));
query.setEndTime(resetTime(period, newEndTime));
return query;
}
public AbtestReport buildReport(AbtestReport query, String goal, String period, Model model) {
if (period.equals("day")) {
return buildDailyReport(query, goal, model);
} else {
return buildHourlyReport(query, goal, model);
}
}
public Map<String, MetricItemConfig> getMetricItemConfig() {
return m_configManager.getMetricConfig().getMetricItemConfigs();
}
@Override
public void handle(Context ctx, Model model, Payload payload) {
int runId = payload.getId();
try {
AbtestRun run = m_abtestRunDao.findByPK(runId, AbtestRunEntity.READSET_FULL);
Abtest abtest = m_abtestDao.findByPK(run.getCaseId(), AbtestEntity.READSET_FULL);
AbtestItem item = new AbtestItem(abtest, run);
Date startTime = payload.getStartDate();
Date endTime = payload.getEndDate();
String goal = payload.getSelectMetricType();
String period = payload.getPeriod();
if (period == null || (!period.equals("hour") && !period.equals("day"))) {
period = "hour";
}
if (goal == null) {
goal = "";
}
AbtestReport query = buildQuery(runId, startTime, endTime, period);
AbtestReport report = buildReport(query, goal, period, model);
Collections.sort(report.getGoals(), new Comparator<Goal>() {
@Override
public int compare(Goal o1, Goal o2) {
Map<String, MetricItemConfig> metricItemConfig = getMetricItemConfig();
MetricItemConfig item1 = metricItemConfig.get(o1.getName());
MetricItemConfig item2 = metricItemConfig.get(o2.getName());
if (item1.getViewOrder() > item2.getViewOrder()) {
return 1;
} else {
return -1;
}
}
});
model.setAbtest(item);
model.setReport(report);
model.setMetricConfigItem(getMetricItemConfig());
payload.setStartDate2(query.getStartTime());
payload.setEndDate2(query.getEndTime());
} catch (Exception e) {
Cat.logError(e);
e.printStackTrace();
}
}
@Override
public void initialize() throws InitializationException {
try {
ContainerLoader.getDefaultContainer().lookup(ABTestReportBuilder.class);
} catch (ComponentLookupException e) {
Cat.logError(e);
}
}
private AbtestReport mergeReport(List<AbtestReport> reports) {
AbtestReport result = new AbtestReport();
AbtestReportVisitor visitor = new AbtestReportVisitor(result);
for (AbtestReport report : reports) {
if (report.getStartTime() != null) {
visitor.visitAbtestReport(report);
result.setRunId(report.getRunId());
if (result.getStartTime() == null || result.getEndTime() == null) {
result.setStartTime(report.getStartTime());
result.setEndTime(report.getEndTime());
}
if (result.getStartTime().after(report.getStartTime())) {
result.setStartTime(report.getStartTime());
}
if (result.getEndTime().before(report.getEndTime())) {
result.setEndTime(report.getEndTime());
}
}
}
return result;
}
private List<AbtestReport> queryReport(int runId, Date startTime, Date endTime) {
List<AbtestReport> results = new ArrayList<AbtestReport>();
try {
List<com.dianping.cat.home.dal.abtest.AbtestReport> reports = m_abtestReportDao.findByRunIdDuration(runId,
startTime, endTime, AbtestReportEntity.READSET_FULL);
for (com.dianping.cat.home.dal.abtest.AbtestReport report : reports) {
String content = report.getContent();
AbtestReport result = DefaultSaxParser.parse(content);
results.add(result);
}
} catch (Exception e) {
Cat.logError(e);
}
return results;
}
private Date resetTime(String period, Date time) {
m_calendar.setTime(time);
m_calendar.set(Calendar.MINUTE, 0);
m_calendar.set(Calendar.SECOND, 0);
m_calendar.set(Calendar.MILLISECOND, 0);
if (period.equals("day")) {
m_calendar.set(Calendar.HOUR_OF_DAY, 0);
}
return m_calendar.getTime();
}
class AbtestReportVisitor extends BaseVisitor {
private AbtestReport m_report;
private String m_variation = "";
private Set<String> m_variationSet;
private Map<String, MetricItemConfig> m_metricItemConfig;
public AbtestReportVisitor(AbtestReport report) {
m_report = report;
m_variationSet = new HashSet<String>();
m_variationSet.add("Control");
m_variationSet.add("A");
m_variationSet.add("B");
m_variationSet.add("C");
m_metricItemConfig = getMetricItemConfig();
}
public AbtestReport getReport() {
return m_report;
}
@Override
public void visitAbtestReport(AbtestReport abtestReport) {
for (Goal goal : abtestReport.getGoals()) {
String name = goal.getName();
MetricItemConfig tmp = m_metricItemConfig.get(name);
if (tmp.getViewOrder() > 0) {
m_report.findOrCreateGoal(name);
}
}
for (Variation variation : abtestReport.getVariations().values()) {
if (m_variationSet.contains(variation.getName())) {
visitVariation(variation);
}
}
}
@Override
public void visitGoal(Goal goal) {
String name = goal.getName();
if (m_variation != null && m_variation.length() > 0) {
Variation variation = m_report.findOrCreateVariation(m_variation);
Goal result = variation.findOrCreateGoal(name);
result.setType(goal.getType());
result.setCount(result.getCount() + goal.getCount());
result.setSum(result.getSum() + goal.getSum());
// avg?
}
}
@Override
public void visitVariation(Variation variation) {
m_variation = variation.getName();
if (m_variation != null && m_variation.length() > 0) {
m_report.findOrCreateVariation(m_variation);
for (Goal goal : variation.getGoals().values()) {
visitGoal(goal);
}
}
}
}
enum DataSetColor {
CONTROL(new DataSets("rgba(70, 136, 71,0.2)", "rgba(70, 136, 71,1)", "rgba(70, 136, 71, 1)", "#468847", null)),
A(new DataSets("rgba(58, 135, 173, 0.3)", "rgba(58, 135, 173, 1)", "rgba(58, 135, 173, 1)", "#3a87ad", null)),
B(new DataSets("rgba(185, 74, 72, 0.2)", "rgba(185, 74, 72, 1)", "rgba(185, 74, 72, 1)", "#b94a48", null)),
C(new DataSets("rgba(248, 148, 6,0.2)", "rgba(248, 148, 6,1)", "rgba(248, 148, 6, 1)", "#f89406", null));
public static DataSetColor getDataSetColor(String variation) {
if (variation.equalsIgnoreCase("control")) {
return CONTROL;
} else if (variation.equalsIgnoreCase("A")) {
return A;
} else if (variation.equalsIgnoreCase("B")) {
return B;
} else if (variation.equalsIgnoreCase("C")) {
return C;
} else {
return CONTROL;
}
}
private DataSets m_dataSets;
private DataSetColor(DataSets dataSets) {
m_dataSets = dataSets;
}
public DataSets getDataSets() {
return m_dataSets;
}
}
@SuppressWarnings("unused")
public static class DataSets {
private String m_fillColor;
private String m_strokeColor;
private String m_pointColor;
private String m_pointStrokeColor;
private List<Number> m_data;
public DataSets() {
}
public DataSets(String fillColor, String strokeColor, String pointColor, String pointStrokeColor,
List<Number> data) {
super();
m_fillColor = fillColor;
m_strokeColor = strokeColor;
m_pointColor = pointColor;
m_pointStrokeColor = pointStrokeColor;
m_data = data;
}
public String getPointStrokeColor() {
return m_pointStrokeColor;
}
public void setData(List<Number> data) {
m_data = data;
}
public String toJson() {
Gson gson = m_gsonBuilderManager.getGsonBuilder().create();
return gson.toJson(this, DataSets.class);
}
}
static class DataSetsBuilder {
public static DataSets buildDataSets(DataSetColor color, List<Number> number) {
DataSets dataSets = color.getDataSets();
dataSets.setData(number);
return dataSets;
}
}
}
package com.dianping.cat.system.page.abtest;
public interface SubHandler {
public void handle(Context ctx, Model model, Payload payload);
}
package com.dianping.cat.system.page.abtest.advisor;
public class ABTestAdvice {
private double m_ctrOfVariationA = 0.00;
private double m_ctrOfVariationB = 0.00;
private double m_difference = 0.00;
private int m_sizePerGroup = 0;
private int m_totalParticipants = 0;
private double m_confidenceInterval = 0.95;
private int m_days = 0;
public double getConfidenceInterval() {
return m_confidenceInterval;
}
public double getCtrOfVariationA() {
return m_ctrOfVariationA;
}
public double getCtrOfVariationB() {
return m_ctrOfVariationB;
}
public int getDays() {
return m_days;
}
public double getDifference() {
return m_difference;
}
public int getSizePerGroup() {
return m_sizePerGroup;
}
public int getTotalParticipants() {
return m_totalParticipants;
}
public void setConfidenceInterval(double confidenceInterval) {
m_confidenceInterval = confidenceInterval;
}
public void setCtrOfVariationA(double ctrOfVariationA) {
m_ctrOfVariationA = ctrOfVariationA;
}
public void setCtrOfVariationB(double ctrOfVariationB) {
m_ctrOfVariationB = ctrOfVariationB;
}
public void setDays(int days) {
m_days = days;
}
public void setDifference(double difference) {
m_difference = difference;
}
public void setSizePerGroup(int sizePerGroup) {
m_sizePerGroup = sizePerGroup;
}
public void setTotalParticipants(int totalParticipants) {
m_totalParticipants = totalParticipants;
}
}
\ No newline at end of file
package com.dianping.cat.system.page.abtest.advisor;
import java.util.List;
public interface ABTestAdvisor {
public List<ABTestAdvice> offer(double actualCTR, double expectedCTR);
public void setConfidenceInterval(double interval);
public void setCurrentPv(int pv);
public void setDifference(double difference);
}
package com.dianping.cat.system.page.abtest.advisor;
public class ABTestEvaluator {
public float getConfidence(float zscore) {
if (zscore < -3.89) {
return 0;
} else if (zscore > 3.89) {
return 1;
}
float ret = 0;
float temp = -3.89f;
while (temp <= zscore) {
ret += 0.0001f * fx(temp);
temp += 0.0001f;
}
return ret;
}
public double getConversionRate(double total, double real) {
return real / total;
}
private float fx(float zscore) {
float ret = 0;
double a = 1.0 / Math.sqrt(Math.PI * 2);
a = a * Math.pow(Math.E, -0.5 * Math.pow(zscore, 2));
ret = (float) a;
return ret;
}
/*
* 95% confidence interval
*/
public double getSampleSize(double crActual, double crExpected) {
double zscore = 1.65;
double diff = crActual - crExpected;
return zscore * zscore * (crActual + crExpected - crActual * crActual - crExpected * crExpected) / (diff * diff);
}
public double getStandardError(double conversionRate, double size) {
return Math.sqrt(conversionRate * (1 - conversionRate) / size);
}
public double getZsore(double cr1, double cr2, double se1, double se2) {
return Math.abs((cr1 - cr2) / Math.sqrt(se1 * se1 + se2 * se2));
}
}
package com.dianping.cat.system.page.abtest.advisor;
public abstract class AbstractABTestAdvisor implements ABTestAdvisor {
protected double m_confidenceInterval = 0.95;
protected int m_pv = 0;
protected double m_difference = 0.01;
@Override
public void setConfidenceInterval(double interval) {
m_confidenceInterval = interval;
}
@Override
public void setCurrentPv(int pv) {
m_pv = pv;
}
@Override
public void setDifference(double difference) {
m_difference = difference;
}
}
package com.dianping.cat.system.page.abtest.advisor;
import java.util.ArrayList;
import java.util.List;
public class DefaultABTestAdvisor extends AbstractABTestAdvisor implements ABTestAdvisor {
@Override
public List<ABTestAdvice> offer(double actualCTR, double expectedCTR) {
List<ABTestAdvice> advices = new ArrayList<ABTestAdvice>();
if (actualCTR >= expectedCTR) {
return advices;
}
double increaseCtr = actualCTR;
do {
ABTestAdvice advice = new ABTestAdvice();
advice.setConfidenceInterval(m_confidenceInterval);
advice.setDifference(m_difference);
advice.setCtrOfVariationA(actualCTR);
increaseCtr += m_difference;
advice.setCtrOfVariationB(increaseCtr);
int sizePerGroup = getSampleSize(actualCTR, increaseCtr);
advice.setSizePerGroup(sizePerGroup);
advice.setTotalParticipants(sizePerGroup * 2);
if (m_pv != 0) {
int days = (sizePerGroup * 2) % m_pv == 0 ? (sizePerGroup * 2) / m_pv : (sizePerGroup * 2) / m_pv + 1;
advice.setDays(days);
}
advices.add(advice);
} while (increaseCtr < expectedCTR);
return advices;
}
private int getSampleSize(double crActual, double crExpected) {
double zscore = 1.65f;
double diff = crActual - crExpected;
double result = zscore * zscore * (crActual + crExpected - crActual * crActual - crExpected * crExpected)
/ (diff * diff);
return (int) result + 1;
}
}
package com.dianping.cat.system.page.abtest.conditions;
import java.util.List;
public interface ScriptProvider {
public List<String> actions();
}
package com.dianping.cat.system.page.abtest.conditions;
import java.util.ArrayList;
import java.util.List;
import com.dianping.cat.abtest.model.entity.Condition;
public class URLScriptProvider implements ScriptProvider {
private String m_actual = "actual";
@Override
public List<String> actions() {
List<String> actions = new ArrayList<String>();
actions.add("Is equals to (case insens.)");
actions.add("Is not equals to (case insens.)");
actions.add("Is equals to (case sens.)");
actions.add("Is not equals to (case sens.)");
actions.add("Is equals to (case insens.)");
actions.add("Marches Regex (case insens.)");
actions.add("Marches Regex (case sens.)");
actions.add("contains");
actions.add("does not contain");
return actions;
}
private String equalsByCaseInsens(String expectedUrl) {
return String.format("%s.equalsIgnoreCase(\"%s\")", m_actual, expectedUrl);
}
private String notEqualsByCaseInsens(String expectedUrl) {
return String.format("!%s.equalsIgnoreCase(\"%s\")", m_actual, expectedUrl);
}
private String equalsByCaseSens(String expectedUrl) {
return String.format("%s.equals(\"%s\")", m_actual, expectedUrl);
}
private String notEqualsByCaseSens(String expectedUrl) {
return String.format("!%s.equals(\"%s\")", m_actual, expectedUrl);
}
private String marcherByCaseInsens(String expectedUrl) {
int pos = expectedUrl.indexOf('*');
String subUrl = expectedUrl.substring(0, pos);
return String.format("%s.toLowerCase().startsWith(\"%s\".toLowerCase())", m_actual, subUrl);
}
private String marcherByCaseSens(String expectedUrl) {
int pos = expectedUrl.indexOf('*');
if(pos > -1){
String subUrl = expectedUrl.substring(0, pos);
return String.format("%s.startsWith(\"%s\")", m_actual, subUrl);
}else{
return "false";
}
}
private String contain(String expectedUrl){
return String.format("%s.indexOf(\"%s\") > -1", m_actual, expectedUrl);
}
private String notContain(String expectedUrl){
return String.format("%s.indexOf(\"%s\") == -1", m_actual, expectedUrl);
}
public String getFragement(Condition condition) {
String expectedUrl = condition.getText();
switch (condition.getComparator()) {
case 1:
return equalsByCaseInsens(expectedUrl);
case 2:
return notEqualsByCaseInsens(expectedUrl);
case 3:
return equalsByCaseSens(expectedUrl);
case 4:
return notEqualsByCaseSens(expectedUrl);
case 5:
return marcherByCaseInsens(expectedUrl);
case 6:
return marcherByCaseSens(expectedUrl);
case 7:
return contain(expectedUrl);
case 8:
return notContain(expectedUrl);
}
return "false";
}
}
package com.dianping.cat.system.page.abtest.service;
import java.util.List;
import java.util.Map;
import com.dianping.cat.abtest.model.entity.AbtestModel;
import com.dianping.cat.core.dal.Project;
import com.dianping.cat.home.dal.abtest.Abtest;
import com.dianping.cat.home.dal.abtest.AbtestRun;
import com.dianping.cat.home.dal.abtest.GroupStrategy;
import com.dianping.cat.system.page.abtest.AbtestStatus;
public interface ABTestService {
public Abtest getABTestNameByRunId(int id);
public Abtest getABTestByRunId(int id);
public AbtestModel getAbtestModelByStatus(AbtestStatus... status);
public AbtestModel getAbtestModelByRunID(int runId);
public GroupStrategy getGroupStrategyById(int id);
public AbtestRun getAbtestRunById(int id);
public List<AbtestRun> getAbtestRunByStatus(AbtestStatus status);
public List<GroupStrategy> getAllGroupStrategies();
public Map<String, List<Project>> getAllProjects();
public void refresh();
public void setModified();
public long getModifiedTime();
}
......@@ -42,7 +42,45 @@
</query>
</query-defs>
</entity>
<entity name="abtest-run" table="abtest_run" alias="ar">
<entity name="abtest-report" table="abtest_report" alias="ar">
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="run-id" field="run_id" value-type="int" length="10" />
<member name="period" field="period" value-type="Date" />
<member name="content" field="content" value-type="String" length="65535" />
<member name="creation-date" field="creation_date" value-type="Date" />
<var name="key-id" value-type="int" key-member="id" />
<primary-key name="PRIMARY" members="id" />
<readsets>
<readset name="FULL" all="true" />
</readsets>
<updatesets>
<updateset name="FULL" all="true" />
</updatesets>
<query-defs>
<query name="find-by-PK" type="SELECT">
<param name="key-id" />
<statement><![CDATA[SELECT <FIELDS/>
FROM <TABLE/>
WHERE <FIELD name='id'/> = ${key-id}]]></statement>
</query>
<query name="insert" type="INSERT">
<statement><![CDATA[INSERT INTO <TABLE/>(<FIELDS/>)
VALUES(<VALUES/>)]]></statement>
</query>
<query name="update-by-PK" type="UPDATE">
<param name="key-id" />
<statement><![CDATA[UPDATE <TABLE/>
SET <FIELDS/>
WHERE <FIELD name='id'/> = ${key-id}]]></statement>
</query>
<query name="delete-by-PK" type="DELETE">
<param name="key-id" />
<statement><![CDATA[DELETE FROM <TABLE/>
WHERE <FIELD name='id'/> = ${key-id}]]></statement>
</query>
</query-defs>
</entity>
<entity name="abtest-run" table="abtest_run" alias="ar2">
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="creator" field="creator" value-type="String" length="45" />
<member name="case-id" field="case_id" value-type="int" length="10" nullable="false" />
......@@ -50,6 +88,9 @@
<member name="end-date" field="end_date" value-type="Date" />
<member name="disabled" field="disabled" value-type="int" length="3" nullable="false" />
<member name="domains" field="domains" value-type="String" length="100" nullable="false" />
<member name="conditions" field="conditions" value-type="String" length="65535" />
<member name="java-fragement" field="java_fragement" value-type="String" length="65535" />
<member name="conversion-goals" field="conversion_goals" value-type="String" length="65535" />
<member name="strategy-configuration" field="strategy_configuration" value-type="String" length="65535" />
<member name="creation-date" field="creation_date" value-type="Date" nullable="false" />
<member name="modified-date" field="modified_date" value-type="Date" nullable="false" />
......@@ -88,9 +129,9 @@
<entity name="group-strategy" table="group_strategy" alias="gs">
<member name="id" field="id" value-type="int" length="10" nullable="false" key="true" auto-increment="true" />
<member name="name" field="name" value-type="String" length="100" nullable="false" />
<member name="alias" field="alias" value-type="String" length="100" nullable="false" />
<member name="classname" field="classname" value-type="String" length="100" nullable="false" />
<member name="configuration" field="configuration" value-type="String" length="65535" />
<member name="class-name" field="class_name" value-type="String" length="100" nullable="false" />
<member name="fully-qualified-name" field="fully_qualified_name" value-type="String" length="100" nullable="false" />
<member name="descriptor" field="descriptor" value-type="String" length="65535" />
<member name="status" field="status" value-type="int" length="3" nullable="false" />
<member name="description" field="description" value-type="String" length="512" />
<var name="key-id" value-type="int" key-member="id" />
......
......@@ -26,7 +26,10 @@
<member name="start-date" field="start_date" value-type="Date" />
<member name="end-date" field="end_date" value-type="Date" />
<member name="domains" field="domains" value-type="String" length="100" nullable="false" />
<member name="strategy-configuration" field="strategy_configuration" value-type="String" length="1024" />
<member name="conditions" field="conditions" value-type="String" length="65535" />
<member name="java-fragement" field="java_fragement" value-type="String" length="65535" />
<member name="conversion-goals" field="conversion_goals" value-type="String" length="65535" />
<member name="strategy-configuration" field="strategy_configuration" value-type="String" length="65535" />
<member name="modified-date" insert-expr="NOW()" update-expr="NOW()" />
</updateset>
</updatesets>
......@@ -58,6 +61,50 @@
WHERE <FIELD name='status'/> = ${status}
]]></statement>
</query>
<query name="find-by-name" type="SELECT" multiple="true">
<param name="name" />
<statement><![CDATA[
SELECT <FIELDS/>
FROM <TABLE/>
WHERE <FIELD name='name'/> = ${name}
]]></statement>
</query>
<query name="find-all" type="SELECT" multiple="true">
<statement><![CDATA[
SELECT <FIELDS/>
FROM <TABLE/>
]]></statement>
</query>
</query-defs>
</entity>
<entity name="abtest-report" table="abtest_report" alias="ar">
<member name="creation-date" insert-expr="NOW()" />
<var name="start-date" value-type="Date" />
<var name="end-date" value-type="Date" />
<query-defs>
<query name="find-by-runId-duration" type="SELECT" multiple="true">
<param name="run-id" />
<param name="start-date" />
<param name="end-date" />
<statement><![CDATA[
SELECT <FIELDS/>
FROM <TABLE/>
WHERE <FIELD name='run-id'/> = ${run-id}
AND <FIELD name='period'/> >= ${start-date}
AND <FIELD name='period'/> <= ${end-date}
]]></statement>
</query>
</query-defs>
<query-defs>
<query name="find-latest-report-by-runId" type="SELECT">
<param name="run-id" />
<statement><![CDATA[
SELECT <FIELDS/>
FROM <TABLE/>
WHERE <FIELD name='run-id'/> = ${run-id}
ORDER BY <FIELD name='period'/> DESC LIMIT 1
]]></statement>
</query>
</query-defs>
</entity>
</entities>
......
......@@ -51,6 +51,7 @@
<member name="creation-date" field="creation_date" value-type="Date" nullable="false" />
<var name="key-id" value-type="int" key-member="id" />
<primary-key name="PRIMARY" members="id" />
<index name="ix_date_domain" members="date ASC, domain ASC" />
<readsets>
<readset name="FULL" all="true" />
</readsets>
......
<?xml version="1.0" encoding="UTF-8"?>
<model>
<entity name="abtest-report" root="true">
<attribute name="runId" value-type="int" />
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<entity-ref name="goal" type="list" names="goals" xml-indent="true" />
<entity-ref name="chart" />
<entity-ref name="variation" type="list" names="variations" />
</entity>
<entity name="goal">
<attribute name="name" value-type="String" />
<attribute name="type" value-type="String" />
<attribute name="count" value-type="int" />
<attribute name="sum" value-type="double" />
<attribute name="avg" value-type="double" />
</entity>
<entity name="chart">
<attribute name="goal" value-type="String" />
<attribute name="type" value-type="String" />
<element name="labels" value-type="String" />
<element name="datasets" value-type="String" />
</entity>
<entity name="variation">
<attribute name="name" value-type="String" />
<entity-ref name="goal" type="list" names="goals" />
</entity>
</model>
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<file path="abtest-report-codegen.xml" />
<file path="abtest-report-model.xml" />
</manifest>
<?xml version="1.0" encoding="UTF-8"?>
<model model-package="com.dianping.cat.report.abtest"
enable-merger="true" enable-sax-parser="true" enable-native-parser="true"
enable-native-builder="true" enable-base-visitor="true">
<entity name="abtest-report" root="true">
<attribute name="runId" value-type="int" />
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<entity-ref name="goal" type="list" names="goals"
xml-indent="true" method-find-or-create="true" />
<entity-ref name="chart" method-find-or-create="true" />
<entity-ref name="variation" type="map" names="variations"
method-find-or-create="true" />
</entity>
<entity name="variation">
<attribute name="name" value-type="String" key="true" />
<entity-ref name="goal" type="map" names="goals"
method-find-or-create="true" />
</entity>
<entity name="goal">
<attribute name="name" value-type="String" key="true" />
<attribute name="type" value-type="String" default-value="" />
<attribute name="count" value-type="int" default-value="0" />
<attribute name="sum" value-type="double" default-value="0.0" />
<attribute name="avg" value-type="double" default-value="0.0" />
</entity>
</model>
......@@ -5,7 +5,7 @@
<driver>com.mysql.jdbc.Driver</driver>
<url>jdbc:mysql://127.0.0.1:3306/cat</url>
<user>root</user>
<password>password</password>
<password></password>
<properties>useUnicode=true&amp;autoReconnect=true</properties>
</datasource>
<group name="report" package="com.dianping.cat.home.dal.report">
......@@ -22,6 +22,7 @@
<table name="abtest"/>
<table name="abtest_run"/>
<table name="group_strategy"/>
<table name="abtest_report"/>
</group>
</jdbc>
<jdbc package="com.dianping.cat.home.dal" name="user">
......
......@@ -9,6 +9,9 @@
<model package="com.dianping.cat.home.info" name="info">
<sample-model>src/test/resources/com/dianping/cat/system/config/info.xml</sample-model>
</model>
<model package="com.dianping.cat.report.abtest" name="abtest-report">
<sample-model>src/test/resources/com/dianping/cat/report/page/abtest/abtest-report.xml</sample-model>
</model>
<model package="com.dianping.cat.home.exception-threshold" name="exception-threshold-config">
<sample-model>src/test/resources/com/dianping/cat/report/page/dependency/exception-threshold-config.xml</sample-model>
</model>
......
package com.dianping.cat.abtest.conditions;
import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import com.dianping.cat.abtest.spi.internal.conditions.ABTestCondition;
public class TrafficFilter {
<#assign count = 1>
<#list run.conditions as condition>
private Condition${count} m_condition${count} = new Condition${count}();
<#assign count = count + 1>
</#list>
<#assign count1 = 1>
<#assign isFirst1 = 1>
<#assign isFirst2 = 1>
public boolean isEligible(HttpServletRequest request) {
if(
<#list run.conditions as condition>
<#if isFirst1 = 0>
<#if condition.seq = 4>
<#if isFirst2 = 0>
)
</#if>
</#if>
<#if operator = 0>
&&
</#if>
<#if operator = 1>
||
</#if>
<#if condition.seq = 3>
<#if isFirst2 = 1>
(
<#assign isFirst2 = 0>
</#if>
</#if>
</#if>
m_condition${count1}.accept(request)
<#if condition.operator = "and">
<#assign operator = 0>
</#if>
<#if condition.operator = "or">
<#assign operator = 1>
</#if>
<#assign isFirst1 = 0>
<#assign count1 = count1 + 1>
</#list>
) {
return true;
}
return false;
}
<#assign count1 = 1>
<#list run.conditions as condition>
<#if condition.name = "url">
public class Condition${count1} implements ABTestCondition {
@Override
public boolean accept(HttpServletRequest request) {
String actual = request.getRequestURL().toString();
if (${urlScriptProvider.getFragement(condition)}) {
return true;
} else {
return false;
}
}
}
</#if>
<#if condition.name = "percent">
public class Condition${count1} implements ABTestCondition {
private int m_percent = -1;
private Random m_random = new Random();
@Override
public boolean accept(HttpServletRequest request) {
if (m_percent == -1) {
m_percent = ${condition.text};
}
if (m_percent == 100) {
return true;
}
int random = m_random.nextInt(100) + 1;
if (random <= m_percent) {
return true;
} else {
return false;
}
}
}
</#if>
<#assign count1 = count1 + 1>
</#list>
}
\ No newline at end of file
......@@ -248,12 +248,6 @@ a.abtest-heartbeat {
margin-left: 0px;
}
.form-horizontal .control-label{
width:50px;
}
.form-horizontal .controls{
margin-left:70px;
}
select{
margin-bottom:0px;
}
......
此差异已折叠。
.rickshaw_graph .detail{pointer-events:none;position:absolute;top:0;z-index:2;background:rgba(0,0,0,.1);bottom:0;width:1px;transition:opacity .25s linear;-moz-transition:opacity .25s linear;-o-transition:opacity .25s linear;-webkit-transition:opacity .25s linear}.rickshaw_graph .detail.inactive{opacity:0}.rickshaw_graph .detail .item.active{opacity:1}.rickshaw_graph .detail .x_label{font-family:Arial,sans-serif;border-radius:3px;padding:6px;opacity:.5;border:1px solid #e0e0e0;font-size:12px;position:absolute;background:#fff;white-space:nowrap}.rickshaw_graph .detail .item{position:absolute;z-index:2;border-radius:3px;padding:.25em;font-size:12px;font-family:Arial,sans-serif;opacity:0;background:rgba(0,0,0,.4);color:#fff;border:1px solid rgba(0,0,0,.4);margin-left:1em;margin-top:-1em;white-space:nowrap}.rickshaw_graph .detail .item.active{opacity:1;background:rgba(0,0,0,.8)}.rickshaw_graph .detail .item:before{content:"\25c2";position:absolute;left:-.5em;color:rgba(0,0,0,.7);width:0}.rickshaw_graph .detail .dot{width:4px;height:4px;margin-left:-4px;margin-top:-3px;border-radius:5px;position:absolute;box-shadow:0 0 2px rgba(0,0,0,.6);background:#fff;border-width:2px;border-style:solid;display:none;background-clip:padding-box}.rickshaw_graph .detail .dot.active{display:block}.rickshaw_graph{position:relative}.rickshaw_graph svg{display:block;overflow:hidden}.rickshaw_graph .x_tick{position:absolute;top:0;bottom:0;width:0;border-left:1px dotted rgba(0,0,0,.2);pointer-events:none}.rickshaw_graph .x_tick .title{position:absolute;font-size:12px;font-family:Arial,sans-serif;opacity:.5;white-space:nowrap;margin-left:3px;bottom:1px}.rickshaw_annotation_timeline{height:1px;border-top:1px solid #e0e0e0;margin-top:10px;position:relative}.rickshaw_annotation_timeline .annotation{position:absolute;height:6px;width:6px;margin-left:-2px;top:-3px;border-radius:5px;background-color:rgba(0,0,0,.25)}.rickshaw_graph .annotation_line{position:absolute;top:0;bottom:-6px;width:0;border-left:2px solid rgba(0,0,0,.3);display:none}.rickshaw_graph .annotation_line.active{display:block}.rickshaw_graph .annotation_range{background:rgba(0,0,0,.1);display:none;position:absolute;top:0;bottom:-6px}.rickshaw_graph .annotation_range.active{display:block}.rickshaw_graph .annotation_range.active.offscreen{display:none}.rickshaw_annotation_timeline .annotation .content{background:#fff;color:#000;opacity:.9;padding:5px;box-shadow:0 0 2px rgba(0,0,0,.8);border-radius:3px;position:relative;z-index:20;font-size:12px;padding:6px 8px 8px;top:18px;left:-11px;width:160px;display:none;cursor:pointer}.rickshaw_annotation_timeline .annotation .content:before{content:"\25b2";position:absolute;top:-11px;color:#fff;text-shadow:0 -1px 1px rgba(0,0,0,.8)}.rickshaw_annotation_timeline .annotation.active,.rickshaw_annotation_timeline .annotation:hover{background-color:rgba(0,0,0,.8);cursor:none}.rickshaw_annotation_timeline .annotation .content:hover{z-index:50}.rickshaw_annotation_timeline .annotation.active .content{display:block}.rickshaw_annotation_timeline .annotation:hover .content{display:block;z-index:50}.rickshaw_graph .y_axis,.rickshaw_graph .x_axis_d3{fill:none}.rickshaw_graph .y_ticks .tick,.rickshaw_graph .x_ticks_d3 .tick{stroke:rgba(0,0,0,.16);stroke-width:2px;shape-rendering:crisp-edges;pointer-events:none}.rickshaw_graph .y_grid .tick,.rickshaw_graph .x_grid_d3 .tick{z-index:-1;stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:1 1}.rickshaw_graph .y_grid path,.rickshaw_graph .x_grid_d3 path{fill:none;stroke:none}.rickshaw_graph .y_ticks path,.rickshaw_graph .x_ticks_d3 path{fill:none;stroke:gray}.rickshaw_graph .y_ticks text,.rickshaw_graph .x_ticks_d3 text{opacity:.5;font-size:12px;pointer-events:none}.rickshaw_graph .x_tick.glow .title,.rickshaw_graph .y_ticks.glow text{fill:#000;color:#000;text-shadow:-1px 1px 0 rgba(255,255,255,.1),1px -1px 0 rgba(255,255,255,.1),1px 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1),0 -1px 0 rgba(255,255,255,.1),1px 0 0 rgba(255,255,255,.1),-1px 0 0 rgba(255,255,255,.1),-1px -1px 0 rgba(255,255,255,.1)}.rickshaw_graph .x_tick.inverse .title,.rickshaw_graph .y_ticks.inverse text{fill:#fff;color:#fff;text-shadow:-1px 1px 0 rgba(0,0,0,.8),1px -1px 0 rgba(0,0,0,.8),1px 1px 0 rgba(0,0,0,.8),0 1px 0 rgba(0,0,0,.8),0 -1px 0 rgba(0,0,0,.8),1px 0 0 rgba(0,0,0,.8),-1px 0 0 rgba(0,0,0,.8),-1px -1px 0 rgba(0,0,0,.8)}.rickshaw_legend{font-family:Arial;font-size:12px;color:#fff;background:#404040;display:inline-block;padding:12px 5px;border-radius:2px;position:relative}.rickshaw_legend:hover{z-index:10}.rickshaw_legend .swatch{width:10px;height:10px;border:1px solid rgba(0,0,0,.2)}.rickshaw_legend .line{clear:both;line-height:140%;padding-right:15px}.rickshaw_legend .line .swatch{display:inline-block;margin-right:3px;border-radius:2px}.rickshaw_legend .label{margin:0;white-space:nowrap;display:inline;font-size:inherit;background-color:transparent;color:inherit;font-weight:400;line-height:normal;padding:0;text-shadow:none}.rickshaw_legend .action:hover{opacity:.6}.rickshaw_legend .action{margin-right:.2em;font-size:10px;opacity:.2;cursor:pointer;font-size:14px}.rickshaw_legend .line.disabled{opacity:.4}.rickshaw_legend ul{list-style-type:none;margin:0;padding:0;margin:2px;cursor:pointer}.rickshaw_legend li{padding:0 0 0 2px;min-width:80px;white-space:nowrap}.rickshaw_legend li:hover{background:rgba(255,255,255,.08);border-radius:3px}.rickshaw_legend li:active{background:rgba(255,255,255,.2);border-radius:3px}
\ No newline at end of file
此差异已折叠。
此差异已折叠。
var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a=
Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);d<b||d>c;)a=d<b?a/2:2*a,d=Math.round(e/a);c=[];z(f,c,d,h,a);return{steps:d,stepValue:a,graphMin:h,labels:c}}function z(a,c,b,e,h){if(a)for(var f=1;f<b+1;f++)c.push(E(a,{value:(e+h*f).toFixed(0!=h%1?h.toString().split(".")[1].length:0)}))}function A(a,c,b){return!isNaN(parseFloat(c))&&isFinite(c)&&a>c?c:!isNaN(parseFloat(b))&&
isFinite(b)&&a<b?b:a}function y(a,c){var b={},e;for(e in a)b[e]=a[e];for(e in c)b[e]=c[e];return b}function E(a,c){var b=!/\W/.test(a)?F[a]=F[a]||E(document.getElementById(a).innerHTML):new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?
b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)?
0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1==
a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*
Math.PI)*Math.asin(1/e);return-(e*Math.pow(2,10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b))},easeOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return e*Math.pow(2,-10*a)*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(2==(a/=0.5))return 1;b||(b=1*0.3*1.5);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return 1>a?-0.5*e*Math.pow(2,10*
(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*
a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0,
scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",
animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",
scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a,
c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,
onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0,
pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",
scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]);
d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.length;f++)a[f].value>e&&(e=a[f].value),a[f].value<h&&(h=a[f].value);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,
m);k=g/j.steps;x(c,function(){for(var a=0;a<j.steps;a++)if(c.scaleShowLine&&(b.beginPath(),b.arc(q/2,u/2,k*(a+1),0,2*Math.PI,!0),b.strokeStyle=c.scaleLineColor,b.lineWidth=c.scaleLineWidth,b.stroke()),c.scaleShowLabels){b.textAlign="center";b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;var e=j.labels[a];if(c.scaleShowLabelBackdrop){var d=b.measureText(e).width;b.fillStyle=c.scaleBackdropColor;b.beginPath();b.rect(Math.round(q/2-d/2-c.scaleBackdropPaddingX),Math.round(u/2-k*(a+
1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(d+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY));b.fill()}b.textBaseline="middle";b.fillStyle=c.scaleFontColor;b.fillText(e,q/2,u/2-k*(a+1))}},function(e){var d=-Math.PI/2,g=2*Math.PI/a.length,f=1,h=1;c.animation&&(c.animateScale&&(f=e),c.animateRotate&&(h=e));for(e=0;e<a.length;e++)b.beginPath(),b.arc(q/2,u/2,f*v(a[e].value,j,k),d,d+h*g,!1),b.lineTo(q/2,u/2),b.closePath(),b.fillStyle=a[e].color,b.fill(),
c.segmentShowStroke&&(b.strokeStyle=c.segmentStrokeColor,b.lineWidth=c.segmentStrokeWidth,b.stroke()),d+=h*g},b)},H=function(a,c,b){var e,h,f,d,g,k,j,l,m;a.labels||(a.labels=[]);g=Math.min.apply(Math,[q,u])/2;d=2*c.scaleFontSize;for(e=l=0;e<a.labels.length;e++)b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily,h=b.measureText(a.labels[e]).width,h>l&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE;
h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(m=0;m<a.datasets[f].data.length;m++)a.datasets[f].data[m]>e&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]<h&&(h=a.datasets[f].data[m]);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,m);k=g/j.steps;x(c,function(){var e=2*Math.PI/
a.datasets[0].data.length;b.save();b.translate(q/2,u/2);if(c.angleShowLineOut){b.strokeStyle=c.angleLineColor;b.lineWidth=c.angleLineWidth;for(var d=0;d<a.datasets[0].data.length;d++)b.rotate(e),b.beginPath(),b.moveTo(0,0),b.lineTo(0,-g),b.stroke()}for(d=0;d<j.steps;d++){b.beginPath();if(c.scaleShowLine){b.strokeStyle=c.scaleLineColor;b.lineWidth=c.scaleLineWidth;b.moveTo(0,-k*(d+1));for(var f=0;f<a.datasets[0].data.length;f++)b.rotate(e),b.lineTo(0,-k*(d+1));b.closePath();b.stroke()}c.scaleShowLabels&&
(b.textAlign="center",b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily,b.textBaseline="middle",c.scaleShowLabelBackdrop&&(f=b.measureText(j.labels[d]).width,b.fillStyle=c.scaleBackdropColor,b.beginPath(),b.rect(Math.round(-f/2-c.scaleBackdropPaddingX),Math.round(-k*(d+1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(f+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY)),b.fill()),b.fillStyle=c.scaleFontColor,b.fillText(j.labels[d],0,-k*(d+
1)))}for(d=0;d<a.labels.length;d++){b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily;b.fillStyle=c.pointLabelFontColor;var f=Math.sin(e*d)*(g+c.pointLabelFontSize),h=Math.cos(e*d)*(g+c.pointLabelFontSize);b.textAlign=e*d==Math.PI||0==e*d?"center":e*d>Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;g<a.datasets.length;g++){b.beginPath();
b.moveTo(0,d*-1*v(a.datasets[g].data[0],j,k));for(var f=1;f<a.datasets[g].data.length;f++)b.rotate(e),b.lineTo(0,d*-1*v(a.datasets[g].data[f],j,k));b.closePath();b.fillStyle=a.datasets[g].fillColor;b.strokeStyle=a.datasets[g].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.fill();b.stroke();if(c.pointDot){b.fillStyle=a.datasets[g].pointColor;b.strokeStyle=a.datasets[g].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(f=0;f<a.datasets[g].data.length;f++)b.rotate(e),b.beginPath(),b.arc(0,d*-1*
v(a.datasets[g].data[f],j,k),c.pointDotRadius,2*Math.PI,!1),b.fill(),b.stroke()}b.rotate(e)}b.restore()},b)},I=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=0;f<a.length;f++)e+=a[f].value;x(c,null,function(d){var g=-Math.PI/2,f=1,j=1;c.animation&&(c.animateScale&&(f=d),c.animateRotate&&(j=d));for(d=0;d<a.length;d++){var l=j*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,f*h,g,g+l);b.lineTo(q/2,u/2);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&(b.lineWidth=
c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());g+=l}},b)},J=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=h*(c.percentageInnerCutout/100),d=0;d<a.length;d++)e+=a[d].value;x(c,null,function(d){var k=-Math.PI/2,j=1,l=1;c.animation&&(c.animateScale&&(j=d),c.animateRotate&&(l=d));for(d=0;d<a.length;d++){var m=l*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,j*h,k,k+m,!1);b.arc(q/2,u/2,j*f,k+m,k,!0);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&
(b.lineWidth=c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());k+=m}},b)},K=function(a,c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(s=45,q/a.labels.length<Math.cos(s)*t?(s=90,g-=t):g-=Math.sin(s)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=
0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;
for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<s?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<s?(b.translate(n+d*m,p+c.scaleFontSize),b.rotate(-(s*(Math.PI/180))),b.fillText(a.labels[d],
0,0),b.restore()):b.fillText(a.labels[d],n+d*m,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+d*m,p+3),c.scaleShowGridLines&&0<d?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+d*m,5)):b.lineTo(n+d*m,p+3),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,
b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){function e(b,c){return p-d*v(a.datasets[b].data[c],j,k)}for(var f=0;f<a.datasets.length;f++){b.strokeStyle=a.datasets[f].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.beginPath();b.moveTo(n,p-d*v(a.datasets[f].data[0],j,k));for(var g=1;g<a.datasets[f].data.length;g++)c.bezierCurve?b.bezierCurveTo(n+m*(g-0.5),e(f,g-1),n+m*(g-0.5),
e(f,g),n+m*g,e(f,g)):b.lineTo(n+m*g,e(f,g));b.stroke();c.datasetFill?(b.lineTo(n+m*(a.datasets[f].data.length-1),p),b.lineTo(n,p),b.closePath(),b.fillStyle=a.datasets[f].fillColor,b.fill()):b.closePath();if(c.pointDot){b.fillStyle=a.datasets[f].pointColor;b.strokeStyle=a.datasets[f].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(g=0;g<a.datasets[f].data.length;g++)b.beginPath(),b.arc(n+m*g,p-d*v(a.datasets[f].data[g],j,k),c.pointDotRadius,0,2*Math.PI,!0),b.fill(),b.stroke()}}},b)},L=function(a,
c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s,w=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(w=45,q/a.labels.length<Math.cos(w)*t?(w=90,g-=t):g-=Math.sin(w)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<
h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=
Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<w?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<w?(b.translate(n+
d*m,p+c.scaleFontSize),b.rotate(-(w*(Math.PI/180))),b.fillText(a.labels[d],0,0),b.restore()):b.fillText(a.labels[d],n+d*m+m/2,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+(d+1)*m,p+3),b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+(d+1)*m,5),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*
k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){b.lineWidth=c.barStrokeWidth;for(var e=0;e<a.datasets.length;e++){b.fillStyle=a.datasets[e].fillColor;b.strokeStyle=a.datasets[e].strokeColor;for(var f=0;f<a.datasets[e].data.length;f++){var g=n+c.barValueSpacing+m*f+s*e+c.barDatasetSpacing*e+c.barStrokeWidth*e;b.beginPath();
b.moveTo(g,p);b.lineTo(g,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p);c.barShowStroke&&b.stroke();b.closePath();b.fill()}}},b)},D=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)},F={}};
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
<%@ page contentType="text/html; charset=utf-8" %>
<jsp:useBean id="ctx" type="com.dianping.cat.system.page.abtest.Context" scope="request"/>
<jsp:useBean id="payload" type="com.dianping.cat.system.page.abtest.Payload" scope="request"/>
<jsp:useBean id="model" type="com.dianping.cat.system.page.abtest.Model" scope="request"/>
View of abtest page under system
\ No newline at end of file
<jsp:useBean id="ctx" type="com.dianping.cat.system.page.abtest.Context" scope="request" />${ctx.responseJson}
<%@ page contentType="text/xml; charset=utf-8" trimDirectiveWhitespaces="true"%>
<jsp:useBean id="model" type="com.dianping.cat.system.page.abtest.Model" scope="request" />${model.abtestModel}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册