/* This file is part of BeepMe. BeepMe is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. BeepMe 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 General Public License for more details. You should have received a copy of the GNU General Public License along with BeepMe. If not, see <http://www.gnu.org/licenses/>. Copyright 2012-2014 Michael Glanznig http://beepme.yourexp.at */ package com.glanznig.beepme.data; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; import java.util.List; import java.util.TreeMap; import com.glanznig.beepme.db.SampleTable; import com.glanznig.beepme.db.UptimeTable; import android.content.Context; import android.os.Bundle; public class Statistics { private static final String TAG = "Statistics"; private static class NegativeComparator implements Comparator<Long> { @Override public int compare(Long lhs, Long rhs) { if (lhs.longValue() > rhs.longValue()) { return -1; } if (lhs.longValue() < rhs.longValue()) { return 1; } else { return 0; } } } public static int getSampleCountToday(Context ctx) { Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_MONTH); GregorianCalendar today = new GregorianCalendar(year, month, day); return getSampleCount(ctx, today); } public static int getSampleCount(Context ctx, Calendar day) { SampleTable sTbl = new SampleTable(ctx.getApplicationContext()); List<Sample> samples = sTbl.getSamplesOfDay(day); if (samples != null && samples.size() > 0) { return samples.size(); } return 0; } public static int getNumSamplesDeclinedToday(Context ctx) { Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_MONTH); GregorianCalendar today = new GregorianCalendar(year, month, day); return getNumSamplesDeclined(ctx, today); } public static int getNumSamplesDeclined(Context ctx, Calendar day) { SampleTable sTbl = new SampleTable(ctx.getApplicationContext()); List<Sample> samples = sTbl.getSamplesOfDay(day); if (samples != null && samples.size() > 0) { int declined = 0; for (int i = 0; i < samples.size(); i++) { Sample s = samples.get(i); if (s.getAccepted().equals(Boolean.FALSE)) { declined += 1; } } return declined; } return 0; } public static int getNumSamplesAcceptedToday(Context ctx) { Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_MONTH); GregorianCalendar today = new GregorianCalendar(year, month, day); return getNumSamplesAccepted(ctx, today); } public static int getNumSamplesAccepted(Context ctx, Calendar day) { SampleTable sTbl = new SampleTable(ctx.getApplicationContext()); List<Sample> samples = sTbl.getSamplesOfDay(day); if (samples != null && samples.size() > 0) { int accepted = 0; for (int i = 0; i < samples.size(); i++) { Sample s = samples.get(i); if (s.getAccepted().equals(Boolean.TRUE)) { accepted += 1; } } return accepted; } return 0; } public static Bundle getStatsOfToday(Context ctx, TimerProfile profile) { Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_MONTH); GregorianCalendar today = new GregorianCalendar(year, month, day); return getStatsOfDay(ctx, profile, today); } public static Bundle getStatsOfDay(Context ctx, TimerProfile profile, Calendar day) { long uptimeDur = getUptimeDuration(ctx, profile, day); int accepted = 0; int declined = 0; SampleTable sTbl = new SampleTable(ctx.getApplicationContext()); List<Sample> samples = sTbl.getSamplesOfDay(day); if (samples != null && samples.size() > 0) { for (int i = 0; i < samples.size(); i++) { Sample s = samples.get(i); if (s.getAccepted().equals(Boolean.TRUE)) { accepted += 1; } else { declined += 1; } } } Bundle b = new Bundle(); b.putLong("uptimeDuration", uptimeDur); b.putInt("acceptedSamples", accepted); b.putInt("declinedSamples", declined); b.putInt("countSamples", accepted+declined); return b; } public static List<Bundle> getStats(Context ctx, TimerProfile profile) { UptimeTable upTbl = new UptimeTable(ctx.getApplicationContext(), profile); List<Uptime> uptimes = upTbl.getUptimes(); SampleTable sTbl = new SampleTable(ctx.getApplicationContext()); List<Sample> samples = sTbl.getSamples(true); if (uptimes != null && samples != null) { TreeMap<Long, Bundle> map = new TreeMap<Long, Bundle>(new NegativeComparator()); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); Bundle item = null; if (samples.size() > 0) { // handle samples (accepted, declined, count) Iterator<Sample> sampleIterator = samples.iterator(); Sample s = null; while (sampleIterator.hasNext()) { s = sampleIterator.next(); if (!map.containsKey(Long.parseLong(dateFormat.format(s.getTimestamp())))) { item = new Bundle(); item.putLong("timestamp", s.getTimestamp().getTime()); item.putLong("uptimeDuration", 0); if (s.getAccepted().equals(Boolean.TRUE)) { item.putInt("acceptedSamples", 1); item.putInt("declinedSamples", 0); } else { item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 1); } item.putInt("countSamples", 1); map.put(Long.parseLong(dateFormat.format(s.getTimestamp())), item); } else { item = map.get(Long.parseLong(dateFormat.format(s.getTimestamp()))); if (s.getAccepted().equals(Boolean.TRUE)) { item.putInt("acceptedSamples", item.getInt("acceptedSamples") + 1); } else { item.putInt("declinedSamples", item.getInt("declinedSamples") + 1); } item.putInt("countSamples", item.getInt("countSamples") + 1); } } } if (uptimes.size() > 0) { // handle uptimes Iterator<Uptime> uptimeIterator = uptimes.iterator(); Uptime up = null; item = null; while (uptimeIterator.hasNext()) { up = uptimeIterator.next(); if (up.getEnd() != null) { // uptime extends over midnight, split up times for 2 different days if (!dateFormat.format(up.getStart()).equals(dateFormat.format(up.getEnd()))) { // get timestamp of midnight GregorianCalendar midnight = new GregorianCalendar(); midnight.setTime(up.getEnd()); midnight = new GregorianCalendar(midnight.get(Calendar.YEAR), midnight.get(Calendar.MONTH), midnight.get(Calendar.DAY_OF_MONTH)); if (map.containsKey(Long.parseLong(dateFormat.format(up.getStart())))) { item = map.get(Long.parseLong(dateFormat.format(up.getStart()))); long newDuration = Math.abs(midnight.getTimeInMillis() - up.getStart().getTime()); item.putLong("uptimeDuration", item.getLong("uptimeDuration") + newDuration); } else { item = new Bundle(); item.putLong("timestamp", up.getStart().getTime()); item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 0); item.putInt("countSamples", 0); item.putLong("uptimeDuration", Math.abs(midnight.getTimeInMillis() - up.getStart().getTime())); map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } if (map.containsKey(Long.parseLong(dateFormat.format(up.getEnd())))) { item = map.get(Long.parseLong(dateFormat.format(up.getEnd()))); long newDuration = Math.abs(up.getEnd().getTime() - midnight.getTimeInMillis()); item.putLong("uptimeDuration", item.getLong("uptimeDuration") + newDuration); } else { item = new Bundle(); item.putLong("timestamp", up.getEnd().getTime()); item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 0); item.putInt("countSamples", 0); item.putLong("uptimeDuration", Math.abs(up.getEnd().getTime() - midnight.getTimeInMillis())); map.put(Long.parseLong(dateFormat.format(up.getEnd())), item); } } else { if (map.containsKey(Long.parseLong(dateFormat.format(up.getStart())))) { item = map.get(Long.parseLong(dateFormat.format(up.getStart()))); long newDuration = Math.abs(up.getEnd().getTime() - up.getStart().getTime()); item.putLong("uptimeDuration", item.getLong("uptimeDuration") + newDuration); } else { item = new Bundle(); item.putLong("timestamp", up.getStart().getTime()); item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 0); item.putInt("countSamples", 0); item.putLong("uptimeDuration", Math.abs(up.getEnd().getTime() - up.getStart().getTime())); map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } } } else { // this means that there either was an error, in this case // the minimum duration should be used // or if it is the most recent uptime, that the beeper is still running Uptime recent = upTbl.getMostRecentUptime(); // still running if (recent.getId() == up.getId()) { if (map.containsKey(Long.parseLong(dateFormat.format(up.getStart())))) { item = map.get(Long.parseLong(dateFormat.format(up.getStart()))); // current time can only be used if it lies within requested day // else time until midnight of this day has to be used Calendar day = Calendar.getInstance(); day.setTime(up.getStart()); day = new GregorianCalendar(day.get(Calendar.YEAR), day.get(Calendar.MONTH), day.get(Calendar.DAY_OF_MONTH)); day.roll(Calendar.DAY_OF_MONTH, true); long endOfDay = day.getTimeInMillis(); day.roll(Calendar.DAY_OF_MONTH, false); Date now = new Date(); long newDuration = 0; if (now.getTime() <= endOfDay) { newDuration = Math.abs(now.getTime() - up.getStart().getTime()); } else { newDuration = Math.abs(endOfDay - up.getStart().getTime()); } item.putLong("uptimeDuration", item.getLong("uptimeDuration") + newDuration); map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } else { item = new Bundle(); item.putLong("timestamp", up.getStart().getTime()); item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 0); item.putInt("countSamples", 0); // current time can only be used if it lies within requested day // else time until midnight of this day has to be used Calendar day = Calendar.getInstance(); day.setTime(up.getStart()); day = new GregorianCalendar(day.get(Calendar.YEAR), day.get(Calendar.MONTH), day.get(Calendar.DAY_OF_MONTH)); day.roll(Calendar.DAY_OF_MONTH, true); long endOfDay = day.getTimeInMillis(); day.roll(Calendar.DAY_OF_MONTH, false); Date now = new Date(); if (now.getTime() <= endOfDay) { item.putLong("uptimeDuration", Math.abs(now.getTime() - up.getStart().getTime())); } else { item.putLong("uptimeDuration", Math.abs(endOfDay - up.getStart().getTime())); } map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } } // due to error else { if (map.containsKey(Long.parseLong(dateFormat.format(up.getStart())))) { item = map.get(Long.parseLong(dateFormat.format(up.getStart()))); item.putLong("uptimeDuration", item.getLong("uptimeDuration") + (long)profile.getMinUptimeDuration()); map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } else { item = new Bundle(); item.putLong("timestamp", up.getStart().getTime()); item.putInt("acceptedSamples", 0); item.putInt("declinedSamples", 0); item.putInt("countSamples", 0); item.putLong("uptimeDuration", (long)profile.getMinUptimeDuration()); map.put(Long.parseLong(dateFormat.format(up.getStart())), item); } } } } } return new ArrayList<Bundle>(map.values()); } return null; } public static long getUptimeDurationToday(Context ctx, TimerProfile profile) { Calendar now = Calendar.getInstance(); int year = now.get(Calendar.YEAR); int month = now.get(Calendar.MONTH); int day = now.get(Calendar.DAY_OF_MONTH); GregorianCalendar today = new GregorianCalendar(year, month, day); return getUptimeDuration(ctx, profile, today); } public static long getUptimeDuration(Context ctx, TimerProfile profile, Calendar day) { UptimeTable upTbl = new UptimeTable(ctx.getApplicationContext(), profile); List<Uptime> times = upTbl.getUptimesOfDay(day); Uptime recent = upTbl.getMostRecentUptime(); if (times != null && times.size() > 0) { long duration = 0; // first item could start the day before and last item could end the day after // so sum up 'inner' uptimes first if (times.size() > 2) { for (int i = 1; i < times.size() - 1; i++) { Uptime u = times.get(i); if (u.getEnd() != null) { duration += Math.abs(u.getEnd().getTime() - u.getStart().getTime()); } else { // uptimes with missing end value are counted as minimum uptime duration // unless current running uptime, then the current time is used as end time if (u.getId() != recent.getId()) { duration += profile.getMinUptimeDuration(); } else { duration += Math.abs(new Date().getTime() - u.getStart().getTime()); } } } } // get start and end timestamp of day long startOfDay = day.getTimeInMillis(); day.roll(Calendar.DAY_OF_MONTH, true); long endOfDay = day.getTimeInMillis(); day.roll(Calendar.DAY_OF_MONTH, false); Uptime first = times.get(0); // first uptime starts the day before // use only time starting from midnight if (first.getStart().getTime() < startOfDay) { if (first.getEnd() != null) { duration += Math.abs(first.getEnd().getTime() - startOfDay); } else { if (first.getId() == recent.getId()) { duration += Math.abs(new Date().getTime() - first.getStart().getTime()); } } } else { if (first.getEnd() != null) { duration += Math.abs(first.getEnd().getTime() - first.getStart().getTime()); } else { if (first.getId() != recent.getId()) { duration += profile.getMinUptimeDuration(); } else { // current time can only be used if it lies within requested day // else time until midnight of this day has to be used Date now = new Date(); if (now.getTime() <= endOfDay) { duration += Math.abs(now.getTime() - first.getStart().getTime()); } else { duration += Math.abs(endOfDay - first.getStart().getTime()); } } } } if (times.size() > 1) { Uptime last = times.get(times.size() - 1); if (last.getEnd() != null) { // if uptime extends over midnight // use only time that lies within requested day if (last.getStart().getTime() <= startOfDay && last.getEnd().getTime() >= endOfDay) { duration += Math.abs(endOfDay - startOfDay); } else if (last.getStart().getTime() <= startOfDay) { duration += Math.abs(last.getEnd().getTime() - startOfDay); } else if (last.getEnd().getTime() >= endOfDay) { duration += Math.abs(endOfDay - last.getStart().getTime()); } else { duration += Math.abs(last.getEnd().getTime() - last.getStart().getTime()); } } else { if (last.getId() != recent.getId()) { duration += profile.getMinUptimeDuration(); } else { // current time can only be used if it lies within requested day // else time until midnight of this day has to be used Date now = new Date(); if (now.getTime() <= endOfDay) { duration += Math.abs(now.getTime() - first.getStart().getTime()); } else { duration += Math.abs(endOfDay - first.getStart().getTime()); } } } } return duration; } return 0L; } }