killbill-uncached

analytics: initial import of the user api As a proof of concept,

8/22/2012 12:40:37 PM

Details

diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java b/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java
new file mode 100644
index 0000000..4c1eebb
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/DefaultTimeSeriesData.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+import com.ning.billing.analytics.dao.TimeSeriesTuple;
+
+public class DefaultTimeSeriesData implements TimeSeriesData {
+
+    private final List<LocalDate> dates;
+    private final List<Double> values;
+
+    public DefaultTimeSeriesData(final List<TimeSeriesTuple> dataOverTime) {
+        // We assume dataOverTime is sorted by time
+        dates = new LinkedList<LocalDate>();
+        values = new LinkedList<Double>();
+        for (final TimeSeriesTuple data : dataOverTime) {
+            dates.add(data.getLocalDate());
+            values.add(data.getValue());
+        }
+    }
+
+    @Override
+    public List<LocalDate> getDates() {
+        return dates;
+    }
+
+    @Override
+    public List<Double> getValues() {
+        return values;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("DefaultTimeSeriesData");
+        sb.append("{dates=").append(dates);
+        sb.append(", values=").append(values);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final DefaultTimeSeriesData that = (DefaultTimeSeriesData) o;
+
+        if (dates != null ? !dates.equals(that.dates) : that.dates != null) {
+            return false;
+        }
+        if (values != null ? !values.equals(that.values) : that.values != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = dates != null ? dates.hashCode() : 0;
+        result = 31 * result + (values != null ? values.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
index 4d90894..cbd652d 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/api/user/DefaultAnalyticsUserApi.java
@@ -16,10 +16,12 @@
 
 package com.ning.billing.analytics.api.user;
 
-import javax.inject.Inject;
 import java.util.List;
 import java.util.UUID;
 
+import javax.inject.Inject;
+
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.dao.AnalyticsDao;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
@@ -29,8 +31,8 @@ import com.ning.billing.analytics.model.BusinessInvoicePayment;
 import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
-// Note: not exposed in api yet
-public class DefaultAnalyticsUserApi {
+public class DefaultAnalyticsUserApi implements AnalyticsUserApi {
+
     private final AnalyticsDao analyticsDao;
 
     @Inject
@@ -38,6 +40,13 @@ public class DefaultAnalyticsUserApi {
         this.analyticsDao = analyticsDao;
     }
 
+    @Override
+    public TimeSeriesData getAccountsCreatedOverTime() {
+        return analyticsDao.getAccountsCreatedOverTime();
+    }
+
+    // Note: the following is not exposed in api yet, as the models need to be extracted first
+
     public BusinessAccount getAccountByKey(final String accountKey) {
         return analyticsDao.getAccountByKey(accountKey);
     }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
index 0e349fc..d4ae76e 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/AnalyticsDao.java
@@ -18,6 +18,7 @@ package com.ning.billing.analytics.dao;
 
 import java.util.List;
 
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
@@ -27,6 +28,9 @@ import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
 public interface AnalyticsDao {
+
+    TimeSeriesData getAccountsCreatedOverTime();
+
     BusinessAccount getAccountByKey(final String accountKey);
 
     List<BusinessSubscriptionTransition> getTransitionsByKey(final String externalKey);
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
index 8ceb0ad..d2c6d2e 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/BusinessAccountSqlDao.java
@@ -16,6 +16,8 @@
 
 package com.ning.billing.analytics.dao;
 
+import java.util.List;
+
 import org.skife.jdbi.v2.sqlobject.Bind;
 import org.skife.jdbi.v2.sqlobject.SqlQuery;
 import org.skife.jdbi.v2.sqlobject.SqlUpdate;
@@ -27,8 +29,12 @@ import org.skife.jdbi.v2.sqlobject.stringtemplate.ExternalizedSqlViaStringTempla
 import com.ning.billing.analytics.model.BusinessAccount;
 
 @ExternalizedSqlViaStringTemplate3()
-@RegisterMapper(BusinessAccountMapper.class)
+@RegisterMapper({BusinessAccountMapper.class, TimeSeriesTupleMapper.class})
 public interface BusinessAccountSqlDao extends Transactional<BusinessAccountSqlDao>, Transmogrifier {
+
+    @SqlQuery
+    List<TimeSeriesTuple> getAccountsCreatedOverTime();
+
     @SqlQuery
     BusinessAccount getAccount(@Bind("account_id") final String accountId);
 
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
index d4855fd..0918673 100644
--- a/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/DefaultAnalyticsDao.java
@@ -16,9 +16,12 @@
 
 package com.ning.billing.analytics.dao;
 
-import javax.inject.Inject;
 import java.util.List;
 
+import javax.inject.Inject;
+
+import com.ning.billing.analytics.api.DefaultTimeSeriesData;
+import com.ning.billing.analytics.api.TimeSeriesData;
 import com.ning.billing.analytics.model.BusinessAccount;
 import com.ning.billing.analytics.model.BusinessAccountTag;
 import com.ning.billing.analytics.model.BusinessInvoice;
@@ -28,6 +31,7 @@ import com.ning.billing.analytics.model.BusinessOverdueStatus;
 import com.ning.billing.analytics.model.BusinessSubscriptionTransition;
 
 public class DefaultAnalyticsDao implements AnalyticsDao {
+
     private final BusinessAccountSqlDao accountSqlDao;
     private final BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
     private final BusinessInvoiceSqlDao invoiceSqlDao;
@@ -54,6 +58,11 @@ public class DefaultAnalyticsDao implements AnalyticsDao {
     }
 
     @Override
+    public TimeSeriesData getAccountsCreatedOverTime() {
+        return new DefaultTimeSeriesData(accountSqlDao.getAccountsCreatedOverTime());
+    }
+
+    @Override
     public BusinessAccount getAccountByKey(final String accountKey) {
         return accountSqlDao.getAccountByKey(accountKey);
     }
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java
new file mode 100644
index 0000000..ec82930
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTuple.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import org.joda.time.LocalDate;
+
+public class TimeSeriesTuple {
+
+    private final LocalDate localDate;
+    private final Double value;
+
+    public TimeSeriesTuple(final LocalDate localDate, final Double value) {
+        this.localDate = localDate;
+        this.value = value;
+    }
+
+    public LocalDate getLocalDate() {
+        return localDate;
+    }
+
+    public Double getValue() {
+        return value;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("TimeSeriesTuple");
+        sb.append("{localDate=").append(localDate);
+        sb.append(", value=").append(value);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final TimeSeriesTuple that = (TimeSeriesTuple) o;
+
+        if (localDate != null ? !localDate.equals(that.localDate) : that.localDate != null) {
+            return false;
+        }
+        if (value != null ? !value.equals(that.value) : that.value != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = localDate != null ? localDate.hashCode() : 0;
+        result = 31 * result + (value != null ? value.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java
new file mode 100644
index 0000000..2843e8c
--- /dev/null
+++ b/analytics/src/main/java/com/ning/billing/analytics/dao/TimeSeriesTupleMapper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.dao;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.StatementContext;
+import org.skife.jdbi.v2.tweak.ResultSetMapper;
+
+public class TimeSeriesTupleMapper implements ResultSetMapper<TimeSeriesTuple> {
+
+    @Override
+    public TimeSeriesTuple map(final int index, final ResultSet r, final StatementContext ctx) throws SQLException {
+        return new TimeSeriesTuple(new LocalDate(r.getDate(1)), r.getDouble(2));
+    }
+}
diff --git a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
index 0fa4317..c6b9195 100644
--- a/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
+++ b/analytics/src/main/resources/com/ning/billing/analytics/dao/BusinessAccountSqlDao.sql.stg
@@ -1,5 +1,16 @@
 group BusinessAccount;
 
+getAccountsCreatedOverTime() ::= <<
+  select
+    date(from_unixtime(created_date / 1000)) day
+    -- TODO: use account_record_id, once populated
+  , count(record_id) count
+  from bac
+  group by 1
+  order by 1
+  ;
+>>
+
 getAccount(account_id) ::= <<
   select
     account_id
diff --git a/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java b/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java
new file mode 100644
index 0000000..1b7ef04
--- /dev/null
+++ b/analytics/src/test/java/com/ning/billing/analytics/api/user/TestDefaultAnalyticsUserApi.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api.user;
+
+import java.math.BigDecimal;
+import java.util.UUID;
+
+import org.joda.time.LocalDate;
+import org.skife.jdbi.v2.IDBI;
+import org.testng.Assert;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.ning.billing.analytics.AnalyticsTestSuiteWithEmbeddedDB;
+import com.ning.billing.analytics.api.TimeSeriesData;
+import com.ning.billing.analytics.dao.AnalyticsDao;
+import com.ning.billing.analytics.dao.BusinessAccountSqlDao;
+import com.ning.billing.analytics.dao.BusinessAccountTagSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceItemSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoicePaymentSqlDao;
+import com.ning.billing.analytics.dao.BusinessInvoiceSqlDao;
+import com.ning.billing.analytics.dao.BusinessOverdueStatusSqlDao;
+import com.ning.billing.analytics.dao.BusinessSubscriptionTransitionSqlDao;
+import com.ning.billing.analytics.dao.DefaultAnalyticsDao;
+import com.ning.billing.analytics.model.BusinessAccount;
+import com.ning.billing.util.clock.Clock;
+import com.ning.billing.util.clock.ClockMock;
+
+public class TestDefaultAnalyticsUserApi extends AnalyticsTestSuiteWithEmbeddedDB {
+
+    private final Clock clock = new ClockMock();
+
+    private AnalyticsUserApi analyticsUserApi;
+    private BusinessAccountSqlDao accountSqlDao;
+    private BusinessSubscriptionTransitionSqlDao subscriptionTransitionSqlDao;
+    private BusinessInvoiceSqlDao invoiceSqlDao;
+    private BusinessInvoiceItemSqlDao invoiceItemSqlDao;
+    private BusinessAccountTagSqlDao accountTagSqlDao;
+    private BusinessOverdueStatusSqlDao overdueStatusSqlDao;
+    private BusinessInvoicePaymentSqlDao invoicePaymentSqlDao;
+
+    @BeforeMethod(groups = "slow")
+    public void setUp() throws Exception {
+        final IDBI dbi = helper.getDBI();
+        accountSqlDao = dbi.onDemand(BusinessAccountSqlDao.class);
+        subscriptionTransitionSqlDao = dbi.onDemand(BusinessSubscriptionTransitionSqlDao.class);
+        invoiceSqlDao = dbi.onDemand(BusinessInvoiceSqlDao.class);
+        invoiceItemSqlDao = dbi.onDemand(BusinessInvoiceItemSqlDao.class);
+        accountTagSqlDao = dbi.onDemand(BusinessAccountTagSqlDao.class);
+        overdueStatusSqlDao = dbi.onDemand(BusinessOverdueStatusSqlDao.class);
+        invoicePaymentSqlDao = dbi.onDemand(BusinessInvoicePaymentSqlDao.class);
+
+        final AnalyticsDao analyticsDao = new DefaultAnalyticsDao(accountSqlDao, subscriptionTransitionSqlDao, invoiceSqlDao,
+                                                                  invoiceItemSqlDao, accountTagSqlDao, overdueStatusSqlDao, invoicePaymentSqlDao);
+        analyticsUserApi = new DefaultAnalyticsUserApi(analyticsDao);
+    }
+
+    @Test(groups = "slow")
+    public void testAccountsCreatedOverTime() throws Exception {
+        final BusinessAccount account = new BusinessAccount(UUID.randomUUID(), UUID.randomUUID().toString(), UUID.randomUUID().toString(), BigDecimal.ONE, clock.getUTCToday(),
+                                                            BigDecimal.TEN, "ERROR_NOT_ENOUGH_FUNDS", "CreditCard", "Visa", "FRANCE", "USD");
+        accountSqlDao.createAccount(account);
+
+        final TimeSeriesData data = analyticsUserApi.getAccountsCreatedOverTime();
+        Assert.assertEquals(data.getDates().size(), 1);
+        Assert.assertEquals(data.getDates().get(0), new LocalDate());
+        Assert.assertEquals(data.getValues().size(), 1);
+        Assert.assertEquals(data.getValues().get(0), (double) 1);
+    }
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java b/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java
new file mode 100644
index 0000000..35c8f02
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/TimeSeriesData.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api;
+
+import java.util.List;
+
+import org.joda.time.LocalDate;
+
+public interface TimeSeriesData {
+
+    /**
+     * @return sorted list of dates, in UTC
+     */
+    public List<LocalDate> getDates();
+
+    /**
+     * @return sorted list of values associated with the dates
+     */
+    public List<Double> getValues();
+}
diff --git a/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java b/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java
new file mode 100644
index 0000000..26a2480
--- /dev/null
+++ b/api/src/main/java/com/ning/billing/analytics/api/user/AnalyticsUserApi.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.analytics.api.user;
+
+import com.ning.billing.analytics.api.TimeSeriesData;
+
+public interface AnalyticsUserApi {
+
+    /**
+     * @return the number of accounts created per day
+     */
+    public TimeSeriesData getAccountsCreatedOverTime();
+}