Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DDP-6891_GoogleAnalyitics #316

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-pubsub</artifactId>
</dependency>
<dependency>
<groupId>com.brsanthu</groupId>
<artifactId>google-analytics-java</artifactId>
<version>2.0.0</version>
</dependency>

<dependency>
<groupId>org.javalite</groupId>
<artifactId>activejdbc</artifactId>
Expand Down
19 changes: 17 additions & 2 deletions src/main/java/org/broadinstitute/dsm/DSMServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import org.broadinstitute.ddp.util.BasicTriggerListener;
import org.broadinstitute.ddp.util.JsonTransformer;
import org.broadinstitute.ddp.util.Utility;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetrics;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetricsTracker;
import org.broadinstitute.dsm.careevolve.Provider;
import org.broadinstitute.dsm.jetty.JettyConfig;
import org.broadinstitute.dsm.jobs.*;
Expand Down Expand Up @@ -167,12 +169,14 @@ protected void configureServer(@NonNull Config config) {
logger.info("Using port {}", port);
port(port);

registerAppEngineStartupCallback(bootTimeoutSeconds);
registerAppEngineStartupCallback(bootTimeoutSeconds, config);

setupDB(config);

// don't run superclass routing--it won't work with JettyConfig changes for capturing proper IP address in GAE
setupCustomRouting(config);
GoogleAnalyticsMetricsTracker.getInstance().sendAnalyticsMetrics("", GoogleAnalyticsMetrics.EVENT_CATEGORY_SERVER_START,
GoogleAnalyticsMetrics.EVENT_ACTION_SERVER_START, GoogleAnalyticsMetrics.EVENT_LABEL_SERVER_START, 1 );

List<String> allowedOrigins = config.getStringList(ApplicationConfigConstants.CORS_ALLOWED_ORIGINS);
enableCORS(StringUtils.join(allowedOrigins, ","), String.join(",", CORS_HTTP_METHODS), String.join(",", CORS_HTTP_HEADERS));
Expand Down Expand Up @@ -834,14 +838,25 @@ public static boolean isTest(@NonNull String shipperName) {
return true;
}

private static void registerAppEngineStartupCallback(long bootTimeoutSeconds) {
private static void registerAppEngineStartupCallback(long bootTimeoutSeconds, Config cfg) {
GoogleAnalyticsMetricsTracker.setConfig(cfg);
// Block until isReady is available, with an optional timeout to prevent
// instance for sitting around too long in a nonresponsive state. There is a
// judgement call to be made here to allow for lengthy liquibase migrations during boot.
logger.info("Will wait for at most {} seconds for boot before GAE termination", bootTimeoutSeconds);
get("/_ah/start", new ReadinessRoute(bootTimeoutSeconds));

get(RoutePath.GAE.STOP_ENDPOINT, (request, response) -> {
logger.info("Received GAE stop request [{}]", RoutePath.GAE.STOP_ENDPOINT);
//flush out any pending GA events
GoogleAnalyticsMetricsTracker.getInstance().flushOutMetrics();

response.status(HttpStatus.SC_OK);
return "";
});
}


private static class ReadinessRoute implements Route {

private final long bootTimeoutSeconds;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.broadinstitute.dsm.analytics;

public class GoogleAnalyticsMetrics {

// public static final String EVENT_CATEGORY_USER_REGISTRATION = "user-registration";
// public static final String EVENT_ACTION_USER_REGISTRATION = "register-user";
// public static final String EVENT_LABEL_USER_REGISTRATION = "registration"; we may need it for FON

public static final String EVENT_CATEGORY_USER_LOGIN = "user-login";
public static final String EVENT_ACTION_USER_LOGIN = "user-logged-in";
public static final String EVENT_LABEL_USER_LOGIN = "login"; //studyGuid appended
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't added these events to the code yet, it wasn't in the ticket, so I wasn't sure


public static final String EVENT_CATEGORY_PARTICIPANT_LIST = "participant-list";
public static final String EVENT_ACTION_PARTICIPANT_LIST = "participant-list-load-time";
public static final String EVENT_LABEL_PARTICIPANT_LIST = "participant-list-load-time";//studyGuid appended

public static final String EVENT_CATEGORY_SERVER_START= "server-start";
public static final String EVENT_ACTION_SERVER_START = "server-start";
public static final String EVENT_LABEL_SERVER_START = "server-start";

public static final String EVENT_CATEGORY_PATCH_DATA = "patch-data";
public static final String EVENT_ACTION_PATCH_DATA = "patch-data-answers";
public static final String EVENT_LABEL_PATCH_DATA = "patch-answers"; //studyGuid & original page appended

public static final String EVENT_CATEGORY_TISSUE_LIST = "tissue-list";
public static final String EVENT_ACTION_TISSUE_LIST = "tissue-list-loaded";
public static final String EVENT_LABEL_TISSUE_LIST = "tissue-list-loaded";//studyGuid appended

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.broadinstitute.dsm.analytics;

import com.brsanthu.googleanalytics.GoogleAnalytics;
import com.brsanthu.googleanalytics.GoogleAnalyticsConfig;
import com.brsanthu.googleanalytics.request.EventHit;
import com.brsanthu.googleanalytics.request.GoogleAnalyticsResponse;
import com.typesafe.config.Config;
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GoogleAnalyticsMetricsTracker {
private static final Logger logger = LoggerFactory.getLogger(GoogleAnalyticsMetricsTracker.class);
private static final String GA_TOKEN_PATH = "GoogleAnalytics.trackingId";
private static GoogleAnalytics googleAnalyticsTrackers;
private static volatile GoogleAnalyticsMetricsTracker instance;
private static Object lockGA = new Object();
private static Config CONFIG;

private GoogleAnalyticsMetricsTracker() {
initStudyMetricTracker();
}

public static void setConfig(@NonNull Config config){
CONFIG = config;
}

public static GoogleAnalyticsMetricsTracker getInstance() {
if (instance == null) {
synchronized (lockGA) {
if (instance == null) {
instance = new GoogleAnalyticsMetricsTracker();
}
}
}
return instance;
}

private GoogleAnalytics getMetricTracker() {
if (googleAnalyticsTrackers == null) {

initStudyMetricTracker();
}
return googleAnalyticsTrackers;
}

private synchronized void initStudyMetricTracker() {
GoogleAnalytics metricTracker = GoogleAnalytics.builder()
.withConfig(new GoogleAnalyticsConfig().setGatherStats(true).setUserAgent("Custom User Agent"))
.withTrackingId(getAnalyticsToken(CONFIG))
.build();
googleAnalyticsTrackers = metricTracker;
logger.info("Initialized GA Metrics Tracker for DSM ");
}


private void sendEventMetrics(EventHit eventHit) {
GoogleAnalytics metricTracker = getMetricTracker();
if (metricTracker != null) {
GoogleAnalyticsResponse response = metricTracker.event().eventCategory(eventHit.eventCategory())
.eventAction(eventHit.eventAction())
.eventLabel(eventHit.eventLabel())
.eventValue(eventHit.eventValue())
.trackingId(getAnalyticsToken(CONFIG))
.send();

logger.info(response.toString());
logger.info(response.getStatusCode()+"");
logger.info(metricTracker.getStats().getEventHits()+"");

}
}

public void sendAnalyticsMetrics(String studyGuid, String category, String action, String label, int value) {
String gaEventLabel = String.join(":", label,
studyGuid);
EventHit eventHit = new EventHit(category, action, gaEventLabel, value);
logger.info(eventHit.toString());
sendEventMetrics(eventHit);

}

public void flushOutMetrics() {
//lookup all Metrics Trackers and flush out any pending events
logger.info("Flushing out all pending GA events");
googleAnalyticsTrackers.flush();
googleAnalyticsTrackers.resetStats();
}

public String getAnalyticsToken(@NonNull Config config) {
if (config.hasPath(GA_TOKEN_PATH)) {
return config.getString(GA_TOKEN_PATH);
}
throw new RuntimeException("There is no secret in the SM for the Google Analytics");
}


}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package org.broadinstitute.dsm.model.filter.participant;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.google.gson.Gson;
import lombok.NonNull;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetrics;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetricsTracker;
import org.broadinstitute.dsm.db.DDPInstance;
import org.broadinstitute.dsm.db.ViewFilter;
import org.broadinstitute.dsm.db.dao.ddp.instance.DDPInstanceDao;
Expand All @@ -19,10 +15,9 @@
import org.broadinstitute.dsm.db.structure.DBElement;
import org.broadinstitute.dsm.model.Filter;
import org.broadinstitute.dsm.model.elasticsearch.ElasticSearch;
import org.broadinstitute.dsm.model.participant.ParticipantWrapper;
import org.broadinstitute.dsm.model.filter.BaseFilter;
import org.broadinstitute.dsm.model.filter.Filterable;
import org.broadinstitute.dsm.model.participant.ParticipantWrapperDto;
import org.broadinstitute.dsm.model.participant.ParticipantWrapper;
import org.broadinstitute.dsm.model.participant.ParticipantWrapperPayload;
import org.broadinstitute.dsm.model.participant.ParticipantWrapperResult;
import org.broadinstitute.dsm.statics.DBConstants;
Expand All @@ -31,6 +26,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public abstract class BaseFilterParticipantList extends BaseFilter implements Filterable<ParticipantWrapperResult> {

private static final Logger logger = LoggerFactory.getLogger(BaseFilterParticipantList.class);
Expand All @@ -57,6 +58,7 @@ public BaseFilterParticipantList(String jsonBody) {


protected ParticipantWrapperResult filterParticipantList(Filter[] filters, Map<String, DBElement> columnNameMap, @NonNull DDPInstance instance) {
Long timer = System.currentTimeMillis();
Map<String, String> queryConditions = new HashMap<>();
List<ParticipantDataDto> allParticipantData = null;
DDPInstanceDto ddpInstanceDto = new DDPInstanceDao().getDDPInstanceByInstanceName(realm).orElseThrow();
Expand Down Expand Up @@ -119,9 +121,12 @@ protected ParticipantWrapperResult filterParticipantList(Filter[] filters, Map<S

logger.info("Found query conditions for " + mergeConditions.size() + " tables");
//search bar ptL

GoogleAnalyticsMetricsTracker.getInstance().sendAnalyticsMetrics(ddpInstance.getName(), GoogleAnalyticsMetrics.EVENT_CATEGORY_PARTICIPANT_LIST,
GoogleAnalyticsMetrics.EVENT_ACTION_PARTICIPANT_LIST, GoogleAnalyticsMetrics.EVENT_LABEL_PARTICIPANT_LIST, Math.toIntExact((System.currentTimeMillis() - timer)/1000));
return new ParticipantWrapper(participantWrapperPayload.withFilter(mergeConditions).build(), elasticSearch).getFilteredList();
} else {
GoogleAnalyticsMetricsTracker.getInstance().sendAnalyticsMetrics(ddpInstance.getName(), GoogleAnalyticsMetrics.EVENT_CATEGORY_PARTICIPANT_LIST,
GoogleAnalyticsMetrics.EVENT_ACTION_PARTICIPANT_LIST, GoogleAnalyticsMetrics.EVENT_LABEL_PARTICIPANT_LIST, Math.toIntExact((System.currentTimeMillis() - timer)/1000));
return new ParticipantWrapper(participantWrapperPayload.build(), elasticSearch).getFilteredList();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.broadinstitute.dsm.model.filter.tissue;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetrics;
import org.broadinstitute.dsm.analytics.GoogleAnalyticsMetricsTracker;
import org.broadinstitute.dsm.db.DDPInstance;
import org.broadinstitute.dsm.db.ViewFilter;
import org.broadinstitute.dsm.model.TissueList;
Expand All @@ -17,6 +14,11 @@
import org.slf4j.LoggerFactory;
import spark.QueryParamsMap;

import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;

//reloadWithDefault/Manual/Empty filtering
public class ManualFilterTissueList extends BaseFilterTissueList {

Expand All @@ -28,6 +30,7 @@ public ManualFilterTissueList(String jsonBody) {

@Override
public List<TissueListWrapper> filter(QueryParamsMap queryParamsMap) {
Long timer = System.currentTimeMillis();
List<TissueListWrapper> wrapperList = new ArrayList<>();
if (Objects.isNull(queryParamsMap)) return wrapperList;
prepareNeccesaryData(queryParamsMap);
Expand All @@ -52,6 +55,8 @@ public List<TissueListWrapper> filter(QueryParamsMap queryParamsMap) {
wrapperList = filterTissueList(filters, PatchUtil.getColumnNameMap(), quickFilterName, ddpInstance, filterQuery);
}
logger.info("Found " + wrapperList.size() + " tissues for Tissue View");
GoogleAnalyticsMetricsTracker.getInstance().sendAnalyticsMetrics(ddpInstance.getName(), GoogleAnalyticsMetrics.EVENT_CATEGORY_TISSUE_LIST,
GoogleAnalyticsMetrics.EVENT_ACTION_TISSUE_LIST, GoogleAnalyticsMetrics.EVENT_LABEL_TISSUE_LIST, Math.toIntExact((System.currentTimeMillis() - timer)/1000));
return wrapperList;
}
}
Loading