/**
* Copyright © 2014 Instituto Superior Técnico
*
* This file is part of FenixEdu CMS.
*
* FenixEdu CMS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu CMS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu CMS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.cms.domain;
import java.io.Serializable;
import java.time.Duration;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import org.fenixedu.bennu.core.security.Authenticate;
import org.fenixedu.bennu.portal.domain.PortalConfiguration;
import org.fenixedu.bennu.social.domain.api.GoogleAPI;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.analytics.Analytics;
import com.google.api.services.analytics.model.GaData;
import com.google.api.services.analytics.model.Profile;
import com.google.api.services.analytics.model.Profiles;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import pt.ist.fenixframework.Atomic;
public class SiteAnalytics implements Serializable {
private static final String LAST_UPDATE_PROPERTY = "$$last_update";
private static final Logger LOGGER = LoggerFactory.getLogger(SiteAnalytics.class);
private static final long serialVersionUID = 4890885803531605616L;
private static final long MAX_UPDATE_DURATION = Duration.ofHours(24).toNanos();
private final JsonElement metadata;
public SiteAnalytics() {
this(new JsonObject());
}
public SiteAnalytics(JsonElement metadata) {
this.metadata = new Gson().fromJson(metadata.toString(), JsonElement.class);
}
public JsonElement externalize() {
return metadata;
}
public static SiteAnalytics internalize(JsonElement json) {
return new SiteAnalytics(json);
}
public JsonElement getOrFetch(Site site) {
if(!isLastUpdateValid()) {
return update(site).get();
}
return this.metadata;
}
public JsonElement get() {
return this.metadata;
}
@Atomic(mode = Atomic.TxMode.WRITE)
public SiteAnalytics update(Site site) {
SiteAnalytics siteAnalytics = new SiteAnalytics(fetch(site));
site.setAnalytics(siteAnalytics);
return siteAnalytics;
}
private boolean isLastUpdateValid() {
return Optional.ofNullable(this.metadata).filter(JsonElement::isJsonObject)
.map(JsonElement::getAsJsonObject).map(jsonObj->jsonObj.get(LAST_UPDATE_PROPERTY))
.filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsString).map(DateTime::parse)
.filter(lastUpdate -> lastUpdate.minus(MAX_UPDATE_DURATION).isBeforeNow()).isPresent();
}
private JsonObject fetch(Site site) {
JsonObject result = new JsonObject();
result.addProperty(LAST_UPDATE_PROPERTY, DateTime.now().toDateTimeISO().toString());
JsonObject googleData = new JsonObject();
try {
if (!Strings.isNullOrEmpty(site.getAnalyticsAccountId()) && !Strings.isNullOrEmpty(
site.getAnalyticsCode())) {
Analytics analytics = getUserAnalytics();
Profiles profiles = analytics.management().profiles().list(
site.getAnalyticsAccountId(), site.getAnalyticsCode()).execute();
for (Profile profile : profiles.getItems()) {
GaData query = analytics.data().ga()
.get("ga:" + profile.getId(), "30daysAgo", "today",
"ga:pageviews,ga:visitors")
.setDimensions("ga:date")
.execute();
for (List<String> days : query.getRows()) {
JsonObject views;
if (googleData.has(days.get(0))) {
views = (JsonObject) googleData.get(days.get(0));
views.addProperty("pageviews", views.get("pageviews") + days.get(1));
views.addProperty("visitors", views.get("visitors") + days.get(2));
} else {
views = new JsonObject();
googleData.add(days.get(0), views);
}
views.addProperty("pageviews", days.get(1));
views.addProperty("visitors", days.get(2));
}
}
}
} catch(Exception e) {
LOGGER.error("Error loading analytics data for site '" + site.getSlug() + "'", e);
}
result.add("google", googleData);
return result;
}
private Analytics getUserAnalytics() {
GoogleCredential
credential = GoogleAPI.getInstance().getAuthenticatedUser(Authenticate.getUser()).get().getAuthenticatedSDK();
return new Analytics.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential).setApplicationName(
PortalConfiguration.getInstance().getApplicationTitle().getContent(Locale.ENGLISH)).build();
}
}