mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 17:18:52 -06:00
ScoreList: Remove groupBy functions
This commit is contained in:
@@ -19,20 +19,16 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.activities.common.views;
|
package org.isoron.uhabits.activities.common.views;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.*;
|
||||||
import androidx.test.filters.*;
|
import androidx.test.filters.*;
|
||||||
import androidx.test.runner.*;
|
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.activities.habits.show.views.*;
|
||||||
import org.isoron.uhabits.core.models.*;
|
import org.isoron.uhabits.core.models.*;
|
||||||
import org.isoron.uhabits.core.utils.*;
|
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.junit.runner.*;
|
import org.junit.runner.*;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@MediumTest
|
@MediumTest
|
||||||
public class ScoreChartTest extends BaseViewTest
|
public class ScoreChartTest extends BaseViewTest
|
||||||
@@ -43,6 +39,8 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
|
|
||||||
private ScoreChart view;
|
private ScoreChart view;
|
||||||
|
|
||||||
|
private ScoreCardPresenter presenter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Before
|
@Before
|
||||||
public void setUp()
|
public void setUp()
|
||||||
@@ -51,15 +49,13 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
|
|
||||||
fixtures.purgeHabits(habitList);
|
fixtures.purgeHabits(habitList);
|
||||||
habit = fixtures.createLongHabit();
|
habit = fixtures.createLongHabit();
|
||||||
|
presenter = new ScoreCardPresenter(habit, prefs.getFirstWeekday());
|
||||||
Timestamp today = DateUtils.getTodayWithOffset();
|
ScoreCardViewModel model = presenter.present(0);
|
||||||
List<Entry> known = habit.getComputedEntries().getKnown();
|
|
||||||
Timestamp oldest = known.get(known.size() - 1).getTimestamp();
|
|
||||||
|
|
||||||
view = new ScoreChart(targetContext);
|
view = new ScoreChart(targetContext);
|
||||||
view.setScores(habit.getScores().getByInterval(oldest, today));
|
view.setScores(model.getScores());
|
||||||
view.setColor(PaletteUtilsKt.toThemedAndroidColor(habit.getColor(), targetContext));
|
view.setColor(PaletteUtilsKt.toFixedAndroidColor(model.getColor()));
|
||||||
view.setBucketSize(7);
|
view.setBucketSize(model.getBucketSize());
|
||||||
measureView(view, dpToPixels(300), dpToPixels(200));
|
measureView(view, dpToPixels(300), dpToPixels(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +84,9 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
@Test
|
@Test
|
||||||
public void testRender_withMonthlyBucket() throws Throwable
|
public void testRender_withMonthlyBucket() throws Throwable
|
||||||
{
|
{
|
||||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SUNDAY));
|
ScoreCardViewModel model = presenter.present(2);
|
||||||
view.setBucketSize(30);
|
view.setScores(model.getScores());
|
||||||
|
view.setBucketSize(model.getBucketSize());
|
||||||
view.invalidate();
|
view.invalidate();
|
||||||
|
|
||||||
assertRenders(view, BASE_PATH + "renderMonthly.png");
|
assertRenders(view, BASE_PATH + "renderMonthly.png");
|
||||||
@@ -105,8 +102,9 @@ public class ScoreChartTest extends BaseViewTest
|
|||||||
@Test
|
@Test
|
||||||
public void testRender_withYearlyBucket() throws Throwable
|
public void testRender_withYearlyBucket() throws Throwable
|
||||||
{
|
{
|
||||||
view.setScores(habit.getScores().groupBy(DateUtils.TruncateField.YEAR, Calendar.SUNDAY));
|
ScoreCardViewModel model = presenter.present(4);
|
||||||
view.setBucketSize(365);
|
view.setScores(model.getScores());
|
||||||
|
view.setBucketSize(model.getBucketSize());
|
||||||
view.invalidate();
|
view.invalidate();
|
||||||
|
|
||||||
assertRenders(view, BASE_PATH + "renderYearly.png");
|
assertRenders(view, BASE_PATH + "renderYearly.png");
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class ScoreCardPresenter(
|
|||||||
val BUCKET_SIZES = intArrayOf(1, 7, 31, 92, 365)
|
val BUCKET_SIZES = intArrayOf(1, 7, 31, 92, 365)
|
||||||
fun getTruncateField(bucketSize: Int): DateUtils.TruncateField {
|
fun getTruncateField(bucketSize: Int): DateUtils.TruncateField {
|
||||||
when (bucketSize) {
|
when (bucketSize) {
|
||||||
|
1 -> return DAY
|
||||||
7 -> return WEEK_NUMBER
|
7 -> return WEEK_NUMBER
|
||||||
31 -> return MONTH
|
31 -> return MONTH
|
||||||
92 -> return QUARTER
|
92 -> return QUARTER
|
||||||
@@ -78,12 +79,20 @@ class ScoreCardPresenter(
|
|||||||
|
|
||||||
fun present(spinnerPosition: Int): ScoreCardViewModel {
|
fun present(spinnerPosition: Int): ScoreCardViewModel {
|
||||||
val bucketSize = BUCKET_SIZES[spinnerPosition]
|
val bucketSize = BUCKET_SIZES[spinnerPosition]
|
||||||
val scoreList = habit.scores
|
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val today = DateUtils.getTodayWithOffset()
|
||||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
||||||
|
|
||||||
val scores = if (bucketSize == 1) scoreList.getByInterval(oldest, today)
|
val field = getTruncateField(bucketSize)
|
||||||
else scoreList.groupBy(getTruncateField(bucketSize), firstWeekday)
|
val scores = habit.scores.getByInterval(oldest, today).groupBy {
|
||||||
|
DateUtils.truncate(field, it.timestamp, firstWeekday)
|
||||||
|
}.map { (timestamp, scores) ->
|
||||||
|
Score(timestamp, scores.map {
|
||||||
|
it.value
|
||||||
|
}.average())
|
||||||
|
}.sortedBy {
|
||||||
|
it.timestamp
|
||||||
|
}.reversed()
|
||||||
|
|
||||||
return ScoreCardViewModel(
|
return ScoreCardViewModel(
|
||||||
color = habit.color,
|
color = habit.color,
|
||||||
scores = scores,
|
scores = scores,
|
||||||
@@ -91,5 +100,4 @@ class ScoreCardPresenter(
|
|||||||
spinnerPosition = spinnerPosition,
|
spinnerPosition = spinnerPosition,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -38,23 +38,16 @@ class ScoreWidget(
|
|||||||
pendingIntentFactory.showHabit(habit)
|
pendingIntentFactory.showHabit(habit)
|
||||||
|
|
||||||
override fun refreshData(view: View) {
|
override fun refreshData(view: View) {
|
||||||
val size = ScoreCardPresenter.BUCKET_SIZES[prefs.scoreCardSpinnerPosition]
|
val presenter = ScoreCardPresenter(habit, prefs.firstWeekday)
|
||||||
val today = DateUtils.getTodayWithOffset()
|
val viewModel = presenter.present(prefs.scoreCardSpinnerPosition)
|
||||||
val oldest = habit.computedEntries.getKnown().lastOrNull()?.timestamp ?: today
|
|
||||||
|
|
||||||
val scores = when(size) {
|
|
||||||
1 -> habit.scores.getByInterval(oldest, today)
|
|
||||||
else -> habit.scores.groupBy(ScoreCardPresenter.getTruncateField(size), prefs.firstWeekday)
|
|
||||||
}
|
|
||||||
|
|
||||||
val widgetView = view as GraphWidgetView
|
val widgetView = view as GraphWidgetView
|
||||||
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
widgetView.setBackgroundAlpha(preferedBackgroundAlpha)
|
||||||
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
if (preferedBackgroundAlpha >= 255) widgetView.setShadowAlpha(0x4f)
|
||||||
(widgetView.dataView as ScoreChart).apply {
|
(widgetView.dataView as ScoreChart).apply {
|
||||||
setIsTransparencyEnabled(true)
|
setIsTransparencyEnabled(true)
|
||||||
setBucketSize(size)
|
setBucketSize(viewModel.bucketSize)
|
||||||
setColor(habit.color.toThemedAndroidColor(context))
|
setColor(habit.color.toThemedAndroidColor(context))
|
||||||
setScores(scores)
|
setScores(viewModel.scores)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ data class Habit(
|
|||||||
isNumerical = isNumerical,
|
isNumerical = isNumerical,
|
||||||
)
|
)
|
||||||
|
|
||||||
val to = DateUtils.getTodayWithOffset()
|
val to = DateUtils.getTodayWithOffset().plus(30)
|
||||||
val entries = computedEntries.getKnown()
|
val entries = computedEntries.getKnown()
|
||||||
var from = entries.lastOrNull()?.timestamp ?: to
|
var from = entries.lastOrNull()?.timestamp ?: to
|
||||||
if (from.isNewerThan(to)) from = to
|
if (from.isNewerThan(to)) from = to
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ package org.isoron.uhabits.core.models;
|
|||||||
|
|
||||||
import androidx.annotation.*;
|
import androidx.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.core.utils.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static org.isoron.uhabits.core.models.Entry.*;
|
import static org.isoron.uhabits.core.models.Entry.*;
|
||||||
@@ -68,22 +66,13 @@ public class ScoreList
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Score> groupBy(DateUtils.TruncateField field, int firstWeekday)
|
|
||||||
{
|
|
||||||
HashMap<Timestamp, ArrayList<Double>> groups = getGroupedValues(field, firstWeekday);
|
|
||||||
List<Score> scores = groupsToAvgScores(groups);
|
|
||||||
Collections.sort(scores, (s1, s2) -> s2.compareNewer(s1));
|
|
||||||
return scores;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void recompute(
|
public void recompute(
|
||||||
Frequency frequency,
|
Frequency frequency,
|
||||||
boolean isNumerical,
|
boolean isNumerical,
|
||||||
double targetValue,
|
double targetValue,
|
||||||
EntryList computedEntries,
|
EntryList computedEntries,
|
||||||
Timestamp from,
|
Timestamp from,
|
||||||
Timestamp to
|
Timestamp to)
|
||||||
)
|
|
||||||
{
|
{
|
||||||
list.clear();
|
list.clear();
|
||||||
if (computedEntries.getKnown().isEmpty()) return;
|
if (computedEntries.getKnown().isEmpty()) return;
|
||||||
@@ -139,47 +128,4 @@ public class ScoreList
|
|||||||
list.put(timestamp, new Score(timestamp, previousValue));
|
list.put(timestamp, new Score(timestamp, previousValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private HashMap<Timestamp, ArrayList<Double>> getGroupedValues(DateUtils.TruncateField field,
|
|
||||||
int firstWeekday)
|
|
||||||
{
|
|
||||||
HashMap<Timestamp, ArrayList<Double>> groups = new HashMap<>();
|
|
||||||
|
|
||||||
for (Score s : list.values())
|
|
||||||
{
|
|
||||||
Timestamp groupTimestamp = new Timestamp(
|
|
||||||
DateUtils.truncate(
|
|
||||||
field,
|
|
||||||
s.getTimestamp().getUnixTime(),
|
|
||||||
firstWeekday));
|
|
||||||
|
|
||||||
if (!groups.containsKey(groupTimestamp))
|
|
||||||
groups.put(groupTimestamp, new ArrayList<>());
|
|
||||||
|
|
||||||
groups.get(groupTimestamp).add(s.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return groups;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private List<Score> groupsToAvgScores(HashMap<Timestamp, ArrayList<Double>> groups)
|
|
||||||
{
|
|
||||||
List<Score> scores = new LinkedList<>();
|
|
||||||
|
|
||||||
for (Timestamp timestamp : groups.keySet())
|
|
||||||
{
|
|
||||||
double meanValue = 0.0;
|
|
||||||
ArrayList<Double> groupValues = groups.get(timestamp);
|
|
||||||
|
|
||||||
for (Double v : groupValues) meanValue += v;
|
|
||||||
meanValue /= groupValues.size();
|
|
||||||
|
|
||||||
scores.add(new Score(timestamp, meanValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
return scores;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,6 +266,13 @@ public abstract class DateUtils
|
|||||||
return Locale.getDefault();
|
return Locale.getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Timestamp truncate(TruncateField field,
|
||||||
|
Timestamp timestamp,
|
||||||
|
int firstWeekday)
|
||||||
|
{
|
||||||
|
return new Timestamp(truncate(field, timestamp.getUnixTime(), firstWeekday));
|
||||||
|
}
|
||||||
|
|
||||||
public static Long truncate(TruncateField field,
|
public static Long truncate(TruncateField field,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
int firstWeekday)
|
int firstWeekday)
|
||||||
@@ -275,6 +282,9 @@ public abstract class DateUtils
|
|||||||
|
|
||||||
switch (field)
|
switch (field)
|
||||||
{
|
{
|
||||||
|
case DAY:
|
||||||
|
return cal.getTimeInMillis();
|
||||||
|
|
||||||
case MONTH:
|
case MONTH:
|
||||||
cal.set(DAY_OF_MONTH, 1);
|
cal.set(DAY_OF_MONTH, 1);
|
||||||
return cal.getTimeInMillis();
|
return cal.getTimeInMillis();
|
||||||
@@ -318,6 +328,6 @@ public abstract class DateUtils
|
|||||||
|
|
||||||
public enum TruncateField
|
public enum TruncateField
|
||||||
{
|
{
|
||||||
MONTH, WEEK_NUMBER, YEAR, QUARTER
|
DAY, MONTH, WEEK_NUMBER, YEAR, QUARTER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -222,19 +222,6 @@ public class ScoreListTest extends BaseUnitTest
|
|||||||
assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99));
|
assertThat(habit.getScores().get(today).getValue(), greaterThan(0.99));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test_groupBy()
|
|
||||||
{
|
|
||||||
Habit habit = fixtures.createLongHabit();
|
|
||||||
List<Score> list =
|
|
||||||
habit.getScores().groupBy(DateUtils.TruncateField.MONTH, Calendar.SATURDAY);
|
|
||||||
|
|
||||||
assertThat(list.size(), equalTo(5));
|
|
||||||
assertThat(list.get(0).getValue(), closeTo(0.644120, E));
|
|
||||||
assertThat(list.get(1).getValue(), closeTo(0.713651, E));
|
|
||||||
assertThat(list.get(2).getValue(), closeTo(0.571922, E));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_recompute()
|
public void test_recompute()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user