show filled gaps with different color
This commit is contained in:
parent
a3711f0c0c
commit
556c1a048f
9 changed files with 88 additions and 10 deletions
|
@ -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
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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] })
|
||||
|
|
|
@ -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) }
|
||||
|
|
Loading…
Reference in a new issue