org.rapidandroid.activity.chart.form.FormDataBroker.java Source code

Java tutorial

Introduction

Here is the source code for org.rapidandroid.activity.chart.form.FormDataBroker.java

Source

/*
 * Copyright (C) 2009 Dimagi Inc., UNICEF
 *
 * 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.rapidandroid.activity.chart.form;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.rapidandroid.activity.chart.ChartBroker;
import org.rapidandroid.activity.chart.JSONGraphData;
import org.rapidandroid.data.RapidSmsDBConstants;
import org.rapidandroid.data.controller.ParsedDataReporter;
import org.rapidsms.java.core.Constants;
import org.rapidsms.java.core.model.Field;
import org.rapidsms.java.core.model.Form;
import org.rapidsms.java.core.model.Message;

import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import android.webkit.WebView;

public class FormDataBroker extends ChartBroker {
    public static final int PLOT_ALL_MESSAGES_FOR_FORM = 0;
    public static final int PLOT_NUMERIC_FIELD_VALUE = 1;
    public static final int PLOT_NUMERIC_FIELD_ADDITIVE = 2;
    public static final int PLOT_WORD_HISTOGRAM = 3;
    public static final int PLOT_NUMERIC_FIELD_COUNT_HISTOGRAM = 4;

    private Form mForm;
    private Field fieldToPlot;

    public FormDataBroker(Activity parentActivity, WebView appView, Form form, Date startDate, Date endDate) {
        super(parentActivity, appView, startDate, endDate);
        mForm = form;
        mVariableStrings = new String[mForm.getFields().length + 1];
        mVariableStrings[0] = "Messages over time";
        for (int i = 1; i < mVariableStrings.length; i++) {
            Field f = mForm.getFields()[i - 1];
            mVariableStrings[i] = f.getName() + "  [" + f.getFieldType().getParsedDataType() + "]";
        }
    }

    @Override
    public void doLoadGraph() {
        // mProgress = ProgressDialog.show(mAppView.getContext(),
        // "Rendering Graph...", "Please Wait",true,false);
        JSONGraphData allData = null;

        if (fieldToPlot == null) {
            // we're going to do all messages over timereturn;
            allData = loadMessageOverTimeHistogram();
        } else if (fieldToPlot.getFieldType().getParsedDataType().toLowerCase(Locale.getDefault()).equals("word")) {
            allData = loadHistogramFromField();
        } else if (fieldToPlot.getFieldType().getParsedDataType().toLowerCase(Locale.getDefault()).equals("boolean")
                || fieldToPlot.getFieldType().getParsedDataType().toLowerCase(Locale.getDefault())
                        .equals("yes/no")) {
            allData = loadBooleanPlot();
        } else {
            allData = loadNumericLine();
            // data.put(loadNumericLine());
        }
        if (allData != null) {
            mGraphData = allData.getData();
            mGraphOptions = allData.getOptions();
        }
        Log.d("FormDataBroker", mGraphData.toString());
        Log.d("FormDataBroker", mGraphOptions.toString());
    }

    private JSONGraphData loadBooleanPlot() {

        Date startDateToUse = getStartDate();
        DateDisplayTypes displayType = this.getDisplayType(startDateToUse, mEndDate);

        String selectionArg = getSelectionString(displayType);

        StringBuilder rawQuery = new StringBuilder();

        String fieldcol = RapidSmsDBConstants.FormData.COLUMN_PREFIX + fieldToPlot.getName();

        rawQuery.append("select time, " + fieldcol + ", count(*) from  ");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());

        rawQuery.append(" join rapidandroid_message on (");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());
        rawQuery.append(".message_id = rapidandroid_message._id");
        rawQuery.append(") ");
        if (startDateToUse.compareTo(Constants.NULLDATE) != 0 && mEndDate.compareTo(Constants.NULLDATE) != 0) {
            rawQuery.append(" WHERE rapidandroid_message.time > '" + Message.SQLDateFormatter.format(startDateToUse)
                    + "' AND rapidandroid_message.time < '" + Message.SQLDateFormatter.format(mEndDate) + "' ");
        }

        rawQuery.append(" group by ").append(selectionArg).append(", " + fieldcol);
        rawQuery.append(" order by ").append("time").append(" ASC");

        SQLiteDatabase db = rawDB.getReadableDatabase();
        // the string value is column 0
        // the magnitude is column 1
        Log.d("query", rawQuery.toString());
        Cursor cr = db.rawQuery(rawQuery.toString(), null);
        // TODO Auto-generated method stub
        int barCount = cr.getCount();
        Date[] allDates = new Date[barCount];
        if (barCount == 0) {
            db.close();
            cr.close();
        } else {
            List<Date> xValsTrue = new ArrayList<Date>();
            // Date[] xValsTrue = new Date[barCount];
            List<Integer> yValsTrue = new ArrayList<Integer>();
            List<Date> xValsFalse = new ArrayList<Date>();
            // Date[] xValsTrue = new Date[barCount];
            List<Integer> yValsFalse = new ArrayList<Integer>();
            cr.moveToFirst();
            int i = 0;
            do {
                String trueFalse = cr.getString(1);
                // int trueFalse2 = cr.getInt(fieldcol);
                // String trueFalseStr = cr.getString(1);

                Date thisDate = getDate(displayType, cr.getString(0));
                Log.d("FormDataBroker: ", cr.getString(0) + ", " + trueFalse + " , " + cr.getInt(2));

                if (trueFalse.equals("true")) {
                    xValsFalse.add(thisDate);
                    yValsFalse.add(new Integer(cr.getInt(2)));
                } else {
                    xValsTrue.add(thisDate);
                    yValsTrue.add(new Integer(cr.getInt(2)));
                }
                allDates[i] = thisDate;
                i++;
            } while (cr.moveToNext());

            try {
                //            String legend = this.getLegendString(displayType);
                int[] yVals = getIntsFromList(yValsTrue);
                JSONArray trueArray = getJSONArrayForValues(displayType, xValsTrue.toArray(new Date[0]), yVals);
                yVals = getIntsFromList(yValsFalse);
                JSONArray falseArray = getJSONArrayForValues(displayType, xValsFalse.toArray(new Date[0]), yVals);
                JSONArray finalValues = new JSONArray();
                JSONObject trueElem = new JSONObject();
                trueElem.put("data", trueArray);
                trueElem.put("label", "Yes");
                trueElem.put("lines", getShowTrue());
                finalValues.put(trueElem);
                JSONObject falseElem = new JSONObject();
                falseElem.put("data", falseArray);
                falseElem.put("label", "No");
                falseElem.put("lines", getShowTrue());
                finalValues.put(falseElem);
                return new JSONGraphData(finalValues, loadOptionsForDateGraph(allDates, true, displayType));

            } catch (Exception ex) {

            } finally {
                if (!cr.isClosed()) {

                    cr.close();
                }
                if (db.isOpen()) {
                    db.close();
                }
            }
        }
        // either there was no data or something bad happened
        return new JSONGraphData(getEmptyData(), new JSONObject());
    }

    private int[] getIntsFromList(List<Integer> values) {
        int[] toReturn = new int[values.size()];
        for (int i = 0; i < values.size(); i++) {
            toReturn[i] = values.get(i);
        }
        return toReturn;
    }

    private JSONGraphData loadNumericLine() {
        Date startDateToUse = getStartDate();

        SQLiteDatabase db = rawDB.getReadableDatabase();

        String fieldcol = RapidSmsDBConstants.FormData.COLUMN_PREFIX + fieldToPlot.getName();
        StringBuilder rawQuery = new StringBuilder();
        rawQuery.append("select rapidandroid_message.time, " + fieldcol);
        rawQuery.append(" from ");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());

        rawQuery.append(" join rapidandroid_message on (");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());
        rawQuery.append(".message_id = rapidandroid_message._id");
        rawQuery.append(") ");

        if (startDateToUse.compareTo(Constants.NULLDATE) != 0 && mEndDate.compareTo(Constants.NULLDATE) != 0) {
            rawQuery.append(" WHERE rapidandroid_message.time > '" + Message.SQLDateFormatter.format(startDateToUse)
                    + "' AND rapidandroid_message.time < '" + Message.SQLDateFormatter.format(mEndDate) + "' ");
        }

        rawQuery.append(" order by rapidandroid_message.time ASC");

        // the string value is column 0
        // the magnitude is column 1

        Cursor cr = db.rawQuery(rawQuery.toString(), null);
        int barCount = cr.getCount();

        if (barCount == 0) {
            cr.close();
        } else {
            Date[] xVals = new Date[barCount];
            int[] yVals = new int[barCount];
            cr.moveToFirst();
            int i = 0;
            do {
                try {
                    xVals[i] = Message.SQLDateFormatter.parse(cr.getString(0));
                    yVals[i] = cr.getInt(1);
                } catch (Exception ex) {

                }
                i++;
            } while (cr.moveToNext());

            // xaxis: { ticks: [0, [Math.PI/2, "\u03c0/2"], [Math.PI, "\u03c0"],
            // [Math.PI * 3/2, "3\u03c0/2"], [Math.PI * 2, "2\u03c0"]]},

            try {
                return new JSONGraphData(prepareDateData(xVals, yVals),
                        loadOptionsForDateGraph(xVals, false, DateDisplayTypes.Daily));
            } catch (Exception ex) {

            } finally {
                if (!cr.isClosed()) {
                    cr.close();
                }
            }

        }
        // either there was no data or something bad happened
        return new JSONGraphData(getEmptyData(), new JSONObject());
    }

    private JSONArray prepareDateData(Date[] xvals, int[] yvals) {
        JSONArray outerArray = new JSONArray();
        JSONArray innerArray = new JSONArray();
        int datalen = xvals.length;
        for (int i = 0; i < datalen; i++) {
            JSONArray elem = new JSONArray();
            elem.put(xvals[i].getTime());
            elem.put(yvals[i]);
            innerArray.put(elem);
        }
        outerArray.put(innerArray);
        return outerArray;
    }

    private JSONGraphData loadMessageOverTimeHistogram() {
        Date startDateToUse = getStartDate();
        DateDisplayTypes displayType = this.getDisplayType(startDateToUse, mEndDate);

        String selectionArg = getSelectionString(displayType);

        StringBuilder rawQuery = new StringBuilder();

        rawQuery.append("select time, count(*) from  ");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());

        rawQuery.append(" join rapidandroid_message on (");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());
        rawQuery.append(".message_id = rapidandroid_message._id");
        rawQuery.append(") ");
        if (startDateToUse.compareTo(Constants.NULLDATE) != 0 && mEndDate.compareTo(Constants.NULLDATE) != 0) {
            rawQuery.append(" WHERE rapidandroid_message.time > '" + Message.SQLDateFormatter.format(startDateToUse)
                    + "' AND rapidandroid_message.time < '" + Message.SQLDateFormatter.format(mEndDate) + "' ");
        }

        rawQuery.append(" group by ").append(selectionArg);
        rawQuery.append("order by ").append(selectionArg).append(" ASC");

        // the X date value is column 0
        // the y value magnitude is column 1
        SQLiteDatabase db = rawDB.getReadableDatabase();
        Cursor cr = db.rawQuery(rawQuery.toString(), null);
        return getDateQuery(displayType, cr, db);

    }

    private Date getStartDate() {
        // TODO Auto-generated method stub
        Date firstDateFromForm = ParsedDataReporter.getOldestMessageDate(rawDB, mForm);
        if (firstDateFromForm.after(mStartDate)) {
            // first date in the form is more recent than the start date, so
            // just go with that.
            return firstDateFromForm;
        } else {
            return mStartDate;
        }
    }

    /**
     * Should return a two element array - the first element is the data, the
     * second are the options
     * 
     * @return
     */
    private JSONGraphData loadHistogramFromField() {
        // JSONObject result = new JSONObject();
        SQLiteDatabase db = rawDB.getReadableDatabase();

        String fieldcol = RapidSmsDBConstants.FormData.COLUMN_PREFIX + fieldToPlot.getName();
        StringBuilder rawQuery = new StringBuilder();
        rawQuery.append("select " + fieldcol);
        rawQuery.append(", count(*) from ");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());

        rawQuery.append(" join rapidandroid_message on (");
        rawQuery.append(RapidSmsDBConstants.FormData.TABLE_PREFIX + mForm.getPrefix());
        rawQuery.append(".message_id = rapidandroid_message._id");
        rawQuery.append(") ");

        if (mStartDate.compareTo(Constants.NULLDATE) != 0 && mEndDate.compareTo(Constants.NULLDATE) != 0) {
            rawQuery.append(" WHERE rapidandroid_message.time > '" + Message.SQLDateFormatter.format(mStartDate)
                    + "' AND rapidandroid_message.time < '" + Message.SQLDateFormatter.format(mEndDate) + "' ");
        }

        rawQuery.append(" group by " + fieldcol);
        rawQuery.append(" order by " + fieldcol);

        // the string value is column 0
        // the magnitude is column 1

        Cursor cr = db.rawQuery(rawQuery.toString(), null);
        int barCount = cr.getCount();

        if (barCount != 0) {
            String[] xVals = new String[barCount];
            int[] yVals = new int[barCount];
            cr.moveToFirst();
            int i = 0;
            do {
                xVals[i] = cr.getString(0);
                yVals[i] = cr.getInt(1);
                i++;
            } while (cr.moveToNext());

            // xaxis: { ticks: [0, [Math.PI/2, "\u03c0/2"], [Math.PI, "\u03c0"],
            // [Math.PI * 3/2, "3\u03c0/2"], [Math.PI * 2, "2\u03c0"]]},

            try {
                // result.put("label", fieldToPlot.getName());
                // result.put("data", prepareData(xVals, yVals));
                // result.put("bars", getShowTrue());
                // result.put("xaxis", getXaxisOptions(xVals));
                return new JSONGraphData(prepareHistogramData(xVals, yVals), loadOptionsForHistogram(xVals));
            } catch (Exception ex) {

            } finally {
                if (!cr.isClosed()) {
                    cr.close();
                }
                if (db.isOpen()) {
                    db.close();
                }
            }
        }
        // either there was no data or something bad happened
        return new JSONGraphData(getEmptyData(), new JSONObject());
    }

    private JSONArray prepareHistogramData(String[] names, int[] counts) throws JSONException {
        // TODO Auto-generated method stub
        JSONArray arr = new JSONArray();
        int datalen = names.length;
        for (int i = 0; i < datalen; i++) {

            JSONObject elem = new JSONObject();
            // values will just be an array of length 1 with a single value
            JSONArray values = new JSONArray();
            JSONArray value = new JSONArray();
            value.put(i);
            value.put(counts[i]);
            values.put(value);
            elem.put("data", values);
            elem.put("bars", getShowTrue());
            elem.put("label", names[i]);
            arr.put(elem);
        }
        return arr;
    }

    @Override
    public String getGraphTitle() {
        return "Form Data";
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rapidandroid.activity.chart.ChartBroker#setVariable(int)
     */

    @Override
    public synchronized void setVariable(int id) {
        // TODO Auto-generated method stub
        if (id == 0) {
            this.fieldToPlot = null;
        } else {
            this.fieldToPlot = mForm.getFields()[id - 1];
        }
        mChosenVariable = id;
        this.mGraphData = null;
        this.mGraphOptions = null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rapidandroid.activity.chart.ChartBroker#finishGraph()
     */

    @Override
    public String getName() {
        return "graph_form";
    }
}