show filled gaps with different color

This commit is contained in:
Milos Kozak 2023-01-16 12:48:15 +01:00
parent a3711f0c0c
commit 556c1a048f
9 changed files with 88 additions and 10 deletions

View file

@ -2,6 +2,7 @@ package info.nightscout.core.graph.data
import android.content.Context
import android.graphics.Paint
import androidx.annotation.ColorInt
import com.jjoe64.graphview.series.DataPointInterface
interface DataPointWithLabelInterface : DataPointInterface {
@ -15,5 +16,5 @@ interface DataPointWithLabelInterface : DataPointInterface {
val shape: PointsWithLabelGraphSeries.Shape
val size: Float
val paintStyle: Paint.Style
fun color(context: Context?): Int
@ColorInt fun color(context: Context?): Int
}

View file

@ -5,14 +5,12 @@ import android.graphics.Paint
import info.nightscout.database.entities.GlucoseValue
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.Profile
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.shared.interfaces.ResourceHelper
class GlucoseValueDataPoint(
val data: GlucoseValue,
private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction,
private val rh: ResourceHelper
) : DataPointWithLabelInterface {

View file

@ -2,6 +2,8 @@ package info.nightscout.core.graph.data
import android.content.Context
import android.graphics.Paint
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import info.nightscout.interfaces.Constants
import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
@ -28,15 +30,17 @@ class InMemoryGlucoseValueDataPoint(
override val size = 1f
override val paintStyle: Paint.Style = Paint.Style.FILL
@ColorInt
override fun color(context: Context?): Int {
val units = profileFunction.getUnits()
val lowLine = defaultValueHelper.determineLowLine()
val highLine = defaultValueHelper.determineHighLine()
return when {
val color = when {
valueToUnits(units) < lowLine -> rh.gac(context, info.nightscout.core.ui.R.attr.bgLow)
valueToUnits(units) > highLine -> rh.gac(context, info.nightscout.core.ui.R.attr.highColor)
else -> rh.gac(context, info.nightscout.core.ui.R.attr.bgInRange)
}
return if (data.filledGap) ColorUtils.setAlphaComponent(color, 128) else color
}
}

View file

@ -0,0 +1,47 @@
package info.nightscout.core.graph.data
import android.content.Context
import android.graphics.Color
import info.nightscout.interfaces.GlucoseUnit
import info.nightscout.interfaces.iob.InMemoryGlucoseValue
import info.nightscout.interfaces.profile.DefaultValueHelper
import info.nightscout.interfaces.profile.ProfileFunction
import info.nightscout.shared.interfaces.ResourceHelper
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.junit.jupiter.MockitoSettings
import org.mockito.kotlin.any
import org.mockito.quality.Strictness
@ExtendWith(MockitoExtension::class)
@MockitoSettings(strictness = Strictness.LENIENT)
internal class InMemoryGlucoseValueDataPointTest {
@Mock lateinit var defaultValueHelper: DefaultValueHelper
@Mock lateinit var profileFunction: ProfileFunction
@Mock lateinit var rh: ResourceHelper
@Mock lateinit var context: Context
@BeforeEach
fun setup() {
Mockito.`when`(profileFunction.getUnits()).thenReturn(GlucoseUnit.MGDL)
Mockito.`when`(rh.gac(any(), any())).thenReturn(Color.GREEN)
}
@Test
fun alphaShouldBeAddedForFilledGaps() {
val gv = InMemoryGlucoseValue(1000, 100.0)
val sut = InMemoryGlucoseValueDataPoint(gv, defaultValueHelper, profileFunction, rh)
var alpha = sut.color(context).ushr(24)
Assertions.assertEquals(255, alpha)
gv.filledGap = true
alpha = sut.color(context).ushr(24)
Assertions.assertEquals(128, alpha)
}
}

View file

@ -2,9 +2,35 @@ package info.nightscout.interfaces.iob
import info.nightscout.database.entities.GlucoseValue
class InMemoryGlucoseValue constructor(var timestamp: Long = 0L, var value: Double = 0.0, var trendArrow: GlucoseValue.TrendArrow = GlucoseValue.TrendArrow.NONE, var smoothed: Double? = null) {
/**
* Simplified [GlucoseValue] for storing in memory and calculations
* It may correspond to GlucoseValue value in db
* but because of 5 min recalculations and smoothing it may not
*/
class InMemoryGlucoseValue constructor(
var timestamp: Long = 0L,
/**
* Value in mg/dl
*/
var value: Double = 0.0,
var trendArrow: GlucoseValue.TrendArrow = GlucoseValue.TrendArrow.NONE,
/**
* Smoothed value. Value is added by smoothing plugin
* or null if smoothing was not done
*/
var smoothed: Double? = null,
/**
* if true value is not corresponding to received value,
* but it was recalculated to fill gap between BGs
*/
var filledGap: Boolean = false
) {
constructor(gv: GlucoseValue) : this(gv.timestamp, gv.value, gv.trendArrow)
/**
* Provide smoothed value if available,
* non smoothed value as a fallback
*/
val recalculated: Double get() = smoothed ?: value
}

View file

@ -859,7 +859,7 @@ class DataHandlerMobile @Inject constructor(
val finalLastRun = loop.lastRun
if (sp.getBoolean(rh.gs(R.string.key_wear_predictions), true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) {
val predArray = finalLastRun.constraintsProcessed!!.predictions
.stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
.stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, profileFunction, rh) }
.collect(Collectors.toList())
if (predArray.isNotEmpty())
for (bg in predArray) if (bg.data.value > 39)

View file

@ -242,8 +242,10 @@ class AutosensDataStoreObject : AutosensDataStore {
} else {
val bgDelta = newer.value - older.value
val timeDiffToNew = newer.timestamp - currentTime
val timeDiffToOlder = currentTime - older.timestamp
val filledGap = timeDiffToOlder > T.mins(5).msecs() || timeDiffToNew > T.mins(5).msecs()
val currentBg = newer.value - timeDiffToNew.toDouble() / (newer.timestamp - older.timestamp) * bgDelta
val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble())
val newBgReading = InMemoryGlucoseValue(currentTime, currentBg.roundToLong().toDouble(), filledGap = filledGap)
newBucketedData.add(newBgReading)
//log.debug("BG: " + newBgReading.value + " (" + new Date(newBgReading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")");
}
@ -279,7 +281,7 @@ class AutosensDataStoreObject : AutosensDataStore {
val gapDelta = bgReadings[i].value - lastBg
//console.error(gapDelta, lastBg, elapsed_minutes);
val nextBg = lastBg + 5.0 / elapsedMinutes * gapDelta
val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble())
val newBgReading = InMemoryGlucoseValue(nextBgTime, nextBg.roundToLong().toDouble(), filledGap = true)
//console.error("Interpolated", bData[j]);
bData.add(newBgReading)
aapsLogger.debug(LTag.AUTOSENS) { "Adding. bgTime: ${dateUtil.toISOString(bgTime)} lastBgTime: ${dateUtil.toISOString(lastBgTime)} $newBgReading" }

View file

@ -49,7 +49,7 @@ class PrepareBgDataWorker(
for (bg in data.overviewData.bgReadingsArray) {
if (bg.timestamp < fromTime || bg.timestamp > toTime) continue
if (bg.value > data.overviewData.maxBgValue) data.overviewData.maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh))
bgListArray.add(GlucoseValueDataPoint(bg, profileFunction, rh))
}
bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
data.overviewData.bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })

View file

@ -80,7 +80,7 @@ class PreparePredictionsWorker(
val bgListArray: MutableList<DataPointWithLabelInterface> = ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
?.map { bg -> GlucoseValueDataPoint(bg, profileFunction, rh) }
?.toMutableList()
if (predictions != null) {
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }