/*
* Copyright 2009 Google Inc.
*
* Licensed 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 org.gtugs.web.reporting;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.datanucleus.store.appengine.query.JDOCursorHelper;
import org.gtugs.domain.Chapter;
import org.gtugs.domain.Country;
import org.gtugs.domain.Event;
import org.gtugs.domain.Topic;
import org.gtugs.repository.JdoStatsDao;
import org.gtugs.repository.PMF;
import org.gtugs.service.ChapterManager;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.google.appengine.api.datastore.Cursor;
import com.google.appengine.api.labs.taskqueue.Queue;
import com.google.appengine.api.labs.taskqueue.QueueFactory;
import com.google.appengine.api.labs.taskqueue.TaskOptions;
/**
* @author jasonacooper@google.com (Jason Cooper)
*/
@Controller
public class GetEventStatsController {
private static final String CURSOR_KEY = "cursor";
private static final String NOW_DATE = "now_date";
private static final String THEN_DATE = "then_date";
@Autowired
private JdoStatsDao statsDao;
@Autowired
private ChapterManager chapterManager;
@RequestMapping("/offline/getEventStats")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Cursor cursor = null;
Date nowDate = null;
Date thenDate = null;
if (request.getParameter(CURSOR_KEY) != null) {
cursor = Cursor.fromWebSafeString(request.getParameter(CURSOR_KEY));
}
if (request.getParameter(NOW_DATE) != null) {
nowDate = new Date(Long.parseLong(request.getParameter(NOW_DATE)));
} else {
nowDate = new Date();
}
if (request.getParameter(THEN_DATE) != null) {
thenDate = new Date(Long.parseLong(request.getParameter(THEN_DATE)));
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MONTH, -6);
thenDate = cal.getTime();
}
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(Event.class);
query.setRange(0, 20);
query.setFilter("startDate >= startRangeParam && startDate <= endRangeParam");
query.declareParameters("java.util.Date startRangeParam, java.util.Date endRangeParam");
if (cursor != null) {
Map<String, Object> extensionMap = new HashMap<String, Object>();
extensionMap.put(JDOCursorHelper.CURSOR_EXTENSION, cursor);
query.setExtensions(extensionMap);
}
List<Event> events = (List<Event>) query.execute(thenDate, nowDate);
String cursorString =
JDOCursorHelper.getCursor(events).toWebSafeString();
TaskOptions options = TaskOptions.Builder
.url("/offline/getEventStats")
.param(CURSOR_KEY, cursorString)
.param(NOW_DATE, ""+nowDate.getTime())
.param(THEN_DATE, ""+thenDate.getTime());
countEvents(request, events, options);
groupEventsByChapterId(request, events, options);
groupEventsByCountry(request, events, options);
groupEventsByTopic(request, events, options);
if (events != null && events.size() > 0) {
Queue queue = QueueFactory.getDefaultQueue();
queue.add(options);
}
query.closeAll();
pm.close();
return null;
}
private void countEvents(HttpServletRequest request,
List<Event> events, TaskOptions options) {
final String KEY = "eventCount";
int eventCount = 0;
if (request.getParameter(KEY) != null) {
eventCount = Integer.parseInt(request.getParameter(KEY));
}
if (events == null || events.size() == 0) {
statsDao.updateField(JdoStatsDao.Field.NUM_EVENTS, eventCount);
} else {
for (Event event : events) {
eventCount++;
}
options = options.param(KEY, "" + eventCount);
}
}
private void groupEventsByChapterId(HttpServletRequest request,
List<Event> events, TaskOptions options) {
final String KEY = "eventsGroupedByChapterId";
JSONObject eventsGrouped = null;
if (request.getParameter(KEY) != null) {
eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY));
} else {
eventsGrouped = new JSONObject();
}
if (events == null || events.size() == 0) {
SummaryEmail summaryEmail = new SummaryEmail();
List<Chapter> chapters = chapterManager.getActiveChapters();
for (Chapter chapter : chapters) {
if (eventsGrouped.containsKey(chapter.getId().toString())) {
chapter.setNumEvents(((Long) eventsGrouped.get(chapter.getId().toString())).intValue());
} else {
chapter.setNumEvents(0);
}
GregorianCalendar threeMonthsAgo = new GregorianCalendar();
threeMonthsAgo.add(GregorianCalendar.MONTH, -3);
if (chapter.getStatus().equals("active")) {
if (chapter.getNumEvents() == 0) {
// send email
summaryEmail.addActiveToInactive(chapter.getName());
}
} else if (chapter.getStatus().equals("incubating")) {
if (chapter.getNumEvents() > 1) { // event requirements met
boolean makeActive = true;
if (chapter.getDescription() == null ||
chapter.getDescription().equals("")) {
makeActive = false;
}
if (chapter.getWebsite() == null ||
chapter.getWebsite().equals("")) {
makeActive = false;
}
if (chapter.getMailingList() == null ||
chapter.getMailingList().equals("")) {
makeActive = false;
}
if (makeActive) {
chapter.setStatus("active");
chapterManager.storeChapter(chapter);
// send email
summaryEmail.addIncubatingToActive(chapter.getName());
} else {
// send email
summaryEmail.addIncubatingEventRqmtsMet(chapter.getName());
}
}
}
}
summaryEmail.send();
// Sort the array on numEvents
for (int i = 1; i < chapters.size(); i++) {
int numEvents = chapters.get(i).getNumEvents();
int j = i - 1;
boolean done = false;
do {
if (chapters.get(j).getNumEvents() < numEvents) {
Chapter swapped = chapters.remove(j + 1);
chapters.add(j, swapped);
j--;
if (j < 0) {
done = true;
}
} else {
done = true;
}
} while (!done);
}
statsDao.updateField(JdoStatsDao.Field.CHAPTERS_SORTED_BY_NUM_EVENTS,
chapters);
} else {
for (Event event : events) {
long eventCount = 0;
if (eventsGrouped.containsKey(event.getChapterId().toString())) {
eventCount = (Long) eventsGrouped.get(event.getChapterId().toString());
}
eventsGrouped.put(""+event.getChapterId(), ++eventCount);
}
options = options.param(KEY, eventsGrouped.toJSONString());
}
}
private void groupEventsByCountry(HttpServletRequest request,
List<Event> events, TaskOptions options) {
final String KEY = "eventsGroupedByCountry";
JSONObject eventsGrouped = null;
if (request.getParameter(KEY) != null) {
eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY));
} else {
eventsGrouped = new JSONObject();
}
if (events == null || events.size() == 0) {
Map<String, Long> countryCounts = new HashMap<String, Long>();
List<Chapter> chapters = chapterManager.getActiveChapters();
for (Chapter chapter : chapters) {
if (eventsGrouped.containsKey(chapter.getId().toString())) {
long count = 0;
if (countryCounts.containsKey(chapter.getCountry())) {
count = countryCounts.get(chapter.getCountry());
}
countryCounts.put(chapter.getCountry(), count + (Long) eventsGrouped.get(chapter.getId().toString()));
}
}
List<Country> countries = new ArrayList<Country>();
for (Map.Entry<String, Long> countryEntry : countryCounts.entrySet()) {
Country country = new Country();
country.setName(countryEntry.getKey());
country.setNumEvents(countryEntry.getValue().intValue());
countries.add(country);
}
// Sort the array on numEvents
for (int i = 1; i < countries.size(); i++) {
int numEvents = countries.get(i).getNumEvents();
int j = i - 1;
boolean done = false;
do {
if (countries.get(j).getNumEvents() < numEvents) {
Country swapped = countries.remove(j + 1);
countries.add(j, swapped);
j--;
if (j < 0) {
done = true;
}
} else {
done = true;
}
} while (!done);
}
statsDao.updateField(JdoStatsDao.Field.COUNTRIES_SORTED_BY_NUM_EVENTS,
countries);
} else {
for (Event event : events) {
long eventCount = 0;
if (eventsGrouped.containsKey(event.getChapterId().toString())) {
eventCount = (Long) eventsGrouped.get(event.getChapterId().toString());
}
eventsGrouped.put(""+event.getChapterId(), ++eventCount);
}
options = options.param(KEY, eventsGrouped.toJSONString());
}
}
private void groupEventsByTopic(HttpServletRequest request,
List<Event> events, TaskOptions options) {
final String KEY = "eventsGroupedByTopic";
JSONObject eventsGrouped = null;
if (request.getParameter(KEY) != null) {
eventsGrouped = (JSONObject) JSONValue.parse(request.getParameter(KEY));
} else {
eventsGrouped = new JSONObject();
}
if (events == null || events.size() == 0) {
List<Topic> topics = new ArrayList<Topic>(eventsGrouped.size());
for (Map.Entry topicEntry : (Set<Map.Entry>) eventsGrouped.entrySet()) {
Topic topic = new Topic();
topic.setName((String) topicEntry.getKey());
topic.setNumEvents(((Long) topicEntry.getValue()).intValue());
topics.add(topic);
}
// Sort the array on numEvents
for (int i = 1; i < topics.size(); i++) {
int numEvents = topics.get(i).getNumEvents();
int j = i - 1;
boolean done = false;
do {
if (topics.get(j).getNumEvents() < numEvents) {
Topic swapped = topics.remove(j + 1);
topics.add(j, swapped);
j--;
if (j < 0) {
done = true;
}
} else {
done = true;
}
} while (!done);
}
statsDao.updateField(JdoStatsDao.Field.TOPICS_SORTED_BY_NUM_EVENTS,
topics);
} else {
for (Event event : events) {
Map<String, Boolean> topics = new HashMap<String, Boolean>();
topics.put(Topic.ANDROID, event.getTopics().contains(Topic.ANDROID));
topics.put(Topic.GOOGLE_AJAX_APIS, event.getTopics().contains(Topic.GOOGLE_AJAX_APIS));
topics.put(Topic.GOOGLE_APP_ENGINE, event.getTopics().contains(Topic.GOOGLE_APP_ENGINE));
topics.put(Topic.GOOGLE_CHROME_EXTENSIONS, event.getTopics().contains(Topic.GOOGLE_CHROME_EXTENSIONS));
topics.put(Topic.GOOGLE_EARTH_APIS, event.getTopics().contains(Topic.GOOGLE_EARTH_APIS));
topics.put(Topic.GOOGLE_MAPS_APIS, event.getTopics().contains(Topic.GOOGLE_MAPS_APIS));
topics.put(Topic.GOOGLE_WAVE_APIS, event.getTopics().contains(Topic.GOOGLE_WAVE_APIS));
topics.put(Topic.GOOGLE_WEB_TOOLKIT, event.getTopics().contains(Topic.GOOGLE_WEB_TOOLKIT));
topics.put(Topic.OPENSOCIAL, event.getTopics().contains(Topic.OPENSOCIAL));
for (Map.Entry<String, Boolean> topicEntry : topics.entrySet()) {
if (topicEntry.getValue() != null && topicEntry.getValue()) {
long count = 0;
if (eventsGrouped.containsKey(topicEntry.getKey())) {
count = (Long) eventsGrouped.get(topicEntry.getKey());
}
eventsGrouped.put(topicEntry.getKey(), ++count);
}
}
}
options = options.param(KEY, eventsGrouped.toJSONString());
}
}
}