com.modusoperandi.dragonfly.widgets.chart.ScatterChartPage.java Source code

Java tutorial

Introduction

Here is the source code for com.modusoperandi.dragonfly.widgets.chart.ScatterChartPage.java

Source

/**
 * Copyright (c) 2016 Modus Operandi, Inc.
 *
 * This 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 any later version.
 *
 * This program 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. A copy of the GNU Lesser General Public License is distributed along
 * with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package com.modusoperandi.dragonfly.widgets.chart;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.TextRequestHandler;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.sort.SortOrder;

/**
 * The Class ScatterChartPage - renders a page with a set of predefined charts
 */
public class ScatterChartPage extends WebPage {

    public class TsvXyAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getTsvXyUrl() { return '").append(getCallbackUrl()).append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getTsvXy"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            String indexName = configuration.get("Index");
            String xField = configuration.get("X");
            String yField = configuration.get("Y");

            StringBuilder tsvResponse = new StringBuilder();
            tsvResponse.append("x\ty\n");

            if ((indexName != null) && !indexName.isEmpty()) {
                // Make query
                final SearchResponse results = esMakeQuery(filterTerm, null);

                for (final SearchHit hit : results.getHits()) {

                    if ((hit.getSource().get(yField) != null) && (hit.getSource().get(xField) != null)) {

                        String x = hit.getSource().get(xField).toString();
                        String y = hit.getSource().get(yField).toString();

                        tsvResponse.append(x).append("\t");
                        tsvResponse.append(y).append("\n");
                    }
                }
            }

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    tsvResponse.toString());
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);
        }
    }

    public class TsvYCountAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getTsvYCountUrl() { return '").append(getCallbackUrl()).append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getTsvYCount"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            String indexName = configuration.get("Index");
            String field = configuration.get("Y");
            String sort = configuration.get("X");

            StringBuilder tsvResponse = new StringBuilder();
            tsvResponse.append("x\ty\txlabel\n");

            if ((indexName != null) && !indexName.isEmpty()) {
                // Make query
                final SearchResponse results = esMakeQuery(filterTerm, sort);

                int count = 0;
                for (final SearchHit hit : results.getHits()) {

                    if (hit.getSource().get(field) != null) {

                        String value = hit.getSource().get(field).toString();
                        String xlabel = hit.getSource().get(sort).toString();

                        tsvResponse.append(Integer.toString(count)).append("\t");
                        tsvResponse.append(value).append("\t");
                        tsvResponse.append(xlabel).append("\n");

                        count++;
                    }
                }
            }

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    tsvResponse.toString());
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);
        }
    }

    public class TsvLabelCountAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getTsvLabelCountUrl() { return '").append(getCallbackUrl()).append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getTsvLabelCount"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            String indexName = configuration.get("Index");
            String countField = configuration.get("CountField");

            StringBuilder tsvResponse = new StringBuilder();
            tsvResponse.append("Label\tValue\n");

            if ((indexName != null) && !indexName.isEmpty()) {
                // Make query
                final SearchResponse response = esMakeFacetQuery(countField);

                if (response.getAggregations() != null) {
                    Terms f = response.getAggregations().get("f");
                    Collection<Terms.Bucket> buckets = f.getBuckets();

                    buckets.stream().map((bucket) -> {
                        tsvResponse.append(bucket.getKeyAsString());
                        return bucket;
                    }).map((bucket) -> {
                        tsvResponse.append("\t");
                        tsvResponse.append(Long.toString(bucket.getDocCount()));
                        return bucket;
                    }).forEach((_item) -> {
                        tsvResponse.append("\n");
                    });
                }
            }

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    tsvResponse.toString());
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);
        }
    }

    public class TsvPercentCountAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getTsvPercentCountUrl() { return '").append(getCallbackUrl()).append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getTsvPercentCount"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            String indexName = configuration.get("Index");
            String countField = configuration.get("CountField");

            StringBuilder tsvResponse = new StringBuilder();
            tsvResponse.append("label\tvalue\n");

            if ((indexName != null) && !indexName.isEmpty()) {
                // Make query
                final SearchResponse response = esMakeFacetQuery(countField);

                if (response.getAggregations() != null) {
                    Terms f = response.getAggregations().get("f");
                    Collection<Terms.Bucket> buckets = f.getBuckets();

                    for (Terms.Bucket bucket : buckets) {
                        tsvResponse.append(bucket.getKeyAsString());
                        tsvResponse.append("\t");
                        tsvResponse.append(Long.toString(bucket.getDocCount()));
                        tsvResponse.append("\n");
                    }

                    tsvResponse.append("Other");
                    tsvResponse.append("\t");
                    tsvResponse.append(Long.toString(f.getSumOfOtherDocCounts()));
                    tsvResponse.append("\n");
                }
            }

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    tsvResponse.toString());
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);
        }
    }

    public class JsonHeatMapRingMapAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;

        private final SimpleDateFormat dateParser = new SimpleDateFormat("MM/dd/yyyy HH:mm");
        double north = 90;
        double south = -90;
        double east = 180;
        double west = -180;
        String locationField;
        String index;
        String labelField;
        String timeField;
        String timeScale;
        String orgFilterTerm;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getJsonHeatMapRingMapUrl() { return '").append(getCallbackUrl())
                    .append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getHeatMapRingMap"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            try {
                north = Double
                        .parseDouble(getRequest().getRequestParameters().getParameterValue("north").toString());
                south = Double
                        .parseDouble(getRequest().getRequestParameters().getParameterValue("south").toString());
                east = Double.parseDouble(getRequest().getRequestParameters().getParameterValue("east").toString());
                west = Double.parseDouble(getRequest().getRequestParameters().getParameterValue("west").toString());
                index = getRequest().getRequestParameters().getParameterValue("index").toString();
                locationField = getRequest().getRequestParameters().getParameterValue("locationField").toString();
                timeField = getRequest().getRequestParameters().getParameterValue("timeField").toString();
                timeScale = getRequest().getRequestParameters().getParameterValue("timeScale").toString();
                labelField = getRequest().getRequestParameters().getParameterValue("labelField").toString();
                orgFilterTerm = getRequest().getRequestParameters().getParameterValue("filterTerm").toString();

            } catch (final NumberFormatException | NullPointerException e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);

                north = 90;
                south = -90;
                east = 180;
                west = -180;
            }

            String jsonResponse = "{";

            jsonResponse += getRingData();

            jsonResponse += "}";

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    jsonResponse);
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);

        }

        private SearchResponse esMakeGeoHistogramQuery(DateHistogramInterval theInterval) {

            QueryBuilder query = QueryBuilders.matchAllQuery();
            if ((orgFilterTerm != null) && !orgFilterTerm.isEmpty()) {
                query = QueryBuilders.matchQuery("_all", orgFilterTerm);
            }

            final SearchRequestBuilder search = getEsClient().prepareSearch(index);

            if ((south > -90) && (north < 90) && (east < 180) && (west > -180)) {
                final QueryBuilder geoQuery = QueryBuilders.geoBoundingBoxQuery(locationField).topLeft(north, west)
                        .bottomRight(south, east);
                search.setQuery(QueryBuilders.boolQuery().must(query).must(geoQuery));

            } else {
                search.setQuery(query);
            }

            search.addAggregation(AggregationBuilders.dateHistogram("instances_over_time").field(timeField)
                    .interval(theInterval).minDocCount(0));

            search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
            search.setSize(0);

            return search.execute().actionGet();
        }

        private SearchResponse esMakeGeoDateQuery(String date, String toDate) {

            QueryBuilder termQuery;

            if ((orgFilterTerm != null) && !orgFilterTerm.isEmpty()) {
                termQuery = QueryBuilders.matchQuery("_all", orgFilterTerm);

            } else {
                termQuery = QueryBuilders.matchAllQuery();

            }

            QueryBuilder timeQuery = QueryBuilders.boolQuery().must(termQuery).must(QueryBuilders
                    .rangeQuery(timeField).from(date).to(toDate).includeLower(true).includeUpper(false));

            final SearchRequestBuilder search = getEsClient().prepareSearch(index);

            if ((south > -90) && (north < 90) && (east < 180) && (west > -180)) {
                final QueryBuilder geoQuery = QueryBuilders.geoBoundingBoxQuery(locationField).topLeft(north, west)
                        .bottomRight(south, east);
                search.setQuery(QueryBuilders.boolQuery().must(termQuery).must(geoQuery));

            } else {
                search.setQuery(termQuery);
            }

            search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
            search.setSize(10000);

            return search.execute().actionGet();
        }

        private DateHistogramInterval getInterval() {

            switch (timeScale) {
            case "Years":
                return DateHistogramInterval.YEAR;

            case "Months":
                return DateHistogramInterval.MONTH;

            case "Days":
                return DateHistogramInterval.DAY;

            case "Hours":
                return DateHistogramInterval.HOUR;

            case "Minutes":
                return DateHistogramInterval.MINUTE;

            case "Seconds":
                return DateHistogramInterval.SECOND;
            }

            return DateHistogramInterval.YEAR;
        }

        private int getDateInterval() {

            switch (timeScale) {
            case "Years":
                return Calendar.YEAR;

            case "Months":
                return Calendar.MONTH;

            case "Days":
                return Calendar.DATE;

            case "Hours":
                return Calendar.HOUR_OF_DAY;

            case "Minutes":
                return Calendar.MINUTE;

            case "Seconds":
                return Calendar.SECOND;
            }

            return Calendar.HOUR_OF_DAY;
        }

        private String getRingData() {

            long min = 0;
            long max = 0;
            long total = 0;

            String ringData = "";

            ringData += jsonAttr("ringData");
            ringData += "[";

            try {

                SearchResponse response = esMakeGeoHistogramQuery(getInterval());

                total = response.getHits().getTotalHits();

                if (response.getAggregations() != null) {
                    Histogram aggregations = response.getAggregations().get("instances_over_time");

                    boolean firstTime = true;

                    for (Histogram.Bucket entry : aggregations.getBuckets()) {

                        if (!firstTime) {
                            ringData += ",";
                            min = entry.getDocCount();
                        }
                        firstTime = false;

                        if (entry.getDocCount() > max) {
                            max = entry.getDocCount();
                        }
                        if (entry.getDocCount() < min) {
                            min = entry.getDocCount();
                        }

                        ringData += "{";
                        ringData += jsonAttr("label") + jsonValue(entry.getKeyAsString()) + ",";
                        ringData += jsonAttr("count") + Long.toString(entry.getDocCount()) + ",";

                        ringData += jsonAttr("events");
                        if (total < 10000) {
                            ringData += getEventList(entry.getKeyAsString(),
                                    getEndInterval(entry.getKeyAsString()));
                        } else {
                            ringData += "[]";
                        }
                        ringData += "}";
                    }
                }
            } catch (Exception e) {
                LOGGER.log(Level.SEVERE, e.getMessage(), e);
            }

            ringData += "],";
            ringData += jsonAttr("minCount") + Long.toString(min) + ",";
            ringData += jsonAttr("maxCount") + Long.toString(max) + ",";
            ringData += jsonAttr("totalCount") + Long.toString(total);

            return ringData;
        }

        private String getEndInterval(String dateStr) {

            try {
                Date date = dateParser.parse(dateStr);

                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                cal.add(getDateInterval(), 1);
                return dateParser.format(cal.getTime());

            } catch (ParseException ex) {
                LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            }

            return dateStr;
        }

        private String getEventList(String date, String toDate) {

            String eventList = "[";

            SearchResponse response = esMakeGeoDateQuery(date, toDate);

            boolean firstTime = true;
            for (SearchHit hit : response.getHits().getHits()) {
                if (!firstTime) {
                    eventList += ",";
                }
                firstTime = false;

                eventList += "{";
                eventList += jsonAttr("id") + jsonValue(hit.getId()) + ",";
                eventList += jsonAttr("label") + jsonValue(hit.getSource().get(labelField).toString()) + ",";

                final GeoPoint location = new GeoPoint();
                location.resetFromString(hit.getSource().get(locationField).toString());

                final BigDecimal lat = new BigDecimal(location.lat()).setScale(11, BigDecimal.ROUND_HALF_DOWN);
                final BigDecimal lon = new BigDecimal(location.lon()).setScale(11, BigDecimal.ROUND_HALF_DOWN);
                eventList += jsonAttr("lon") + jsonValue(Double.toString(lon.doubleValue())) + ",";
                eventList += jsonAttr("lat") + jsonValue(Double.toString(lat.doubleValue()));
                eventList += "}";
            }

            eventList += "]";
            return eventList;
        }
    }

    public class JsonTimewheelAjaxBehavior extends AbstractDefaultAjaxBehavior {

        private static final long serialVersionUID = 1L;
        private long[][] buckets;
        private int numRings;
        private int numSlices;
        private int labelOffset;

        @Override
        public void renderHead(final Component component, final IHeaderResponse response) {

            final StringBuffer getTsv = new StringBuffer();
            getTsv.append("function getJsonTimewheelUrl() { return '").append(getCallbackUrl()).append("'; };");

            super.renderHead(component, response);
            response.render(JavaScriptHeaderItem.forScript(getTsv.toString(), "getJsonTimewheel"));
        }

        @Override
        protected void respond(final AjaxRequestTarget target) {

            String jsonResponse = "";

            String indexName = configuration.get("Index");
            String countField = configuration.get("CountField");

            allocateBuckets();

            SearchResponse response = esMakeHistogramQuery(getInterval());

            try {
                if (response.getAggregations() != null) {
                    Histogram aggregations = response.getAggregations().get("instances_over_time");

                    SimpleDateFormat dateParser = new SimpleDateFormat("MM/dd/yyyy HH:mm");
                    for (Histogram.Bucket entry : aggregations.getBuckets()) {

                        Calendar day = Calendar.getInstance();
                        day.setTime(dateParser.parse(entry.getKeyAsString()));
                        buckets[getRingIndex(day)][getSliceIndex(day)] += entry.getDocCount();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            jsonResponse += "{";
            jsonResponse += jsonAttr("maxContrastFactor") + getMaxContrastFactor() + ",";
            jsonResponse += jsonAttr("minContrastFactor") + getMinContrastFactor() + ",";
            jsonResponse += jsonAttr("ary") + "[";
            for (int i = 0; i < numRings; i++) {
                if (i != 0) {
                    jsonResponse += ",";
                }
                jsonResponse += getRingJson(buckets[i], i);
            }
            jsonResponse += "]}";

            final TextRequestHandler textRequestHandler = new TextRequestHandler("application/json", "UTF-8",
                    jsonResponse);
            RequestCycle.get().scheduleRequestHandlerAfterCurrent(textRequestHandler);

        }

        private SearchResponse esMakeHistogramQuery(DateHistogramInterval theInterval) {

            QueryBuilder query = QueryBuilders.matchAllQuery();
            if ((filterTerm != null) && !filterTerm.isEmpty()) {
                query = QueryBuilders.matchQuery("_all", filterTerm);
            }

            final SearchRequestBuilder search = getEsClient().prepareSearch(configuration.get("Index"))
                    .setQuery(query).addAggregation(AggregationBuilders.dateHistogram("instances_over_time")
                            .field(configuration.get("TimeField")).interval(theInterval));

            search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
            search.setSize(0);

            return search.execute().actionGet();
        }

        private DateHistogramInterval getInterval() {

            switch (configuration.get("TimeScale")) {
            case "Years/Months":
                return DateHistogramInterval.MONTH;

            case "Months/Days":
                return DateHistogramInterval.DAY;

            case "Days/Hours":
                return DateHistogramInterval.HOUR;
            }

            return DateHistogramInterval.YEAR;
        }

        private double getMaxContrastFactor() {

            switch (configuration.get("ContrastScale")) {
            case "-90%":
                return 0.9;

            case "-75%":
                return 0.75;

            case "-50%":
                return 0.50;

            case "-25%":
                return 0.25;
            }

            return 0;
        }

        private double getMinContrastFactor() {

            switch (configuration.get("ContrastScale")) {
            case "+90%":
                return 0.9;

            case "+75%":
                return 0.75;

            case "+50%":
                return 0.50;

            case "+25%":
                return 0.25;
            }

            return 0;
        }

        private String getRingJsonDaysHours(long[] ringData, int day) {

            String[] days = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thrusday", "Friday", "Saturday" };

            String jsonString = "";

            jsonString += "{";
            jsonString += jsonAttr("ring") + jsonValue(days[day]) + ",";
            jsonString += jsonAttr("data");
            jsonString += "[";
            for (int i = 0; i < ringData.length; i++) {
                if (i != 0) {
                    jsonString += ",";
                }
                jsonString += "{";
                jsonString += jsonAttr("label") + jsonValue(Integer.toString(i)) + ",";
                jsonString += jsonAttr("value") + Long.toString(ringData[i]);
                jsonString += "}";
            }
            jsonString += "]";
            jsonString += "}";

            return jsonString;
        }

        private String getRingJsonMonthsDays(long[] ringData, int day) {

            String[] days = { "January", "Feburary", "March", "April", "May", "June", "July", "August", "September",
                    "October", "November", "December" };

            String jsonString = "";

            jsonString += "{";
            jsonString += jsonAttr("ring") + jsonValue(days[day]) + ",";
            jsonString += jsonAttr("data");
            jsonString += "[";
            for (int i = 0; i < ringData.length; i++) {
                if (i != 0) {
                    jsonString += ",";
                }
                jsonString += "{";
                jsonString += jsonAttr("label") + jsonValue(Integer.toString(i + labelOffset)) + ",";
                jsonString += jsonAttr("value") + Long.toString(ringData[i]);
                jsonString += "}";
            }
            jsonString += "]";
            jsonString += "}";

            return jsonString;
        }

        private int getRingIndex(Calendar day) {

            switch (configuration.get("TimeScale")) {
            case "Years/Months":
                break;

            case "Months/Days":
                return day.get(Calendar.MONTH);

            case "Days/Hours":
                return day.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY;
            }

            return 0;
        }

        private int getSliceIndex(Calendar day) {

            switch (configuration.get("TimeScale")) {
            case "Years/Months":
                break;

            case "Months/Days":
                return day.get(Calendar.DAY_OF_MONTH) - 1;

            case "Days/Hours":
                return day.get(Calendar.HOUR_OF_DAY);
            }

            return 0;
        }

        private String getRingJson(long[] ringData, int day) {

            switch (configuration.get("TimeScale")) {
            case "Years/Months":
                break;

            case "Months/Days":
                return getRingJsonMonthsDays(ringData, day);

            case "Days/Hours":
                return getRingJsonDaysHours(ringData, day);
            }

            return getRingJsonDaysHours(ringData, day);
        }

        private void allocateBuckets() {

            switch (configuration.get("TimeScale")) {
            case "Years/Months":
                Calendar day = Calendar.getInstance();
                labelOffset = day.get(Calendar.YEAR);
                numRings = 10;
                numSlices = 12;
                break;

            case "Months/Days":
                labelOffset = 1;
                numRings = 12;
                numSlices = 31;
                break;

            case "Days/Hours":
                labelOffset = 0;
                numRings = 7;
                numSlices = 24;
                break;
            }

            buckets = new long[numRings][numSlices];
        }
    }

    private SearchResponse esMakeQuery(String filter, String sort) {

        QueryBuilder query = QueryBuilders.matchAllQuery();
        if ((filterTerm != null) && !filterTerm.isEmpty()) {
            query = QueryBuilders.matchQuery("_all", filterTerm);
        }

        final SearchRequestBuilder search = getEsClient().prepareSearch(configuration.get("Index"));
        search.setQuery(query);
        search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
        search.setSize(10000);

        System.out.println("TO DO: fix script filters");
        // if ((filter != null) && !filter.trim().equals("")) {
        // search.setPostFilter(FilterBuilders.scriptFilter(filter));
        // }

        if ((sort != null) && !sort.trim().equals("")) {
            search.addSort(sort, SortOrder.ASC);
        }
        return search.execute().actionGet();
    }

    private SearchResponse esMakeFacetQuery(String facetField) {

        QueryBuilder query = QueryBuilders.matchAllQuery();
        if ((filterTerm != null) && !filterTerm.isEmpty()) {
            query = QueryBuilders.matchQuery("_all", filterTerm);
        }

        final SearchRequestBuilder search = getEsClient().prepareSearch(configuration.get("Index"));
        search.setQuery(query);
        search.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
        search.setSize(0);

        if ((facetField != null) && !facetField.trim().equals("")) {
            search.addAggregation(AggregationBuilders.terms("f").field(facetField)
                    .size(Integer.parseInt(configuration.get("MaxItems"))));
        }

        return search.execute().actionGet();
    }

    private static final Logger LOGGER = Logger.getLogger(ScatterChartPage.class.getName());
    private static final long serialVersionUID = 1L;

    private final ModalWindow addWindow;
    private final List<String> fieldList = new LinkedList<String>();
    private String filterTerm;
    private final SettingsPanel d3SettingsPanel;
    private String hits = "0";
    private final Label hitsField;
    private final TextField<String> termFilterField;
    private Map<String, String> configuration;
    private int chartId = 1;

    private transient static Client esClient = null;

    private class AddAjaxButton extends AjaxButton {

        private static final long serialVersionUID = 1L;

        public AddAjaxButton() {

            super("addButton");
        }

        @Override
        protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {

            addWindow.show(target);
        }
    }

    private class RefreshAjaxButton extends AjaxButton {

        private static final long serialVersionUID = 1L;

        public RefreshAjaxButton() {

            super("refreshButton");
        }

        @Override
        protected void onSubmit(final AjaxRequestTarget target, final Form<?> form) {

            // refreshDataOnMap(heatMapSettingsPanel.getConfiguration(), target);
        }
    }

    /**
     * Instantiates a new portal page.
     *
     * @param parameters
     */
    public ScatterChartPage(final PageParameters parameters) {

        LOGGER.info("parameters = " + parameters.toString());

        add(new TsvXyAjaxBehavior());
        add(new TsvYCountAjaxBehavior());
        add(new TsvLabelCountAjaxBehavior());
        add(new TsvPercentCountAjaxBehavior());
        add(new JsonTimewheelAjaxBehavior());
        add(new JsonHeatMapRingMapAjaxBehavior());

        // /////////////////////////////
        // Elasticsearch
        getEsClient();

        // /////////////////////////////
        // UI
        final Form<?> queryForm = new Form<Void>("queryForm");
        add(queryForm);

        queryForm.add(new AddAjaxButton());

        final RefreshAjaxButton refreshButton = new RefreshAjaxButton();
        queryForm.add(refreshButton);
        queryForm.setDefaultButton(refreshButton);

        termFilterField = new TextField<String>("termFilter", new PropertyModel<String>(this, "filterTerm"));
        queryForm.add(termFilterField);

        hitsField = new Label("hits", new PropertyModel<String>(this, "hits"));
        hitsField.setOutputMarkupId(true);
        queryForm.add(hitsField);

        // /////////////////////////////
        // Add Panel
        addWindow = new ModalWindow("dataPanel");
        d3SettingsPanel = new SettingsPanel(addWindow.getContentId(), addWindow);
        addWindow.setContent(d3SettingsPanel);
        addWindow.setCookieName(null);
        addWindow.setInitialHeight(400);
        addWindow.setHeightUnit("px");
        addWindow.setInitialWidth(620);
        addWindow.setWidthUnit("px");
        addWindow.setResizable(false);
        addWindow.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {

            private static final long serialVersionUID = 8118424010410661292L;

            @Override
            public void onClose(final AjaxRequestTarget target) {

                if (d3SettingsPanel.isOk()) {

                    configuration = d3SettingsPanel.getConfiguration();

                    if (configuration.get("Type").equals(SettingsPanel.XY)) {
                        target.appendJavaScript("drawScatterChart(\"" + configuration.get("X") + "\",\""
                                + configuration.get("Y") + "\");");

                    } else if (configuration.get("Type").equals(SettingsPanel.FACET_BAR)) {
                        target.appendJavaScript("drawBarChart(\"" + configuration.get("CountField") + "\");");

                    } else if (configuration.get("Type").equals(SettingsPanel.SERIES_LINE)) {
                        target.appendJavaScript("drawLineChart(\"" + configuration.get("X") + "\",\""
                                + configuration.get("Y") + "\");");

                    } else if (configuration.get("Type").equals(SettingsPanel.FACET_PIE)) {
                        target.appendJavaScript("drawPieChart(\"" + configuration.get("CountField") + "\");");

                    } else if (configuration.get("Type").equals(SettingsPanel.TIMEWHEEL)) {
                        target.appendJavaScript("drawTimewheelChart();");

                    } else if (configuration.get("Type").equals(SettingsPanel.HEAT_MAP_RING_MAP)) {
                        target.appendJavaScript("drawHeatMapRingMap(" + Integer.toString(chartId) + ", "
                                + jsString(configuration.get("Index")) + ", "
                                + jsString(configuration.get("LocationField")) + ", "
                                + jsString(configuration.get("TimeField")) + ", "
                                + jsString(configuration.get("TimeScale")) + ", "
                                + jsString(configuration.get("LabelField")) + ", " + jsString(filterTerm) + ");");
                    }

                    chartId++;
                }
            }
        });
        add(addWindow);
    }

    private String jsString(String value) {

        String rtnValue = "";

        if (value != null) {
            rtnValue += "'";
        }

        rtnValue += value;

        if (value != null) {
            rtnValue += "'";
        }

        return rtnValue;
    }

    private static Client getEsClient() {

        // ////////////
        // Remote
        if (esClient == null) {
            try {
                final Settings settings = Settings.settingsBuilder()
                        .put("node.name", "DRAGONFLY_" + Long.toString(Thread.currentThread().getId()))
                        .put("cluster.name", "DRAGONFLY").build();
                esClient = new TransportClient.Builder().settings(settings).build().addTransportAddress(
                        new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));
            } catch (UnknownHostException ex) {
                LOGGER.log(Level.SEVERE, null, ex);
            }
        }

        return esClient;
    }

    private String jsonAttr(String attr) {

        return "\"" + attr + "\"" + ":";
    }

    private String jsonValue(String value) {

        return "\"" + value + "\"";
    }
}