com.josecalles.tistiq.task.StatCalculator.java Source code

Java tutorial

Introduction

Here is the source code for com.josecalles.tistiq.task.StatCalculator.java

Source

/*
 *
 * Copyright 2015,  Jose Calles
 *
 * 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 com.josecalles.tistiq.task;

import android.os.AsyncTask;
import com.josecalles.tistiq.TistiqApplication;
import com.josecalles.tistiq.event.SyncCompleteEvent;
import com.josecalles.tistiq.object.Contact;
import com.josecalles.tistiq.object.StatSettings;
import com.josecalles.tistiq.object.Statistic;
import com.josecalles.tistiq.object.TextMessage;
import com.josecalles.tistiq.type.StatType;
import de.greenrobot.event.EventBus;
import io.realm.Realm;
import io.realm.RealmList;
import io.realm.RealmResults;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.joda.time.DateTime;
import org.joda.time.Duration;

public final class StatCalculator {

    private final static String CLEAN_MSG_BODY_REGEX = "[^A-Za-z&&[^']]";
    private final static String LAUGH_REGEX = "lol|haha|hehe|jaja|lma[o+]";

    private long mReplyCutOffTime;
    private int mValidWordLength;
    private int mNumberOfMostUsed;

    private TistiqApplication mApplication;

    public StatCalculator(TistiqApplication application) {
        mApplication = application;
    }

    public void fetchStatistics(StatSettings settings) {
        setSettings(settings);
        new CalculateStatsTask().execute();
    }

    private void setSettings(StatSettings settings) {
        mReplyCutOffTime = settings.getReplyCutOffTime();
        mValidWordLength = settings.getValidWordLength();
        mNumberOfMostUsed = settings.getNumberOfMostUsed();
    }

    public int getAverageLength(RealmList<TextMessage> messages) {

        int sumOfWords = 0;
        for (TextMessage message : messages) {
            String messageBody = message.getMessageBody();
            String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim();
            int numberOfWords = cleanMessageBody.isEmpty() ? 0 : cleanMessageBody.split("\\s+").length;
            sumOfWords += numberOfWords;
        }
        return sumOfWords / messages.size();
    }

    public String getMostUsedWords(RealmList<TextMessage> messages) {
        ArrayList<String> words = new ArrayList<>();
        ArrayList<String> results;
        StringBuilder stringBuilder = new StringBuilder();

        Map<String, Integer> wordsWithCount = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < messages.size(); i++) {
            String messageBody = messages.get(i).getMessageBody();
            String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim();
            String[] wordsInMessage = cleanMessageBody.split("\\s+");
            for (int j = 0; j < wordsInMessage.length; j++) {
                if (wordsInMessage[j].length() > mValidWordLength)
                    words.add(wordsInMessage[j]);
            }
        }
        for (int i = 0; i < words.size(); i++) {
            if (wordsWithCount.containsKey(words.get(i))) {
                wordsWithCount.put(words.get(i), (wordsWithCount.get(words.get(i)) + 1));
            } else {
                wordsWithCount.put(words.get(i), 1);
            }
        }
        results = sortValues(wordsWithCount, mNumberOfMostUsed, false);

        if (results.size() == 0)
            return "N/A";

        for (int i = 0; i < results.size(); i++) {
            if (i == mNumberOfMostUsed - 1) {
                stringBuilder.append(results.get(i));
            } else {
                stringBuilder.append(results.get(i) + "    ");
            }
        }
        return stringBuilder.toString();
    }

    public static <K, V extends Comparable<? super V>> ArrayList<String> sortValues(Map<K, V> map,
            int numberOfResults, boolean getValue) {
        ArrayList<String> results = new ArrayList<>();
        List<Map.Entry<K, V>> list = new LinkedList<>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
            @Override
            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
                return (o2.getValue()).compareTo(o1.getValue());
            }
        });

        Map<K, V> result = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        if (getValue) {
            for (Map.Entry<K, V> entry : result.entrySet()) {
                Integer value = ((Integer) entry.getValue());
                String stringValue = Integer.toString(value);
                results.add(stringValue);
                return results;
            }
        }
        boolean done = false;
        for (Map.Entry<K, V> entry : result.entrySet()) {
            if (!done) {
                results.add((String) entry.getKey());
                if (results.size() == result.entrySet().size()) {
                    done = true;
                } else {
                    done = (results.size() == numberOfResults);
                }
            }
        }
        return results;
    }

    public String getWhatDayMostMessagesSent(RealmList<TextMessage> messages) {
        ArrayList<String> dateTimestamps = new ArrayList<>();
        for (TextMessage message : messages) {
            DateTime time = new DateTime(message.getDateReceived());
            String dayOfWeek = time.dayOfWeek().getAsText(Locale.ENGLISH);
            dateTimestamps.add(dayOfWeek);
        }

        HashMap<String, Integer> daysWithCount = new HashMap<>();
        for (int i = 0; i < dateTimestamps.size(); i++) {
            if (daysWithCount.containsKey(dateTimestamps.get(i))) {
                daysWithCount.put(dateTimestamps.get(i), (daysWithCount.get(dateTimestamps.get(i)) + 1));
            } else {
                daysWithCount.put(dateTimestamps.get(i), 1);
            }
        }
        String dayOnWhich = sortValues(daysWithCount, 1, false).get(0);

        return dayOnWhich + "s";
    }

    public long timeItTakesToReplySelf(RealmList<TextMessage> messagesReceived,
            RealmList<TextMessage> messagesSent) {

        if (messagesReceived.size() == 0) {
            return -1;
        }
        long totalTime = 0;
        long count = 0;

        for (TextMessage sentMessage : messagesSent) {
            for (TextMessage receivedMessage : messagesReceived) {
                if (receivedMessage.getDateReceived() > sentMessage.getDateReceived()) {
                    if (receivedMessage.getDateReceived() - sentMessage.getDateReceived() < mReplyCutOffTime) {
                        totalTime += (receivedMessage.getDateReceived() - sentMessage.getDateReceived());
                        count += 1;
                        break;
                    }
                }
            }
        }

        if (count == 0)
            return -1;
        return totalTime / count;
    }

    public long timeItTakesToReplyContact(RealmList<TextMessage> messagesReceived,
            RealmList<TextMessage> messagesSent) {

        if (messagesSent.size() == 0) {
            return -1;
        }
        long totalTime = 0;
        long count = 0;

        for (TextMessage receivedMessage : messagesReceived) {
            for (TextMessage sentMessage : messagesSent) {
                if (sentMessage.getDateReceived() > receivedMessage.getDateReceived()) {
                    if (sentMessage.getDateReceived() - receivedMessage.getDateReceived() < mReplyCutOffTime) {
                        totalTime += (sentMessage.getDateReceived() - receivedMessage.getDateReceived());
                        count += 1;
                        break;
                    }
                }
            }
        }

        if (count == 0)
            return -1;
        return totalTime / count;
    }

    public long timeSinceLastMessage(RealmList<TextMessage> messages) {
        long time = messages.get(messages.size() - 1).getDateReceived();
        DateTime lastMessageTime = new DateTime(time);
        DateTime currentTime = new DateTime();
        Duration duration = new Duration(lastMessageTime, currentTime);
        return duration.getMillis();
    }

    public int mostMessagesSentInADay(RealmList<TextMessage> messages) {
        ArrayList<String> daysOfYear = new ArrayList<>();
        for (TextMessage message : messages) {
            DateTime time = new DateTime(message.getDateReceived());
            String dayOfYear = Integer.toString(time.getDayOfYear());
            daysOfYear.add(dayOfYear);
        }

        HashMap<String, Integer> daysWithCount = new HashMap<>();
        for (int i = 0; i < daysOfYear.size(); i++) {
            if (daysWithCount.containsKey(daysOfYear.get(i))) {
                daysWithCount.put(daysOfYear.get(i), (daysWithCount.get(daysOfYear.get(i)) + 1));
            } else {
                daysWithCount.put(daysOfYear.get(i), 1);
            }
        }
        String numberOfMessages = sortValues(daysWithCount, 1, true).get(0);
        return Integer.parseInt(numberOfMessages);
    }

    public int mostMessagesReceivedInADay(RealmList<TextMessage> messages) {
        ArrayList<String> daysOfYear = new ArrayList<>();
        for (TextMessage message : messages) {
            DateTime time = new DateTime(message.getDateReceived());
            String dayOfYear = Integer.toString(time.getDayOfYear());
            daysOfYear.add(dayOfYear);
        }

        HashMap<String, Integer> daysWithCount = new HashMap<>();
        for (int i = 0; i < daysOfYear.size(); i++) {
            if (daysWithCount.containsKey(daysOfYear.get(i))) {
                daysWithCount.put(daysOfYear.get(i), (daysWithCount.get(daysOfYear.get(i)) + 1));
            } else {
                daysWithCount.put(daysOfYear.get(i), 1);
            }
        }
        String numberOfMessages = sortValues(daysWithCount, 1, true).get(0);
        return Integer.parseInt(numberOfMessages);
    }

    public int numberOfQuestionsAsked(RealmList<TextMessage> messages) {
        String regex = "[?]+";
        Pattern pattern = Pattern.compile(regex);
        int count = 0;
        for (TextMessage message : messages) {
            Matcher matcher = pattern.matcher(message.getMessageBody());
            while (matcher.find()) {
                count += 1;
            }
        }
        return count;
    }

    public int numberOfLaughs(RealmList<TextMessage> messages) {
        Pattern pattern = Pattern.compile(LAUGH_REGEX, Pattern.CASE_INSENSITIVE);
        int count = 0;
        for (TextMessage message : messages) {
            Matcher matcher = pattern.matcher(message.getMessageBody());
            while (matcher.find()) {
                count += 1;
            }
        }
        return count;
    }

    public int getSentToAverageLength(RealmList<TextMessage> messages) {

        int sumOfWords = 0;
        for (int i = 0; i < messages.size(); i++) {
            String messageBody = messages.get(i).getMessageBody();
            String cleanMessageBody = messageBody.replaceAll(CLEAN_MSG_BODY_REGEX, " ").trim();
            int numberOfWords = cleanMessageBody.isEmpty() ? 0 : cleanMessageBody.split("\\s+").length;
            sumOfWords += numberOfWords;
        }
        if (messages.size() == 0)
            return 0;

        return sumOfWords / messages.size();
    }

    public int sentToNumberOfLaughs(RealmList<TextMessage> messages) {
        Pattern pattern = Pattern.compile(LAUGH_REGEX, Pattern.CASE_INSENSITIVE);
        int count = 0;
        for (TextMessage message : messages) {
            Matcher matcher = pattern.matcher(message.getMessageBody());
            while (matcher.find()) {
                count += 1;
            }
        }
        return count;
    }

    private class CalculateStatsTask extends AsyncTask<Void, Void, Void> {

        private RealmResults<Contact> mContactList;
        private Contact mSelfContact;
        private EventBus mEventBus;

        public CalculateStatsTask() {
            mEventBus = EventBus.getDefault();
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected Void doInBackground(Void... params) {
            Realm realm = Realm.getInstance(mApplication);
            RealmList<TextMessage> sentMessages;
            RealmList<TextMessage> receivedMessages;

            mContactList = realm.where(Contact.class).notEqualTo("uniqueID", 0).findAll();
            mSelfContact = realm.where(Contact.class).equalTo("uniqueID", 0).findFirst();

            realm.beginTransaction();
            realm.where(Statistic.class).findAll().clear();

            for (int i = 0; i < mContactList.size(); i++) {
                if (mContactList.get(i).getSentMessages().size() != 0
                        && mContactList.get(i).getReceivedMessages().size() != 0) {
                    Statistic statistic = new Statistic();
                    sentMessages = mContactList.get(i).getSentMessages();
                    receivedMessages = mContactList.get(i).getReceivedMessages();

                    statistic.setContactID(mContactList.get(i).getUniqueID());
                    statistic.setStatType(StatType.CONTACT);
                    statistic.setContactName(mContactList.get(i).getDisplayName());
                    statistic.setMessagesSent(sentMessages.size());
                    statistic.setMessagesReceived(receivedMessages.size());
                    statistic.setAvgLengthOfMessage(getAverageLength(sentMessages));
                    statistic.setAvgReplyTimeInMillis(timeItTakesToReplyContact(receivedMessages, sentMessages));
                    statistic.setMostUsedWords(getMostUsedWords(sentMessages));
                    statistic.setDayMostMessagesSent(getWhatDayMostMessagesSent(sentMessages));
                    statistic.setMostSentInADay(mostMessagesSentInADay(sentMessages));
                    statistic.setTimeSinceLastMessage(timeSinceLastMessage(sentMessages));
                    statistic.setNumberOfQuestionsAsked(numberOfQuestionsAsked(sentMessages));
                    statistic.setNumberOfLaughs(numberOfLaughs(sentMessages));

                    statistic.setSentToAvgLength(getSentToAverageLength(receivedMessages));
                    statistic.setSentToReplyTimeInMillis(timeItTakesToReplySelf(receivedMessages, sentMessages));
                    statistic.setSentToNumberOfLaughs(sentToNumberOfLaughs(receivedMessages));

                    realm.copyToRealm(statistic);
                }
            }

            sentMessages = mSelfContact.getSentMessages();
            receivedMessages = mSelfContact.getReceivedMessages();
            long selfAvgReplyTime = (long) realm.where(Statistic.class).equalTo("statType", StatType.CONTACT)
                    .averageInt("sentToReplyTimeInMillis");

            Statistic statistic = new Statistic();
            statistic.setContactID(0);
            statistic.setStatType(StatType.SELF);
            statistic.setMessagesSent(sentMessages.size());
            statistic.setMessagesReceived(receivedMessages.size());
            statistic.setMostUsedWords(getMostUsedWords(sentMessages));
            statistic.setAvgLengthOfMessage(getAverageLength(sentMessages));
            statistic.setAvgReplyTimeInMillis(selfAvgReplyTime);
            statistic.setDayMostMessagesSent(getWhatDayMostMessagesSent(sentMessages));
            statistic.setMostSentInADay(mostMessagesSentInADay(sentMessages));
            statistic.setMostReceivedInADay(mostMessagesReceivedInADay(receivedMessages));
            statistic.setNumberOfQuestionsAsked(numberOfQuestionsAsked(sentMessages));

            realm.copyToRealm(statistic);
            realm.commitTransaction();
            realm.close();
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
            mEventBus.post(new SyncCompleteEvent());
        }
    }
}