Merge branch 'dev' into smoothing
This commit is contained in:
commit
9c0775c826
22 changed files with 582 additions and 101 deletions
|
@ -7,10 +7,10 @@ import info.nightscout.database.entities.TotalDailyDose
|
|||
|
||||
interface TddCalculator {
|
||||
|
||||
fun calculate(days: Long): LongSparseArray<TotalDailyDose>
|
||||
fun calculateToday(): TotalDailyDose
|
||||
fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose
|
||||
fun calculate(startTime: Long, endTime: Long): TotalDailyDose
|
||||
fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): TotalDailyDose?
|
||||
fun calculate(days: Long, allowMissingDays: Boolean): LongSparseArray<TotalDailyDose>?
|
||||
fun calculateToday(): TotalDailyDose?
|
||||
fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose?
|
||||
fun calculate(startTime: Long, endTime: Long, allowMissingData: Boolean): TotalDailyDose?
|
||||
fun averageTDD(tdds: LongSparseArray<TotalDailyDose>?): TotalDailyDose?
|
||||
fun stats(context: Context): TableLayout
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@ data class NSBolus(
|
|||
override val pumpSerial: String?,
|
||||
override var app: String? = null,
|
||||
val insulin: Double,
|
||||
val type: BolusType
|
||||
val type: BolusType,
|
||||
val isBasalInsulin: Boolean
|
||||
|
||||
) : NSTreatment {
|
||||
enum class BolusType {
|
||||
|
|
|
@ -17,10 +17,21 @@ import info.nightscout.sdk.remotemodel.RemoteTreatment
|
|||
import org.json.JSONObject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Convert to [RemoteTreatment] and back to [NSTreatment]
|
||||
* testing purpose only
|
||||
*
|
||||
* @param treatment original
|
||||
*
|
||||
* @return treatment after double conversion
|
||||
*/
|
||||
fun NSTreatment.convertToRemoteAndBack(): NSTreatment? =
|
||||
toRemoteTreatment()?.toTreatment()
|
||||
|
||||
internal fun RemoteTreatment.toTreatment(): NSTreatment? {
|
||||
val treatmentTimestamp = timestamp()
|
||||
when {
|
||||
insulin != null && insulin > 0 ->
|
||||
insulin != null && insulin > 0 ->
|
||||
return NSBolus(
|
||||
date = treatmentTimestamp,
|
||||
device = this.device,
|
||||
|
@ -40,6 +51,7 @@ internal fun RemoteTreatment.toTreatment(): NSTreatment? {
|
|||
pumpSerial = this.pumpSerial,
|
||||
insulin = this.insulin,
|
||||
type = NSBolus.BolusType.fromString(this.type),
|
||||
isBasalInsulin = isBasalInsulin ?: false
|
||||
)
|
||||
|
||||
carbs != null && carbs > 0 ->
|
||||
|
@ -355,7 +367,8 @@ internal fun NSTreatment.toRemoteTreatment(): RemoteTreatment? =
|
|||
pumpType = pumpType,
|
||||
pumpSerial = pumpSerial,
|
||||
insulin = insulin,
|
||||
type = type.name
|
||||
type = type.name,
|
||||
isBasalInsulin = isBasalInsulin
|
||||
)
|
||||
|
||||
is NSCarbs -> RemoteTreatment(
|
||||
|
|
|
@ -76,7 +76,8 @@ internal data class RemoteTreatment(
|
|||
@SerializedName("rate") val rate: Double? = null, // Double "Temp Basal" absolute rate (could be calculated with percent and profile information...)
|
||||
@SerializedName("extendedEmulated") val extendedEmulated: RemoteTreatment? = null, // Gson of emulated EB
|
||||
@SerializedName("timeshift") val timeshift: Long? = null, // integer "Profile Switch"
|
||||
@SerializedName("percentage") val percentage: Int? = null // integer "Profile Switch"
|
||||
@SerializedName("percentage") val percentage: Int? = null, // integer "Profile Switch"
|
||||
@SerializedName("isBasalInsulin") val isBasalInsulin: Boolean? = null // boolean "Bolus"
|
||||
) {
|
||||
|
||||
fun timestamp(): Long {
|
||||
|
|
|
@ -49,7 +49,7 @@ data class Bolus(
|
|||
var insulinConfiguration: InsulinConfiguration? = null
|
||||
) : TraceableDBEntry, DBEntryWithTime {
|
||||
|
||||
private fun contentEqualsTo(other: Bolus): Boolean =
|
||||
fun contentEqualsTo(other: Bolus): Boolean =
|
||||
isValid == other.isValid &&
|
||||
timestamp == other.timestamp &&
|
||||
utcOffset == other.utcOffset &&
|
||||
|
|
|
@ -38,7 +38,7 @@ data class Carbs(
|
|||
var notes: String? = null
|
||||
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
|
||||
|
||||
private fun contentEqualsTo(other: Carbs): Boolean =
|
||||
fun contentEqualsTo(other: Carbs): Boolean =
|
||||
isValid == other.isValid &&
|
||||
timestamp == other.timestamp &&
|
||||
utcOffset == other.utcOffset &&
|
||||
|
|
|
@ -49,12 +49,12 @@ data class EffectiveProfileSwitch(
|
|||
var originalTimeshift: Long, // [milliseconds]
|
||||
var originalPercentage: Int, // 1 ~ XXX [%]
|
||||
var originalDuration: Long, // [milliseconds]
|
||||
var originalEnd: Long,
|
||||
var originalEnd: Long, // not used (calculated from duration)
|
||||
@Embedded
|
||||
var insulinConfiguration: InsulinConfiguration
|
||||
) : TraceableDBEntry, DBEntryWithTime {
|
||||
|
||||
private fun contentEqualsTo(other: EffectiveProfileSwitch): Boolean =
|
||||
fun contentEqualsTo(other: EffectiveProfileSwitch): Boolean =
|
||||
isValid == other.isValid &&
|
||||
timestamp == other.timestamp &&
|
||||
utcOffset == other.utcOffset &&
|
||||
|
|
|
@ -52,7 +52,25 @@ data class ProfileSwitch(
|
|||
var insulinConfiguration: InsulinConfiguration
|
||||
) : TraceableDBEntry, DBEntryWithTimeAndDuration {
|
||||
|
||||
private fun contentEqualsTo(other: ProfileSwitch): Boolean =
|
||||
fun copy(): ProfileSwitch =
|
||||
ProfileSwitch(
|
||||
isValid = isValid,
|
||||
timestamp = timestamp,
|
||||
utcOffset = utcOffset,
|
||||
basalBlocks = basalBlocks,
|
||||
isfBlocks = isfBlocks,
|
||||
icBlocks = icBlocks,
|
||||
targetBlocks = targetBlocks,
|
||||
glucoseUnit = glucoseUnit,
|
||||
profileName = profileName,
|
||||
timeshift = timeshift,
|
||||
percentage = percentage,
|
||||
duration = duration,
|
||||
insulinConfiguration = insulinConfiguration,
|
||||
interfaceIDs_backing = interfaceIDs_backing
|
||||
)
|
||||
|
||||
fun contentEqualsTo(other: ProfileSwitch): Boolean =
|
||||
isValid == other.isValid &&
|
||||
timestamp == other.timestamp &&
|
||||
utcOffset == other.utcOffset &&
|
||||
|
|
|
@ -25,4 +25,15 @@ interface TraceableDBEntry: DBEntry {
|
|||
set(value) {
|
||||
interfaceIDs_backing = value
|
||||
}
|
||||
|
||||
fun interfaceIdsEqualsTo(other: TraceableDBEntry): Boolean =
|
||||
interfaceIDs.nightscoutId == interfaceIDs.nightscoutId &&
|
||||
interfaceIDs.nightscoutSystemId == interfaceIDs.nightscoutSystemId &&
|
||||
interfaceIDs.pumpType == interfaceIDs.pumpType &&
|
||||
interfaceIDs.pumpSerial == interfaceIDs.pumpSerial &&
|
||||
interfaceIDs.temporaryId == interfaceIDs.temporaryId &&
|
||||
interfaceIDs.pumpId == interfaceIDs.pumpId &&
|
||||
interfaceIDs.startId == interfaceIDs.startId &&
|
||||
interfaceIDs.endId == interfaceIDs.endId
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@ import android.view.ViewGroup
|
|||
import android.widget.TableLayout
|
||||
import android.widget.TableRow
|
||||
import android.widget.TextView
|
||||
import androidx.core.util.size
|
||||
import info.nightscout.database.ValueWrapper
|
||||
import info.nightscout.database.entities.Bolus
|
||||
import info.nightscout.database.entities.TotalDailyDose
|
||||
|
@ -37,7 +38,7 @@ class TddCalculatorImpl @Inject constructor(
|
|||
private val repository: AppRepository
|
||||
) : TddCalculator {
|
||||
|
||||
override fun calculate(days: Long): LongSparseArray<TotalDailyDose> {
|
||||
override fun calculate(days: Long, allowMissingDays: Boolean): LongSparseArray<TotalDailyDose>? {
|
||||
var startTime = MidnightTime.calc(dateUtil.now() - T.days(days).msecs())
|
||||
val endTime = MidnightTime.calc(dateUtil.now())
|
||||
//val stepSize = T.hours(24).msecs() // this is not true on DST change
|
||||
|
@ -55,8 +56,8 @@ class TddCalculatorImpl @Inject constructor(
|
|||
if (endTime > startTime) {
|
||||
var midnight = startTime
|
||||
while (midnight < endTime) {
|
||||
val tdd = calculate(midnight, midnight + T.hours(24).msecs())
|
||||
result.put(midnight, tdd)
|
||||
val tdd = calculate(midnight, midnight + T.hours(24).msecs(), allowMissingData = false)
|
||||
if (tdd != null) result.put(midnight, tdd)
|
||||
midnight = MidnightTime.calc(midnight + T.hours(27).msecs()) // be sure we find correct midnight
|
||||
}
|
||||
}
|
||||
|
@ -68,25 +69,28 @@ class TddCalculatorImpl @Inject constructor(
|
|||
repository.insertTotalDailyDose(tdd)
|
||||
}
|
||||
}
|
||||
return result
|
||||
if (result.size.toLong() == days || allowMissingDays) return result
|
||||
return null
|
||||
}
|
||||
|
||||
override fun calculateToday(): TotalDailyDose {
|
||||
override fun calculateToday(): TotalDailyDose? {
|
||||
val startTime = MidnightTime.calc(dateUtil.now())
|
||||
val endTime = dateUtil.now()
|
||||
return calculate(startTime, endTime)
|
||||
return calculate(startTime, endTime, allowMissingData = true)
|
||||
}
|
||||
|
||||
override fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose {
|
||||
override fun calculateDaily(startHours: Long, endHours: Long): TotalDailyDose? {
|
||||
val startTime = dateUtil.now() + T.hours(hour = startHours).msecs()
|
||||
val endTime = dateUtil.now() + T.hours(hour = endHours).msecs()
|
||||
return calculate(startTime, endTime)
|
||||
return calculate(startTime, endTime, allowMissingData = false)
|
||||
}
|
||||
|
||||
override fun calculate(startTime: Long, endTime: Long): TotalDailyDose {
|
||||
override fun calculate(startTime: Long, endTime: Long, allowMissingData: Boolean): TotalDailyDose? {
|
||||
val isWholeDay = (endTime - startTime) >= T.days(1).msecs()
|
||||
val startTimeAligned = startTime - startTime % (5 * 60 * 1000)
|
||||
val endTimeAligned = endTime - endTime % (5 * 60 * 1000)
|
||||
val tdd = TotalDailyDose(timestamp = startTimeAligned)
|
||||
var tbrFound = false
|
||||
repository.getBolusesDataFromTimeToTime(startTime, endTime, true).blockingGet()
|
||||
.filter { it.type != Bolus.Type.PRIMING }
|
||||
.forEach { t ->
|
||||
|
@ -98,8 +102,9 @@ class TddCalculatorImpl @Inject constructor(
|
|||
val calculationStep = T.mins(5).msecs()
|
||||
for (t in startTimeAligned until endTimeAligned step calculationStep) {
|
||||
|
||||
val profile = profileFunction.getProfile(t) ?: continue
|
||||
val profile = profileFunction.getProfile(t) ?: if (allowMissingData) continue else return null
|
||||
val tbr = iobCobCalculator.getBasalData(profile, t)
|
||||
if (tbr.isTempBasalRunning) tbrFound = true
|
||||
val absoluteRate = tbr.tempBasalAbsolute
|
||||
tdd.basalAmount += absoluteRate / 60.0 * 5.0
|
||||
|
||||
|
@ -111,11 +116,13 @@ class TddCalculatorImpl @Inject constructor(
|
|||
}
|
||||
tdd.totalAmount = tdd.bolusAmount + tdd.basalAmount
|
||||
aapsLogger.debug(LTag.CORE, tdd.toString())
|
||||
return tdd
|
||||
if (tdd.bolusAmount > 0 || tdd.basalAmount > 0 || tbrFound) return tdd
|
||||
return null
|
||||
}
|
||||
|
||||
override fun averageTDD(tdds: LongSparseArray<TotalDailyDose>): TotalDailyDose? {
|
||||
override fun averageTDD(tdds: LongSparseArray<TotalDailyDose>?): TotalDailyDose? {
|
||||
val totalTdd = TotalDailyDose(timestamp = dateUtil.now())
|
||||
tdds ?: return null
|
||||
if (tdds.size() == 0) return null
|
||||
for (i in 0 until tdds.size()) {
|
||||
val tdd = tdds.valueAt(i)
|
||||
|
@ -132,7 +139,7 @@ class TddCalculatorImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override fun stats(context: Context): TableLayout {
|
||||
val tdds = calculate(7)
|
||||
val tdds = calculate(7, allowMissingDays = true) ?: return TableLayout(context)
|
||||
val averageTdd = averageTDD(tdds)
|
||||
val todayTdd = calculateToday()
|
||||
val lp = TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT)
|
||||
|
@ -156,13 +163,15 @@ class TddCalculatorImpl @Inject constructor(
|
|||
})
|
||||
layout.addView(averageTdd.toTableRow(context, rh, tdds.size(), includeCarbs = true))
|
||||
}
|
||||
layout.addView(TextView(context).apply {
|
||||
text = rh.gs(info.nightscout.shared.R.string.today)
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
setTextAppearance(android.R.style.TextAppearance_Material_Medium)
|
||||
})
|
||||
layout.addView(todayTdd.toTableRow(context, rh, dateUtil, includeCarbs = true))
|
||||
todayTdd?.let {
|
||||
layout.addView(TextView(context).apply {
|
||||
text = rh.gs(info.nightscout.shared.R.string.today)
|
||||
setTypeface(typeface, Typeface.BOLD)
|
||||
gravity = Gravity.CENTER_HORIZONTAL
|
||||
setTextAppearance(android.R.style.TextAppearance_Material_Medium)
|
||||
})
|
||||
layout.addView(todayTdd.toTableRow(context, rh, dateUtil, includeCarbs = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -577,14 +577,12 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
|||
// for IOBpredBGs, predicted deviation impact drops linearly from current deviation down to zero
|
||||
// over 60 minutes (data points every 5m)
|
||||
var predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) );
|
||||
//IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev;
|
||||
IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log((Math.max( IOBpredBGs[IOBpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) ))
|
||||
* 5 ),2)) + predDev;
|
||||
if (!TDD) IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev;
|
||||
else IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log((Math.max( IOBpredBGs[IOBpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + predDev;
|
||||
|
||||
// calculate predBGs with long zero temp without deviations
|
||||
//var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI;
|
||||
var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + (round(( -iobTick.iobWithZeroTemp.activity * (1800 / ( TDD * (Math.log(( Math.max(ZTpredBGs[ZTpredBGs.length-1],39) /
|
||||
insulinDivisor ) + 1 ) ) )) * 5 ), 2));
|
||||
if (!TDD) var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI;
|
||||
else var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + (round(( -iobTick.iobWithZeroTemp.activity * (1800 / ( TDD * (Math.log(( Math.max(ZTpredBGs[ZTpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ), 2));
|
||||
// for COBpredBGs, predicted carb impact drops linearly from current carb impact down to zero
|
||||
// eventually accounting for all carbs (if they can be absorbed over DIA)
|
||||
var predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) );
|
||||
|
@ -613,9 +611,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
|||
//console.error(UAMpredBGs.length,slopeFromDeviations, predUCI);
|
||||
UAMduration=round((UAMpredBGs.length+1)*5/60,1);
|
||||
}
|
||||
//UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI;
|
||||
UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD
|
||||
* (Math.log(( Math.max(UAMpredBGs[UAMpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + Math.min(0, predDev) + predUCI;
|
||||
if (!TDD) UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + predBGI + Math.min(0, predDev) + predUCI;
|
||||
else UAMpredBG = UAMpredBGs[UAMpredBGs.length-1] + (round(( -iobTick.activity * (1800 / ( TDD * (Math.log(( Math.max(UAMpredBGs[UAMpredBGs.length-1],39) / insulinDivisor ) + 1 ) ) )) * 5 ),2)) + Math.min(0, predDev) + predUCI;
|
||||
//console.error(predBGI, predCI, predUCI);
|
||||
// truncate all BG predictions at 4 hours
|
||||
if ( IOBpredBGs.length < 48) { IOBpredBGs.push(IOBpredBG); }
|
||||
|
@ -727,27 +724,29 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
|
|||
|
||||
var fSensBG = Math.min(minPredBG,bg);
|
||||
|
||||
if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) {
|
||||
var future_sens = ( 1800 / (Math.log((((fSensBG * 0.5) + (bg * 0.5))/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * ((bg * 0.5) + (eventualBG * 0.5 ))));
|
||||
console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
|
||||
}
|
||||
|
||||
else if( glucose_status.delta > 0 && eventualBG > target_bg || eventualBG > bg) {
|
||||
var future_sens = ( 1800 / (Math.log((bg/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * bg));
|
||||
console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
|
||||
if (TDD) {
|
||||
if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) {
|
||||
var future_sens = ( 1800 / (Math.log((((fSensBG * 0.5) + (bg * 0.5))/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * ((bg * 0.5) + (eventualBG * 0.5 ))));
|
||||
console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
|
||||
}
|
||||
|
||||
else {
|
||||
var future_sens = ( 1800 / (Math.log((fSensBG/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * eventualBG));
|
||||
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
|
||||
}
|
||||
future_sens = round(future_sens,1);
|
||||
else if( glucose_status.delta > 0 && eventualBG > target_bg || eventualBG > bg) {
|
||||
var future_sens = ( 1800 / (Math.log((bg/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * bg));
|
||||
console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
|
||||
}
|
||||
|
||||
else {
|
||||
var future_sens = ( 1800 / (Math.log((fSensBG/insulinDivisor)+1)*TDD));
|
||||
//var future_sens_old = ( 277700 / (TDD * eventualBG));
|
||||
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta");
|
||||
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
|
||||
}
|
||||
future_sens = round(future_sens,1);
|
||||
} else future_sens = variable_sens
|
||||
|
||||
|
||||
var fractionCarbsLeft = meal_data.mealCOB/meal_data.carbs;
|
||||
|
|
|
@ -262,23 +262,11 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
|
|||
this.mealData.put("lastBolusTime", mealData.lastBolusTime)
|
||||
this.mealData.put("lastCarbTime", mealData.lastCarbTime)
|
||||
|
||||
val tdd1D = tddCalculator.averageTDD(tddCalculator.calculate(1))?.totalAmount
|
||||
val tdd7D = tddCalculator.averageTDD(tddCalculator.calculate(7))?.totalAmount
|
||||
val tddLast24H = tddCalculator.calculateDaily(-24, 0).totalAmount
|
||||
val tddLast4H = tddCalculator.calculateDaily(-4, 0).totalAmount
|
||||
val tddLast8to4H = tddCalculator.calculateDaily(-8, -4).totalAmount
|
||||
|
||||
val tddWeightedFromLast8H = ((1.4 * tddLast4H) + (0.6 * tddLast8to4H)) * 3
|
||||
// console.error("Rolling 8 hours weight average: " + tdd_last8_wt + "; ");
|
||||
// console.error("1-day average TDD is: " + tdd1 + "; ");
|
||||
// console.error("7-day average TDD is: " + tdd7 + "; ");
|
||||
|
||||
var tdd =
|
||||
if (tdd1D != null && tdd7D != null) (tddWeightedFromLast8H * 0.33) + (tdd7D * 0.34) + (tdd1D * 0.33)
|
||||
else tddWeightedFromLast8H
|
||||
// console.log("TDD = " + TDD + " using average of 7-day, 1-day and weighted 8hr average");
|
||||
|
||||
// console.log("Insulin Peak = " + insulin.peak + "; ");
|
||||
val tdd1D = tddCalculator.averageTDD(tddCalculator.calculate(1, allowMissingDays = false))?.totalAmount
|
||||
val tdd7D = tddCalculator.averageTDD(tddCalculator.calculate(7, allowMissingDays = false))?.totalAmount
|
||||
val tddLast24H = tddCalculator.calculateDaily(-24, 0)?.totalAmount
|
||||
val tddLast4H = tddCalculator.calculateDaily(-4, 0)?.totalAmount
|
||||
val tddLast8to4H = tddCalculator.calculateDaily(-8, -4)?.totalAmount
|
||||
|
||||
val insulin = activePlugin.activeInsulin
|
||||
val insulinDivisor = when {
|
||||
|
@ -286,25 +274,35 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
|
|||
insulin.peak > 50 -> 65 // ultra rapid peak: 55
|
||||
else -> 75 // rapid peak: 75
|
||||
}
|
||||
// console.log("For " + insulin.friendlyName + " (insulin peak: " + insulin.peak + ") insulin divisor is: " + ins_val + "; ");
|
||||
|
||||
val dynISFadjust = SafeParse.stringToDouble(sp.getString(R.string.key_DynISFAdjust, "100")) / 100.0
|
||||
tdd *= dynISFadjust
|
||||
var tdd: Double? = null
|
||||
var variableSensitivity: Double
|
||||
if (tddLast24H != null && tddLast4H != null && tddLast8to4H != null) {
|
||||
val tddWeightedFromLast8H = ((1.4 * tddLast4H) + (0.6 * tddLast8to4H)) * 3
|
||||
// console.error("Rolling 8 hours weight average: " + tdd_last8_wt + "; ");
|
||||
// console.error("1-day average TDD is: " + tdd1 + "; ");
|
||||
// console.error("7-day average TDD is: " + tdd7 + "; ");
|
||||
|
||||
var variableSensitivity = 1800 / (tdd * (ln((glucoseStatus.glucose / insulinDivisor) + 1)))
|
||||
variableSensitivity = Round.roundTo(variableSensitivity, 0.1)
|
||||
tdd =
|
||||
if (tdd1D != null && tdd7D != null) (tddWeightedFromLast8H * 0.33) + (tdd7D * 0.34) + (tdd1D * 0.33)
|
||||
else tddWeightedFromLast8H
|
||||
// console.log("TDD = " + TDD + " using average of 7-day, 1-day and weighted 8hr average");
|
||||
|
||||
if (dynISFadjust != 1.0) {
|
||||
// console.log("TDD adjusted to " + TDD + " using adjustment factor of " + dynISFadjust + "; ");
|
||||
val dynISFadjust = SafeParse.stringToDouble(sp.getString(R.string.key_DynISFAdjust, "100")) / 100.0
|
||||
tdd *= dynISFadjust
|
||||
|
||||
variableSensitivity = 1800 / (tdd * (ln((glucoseStatus.glucose / insulinDivisor) + 1)))
|
||||
variableSensitivity = Round.roundTo(variableSensitivity, 0.1)
|
||||
} else {
|
||||
variableSensitivity = profile.getIsfMgdl()
|
||||
}
|
||||
// console.log("Current sensitivity for predictions is " + variable_sens + " based on current bg");
|
||||
|
||||
this.profile.put("variable_sens", variableSensitivity)
|
||||
this.profile.put("insulinDivisor", insulinDivisor)
|
||||
this.profile.put("TDD", tdd)
|
||||
tdd?.let { this.profile.put("TDD", tdd) }
|
||||
|
||||
|
||||
if (sp.getBoolean(R.string.key_adjust_sensitivity, false) && tdd7D != null && tdd7D != 0.0)
|
||||
if (sp.getBoolean(R.string.key_adjust_sensitivity, false) && tdd7D != null && tddLast24H != null)
|
||||
autosensData.put("ratio", tddLast24H / tdd7D)
|
||||
else
|
||||
autosensData.put("ratio", 1.0)
|
||||
|
|
|
@ -134,11 +134,10 @@ class StatusLightHandler @Inject constructor(
|
|||
private fun handleUsage(view: TextView?, units: String) {
|
||||
handler.post {
|
||||
val therapyEvent = repository.getLastTherapyRecordUpToNow(TherapyEvent.Type.CANNULA_CHANGE).blockingGet()
|
||||
var usage = 0.0
|
||||
if (therapyEvent is ValueWrapper.Existing) {
|
||||
val tdd = tddCalculator.calculate(therapyEvent.value.timestamp, dateUtil.now())
|
||||
usage = tdd.totalAmount
|
||||
}
|
||||
var usage =
|
||||
if (therapyEvent is ValueWrapper.Existing) {
|
||||
tddCalculator.calculate(therapyEvent.value.timestamp, dateUtil.now(), allowMissingData = false)?.totalAmount ?: 0.0
|
||||
} else 0.0
|
||||
runOnUiThread {
|
||||
view?.text = DecimalFormatter.to0Decimal(usage, units)
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ dependencies {
|
|||
implementation project(':core:utils')
|
||||
implementation project(':core:validators')
|
||||
|
||||
testImplementation project(':implementation')
|
||||
|
||||
// NSClient, Tidepool
|
||||
api("io.socket:socket.io-client:1.0.2") {
|
||||
|
|
|
@ -611,6 +611,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
offlineEvents.clear()
|
||||
result.inserted.forEach { oe ->
|
||||
if (config.NSCLIENT.not()) userEntries.add(
|
||||
UserEntry(
|
||||
|
@ -679,6 +680,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
extendedBoluses.clear()
|
||||
result.inserted.forEach {
|
||||
if (config.NSCLIENT.not()) userEntries.add(
|
||||
UserEntry(
|
||||
|
@ -775,6 +777,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdTemporaryTargets.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryTarget $it")
|
||||
nsIdUpdated.inc(TemporaryTarget::class.java.simpleName)
|
||||
|
@ -787,6 +790,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdGlucoseValues.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of GlucoseValue $it")
|
||||
nsIdUpdated.inc(GlucoseValue::class.java.simpleName)
|
||||
|
@ -799,6 +803,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdFoods.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Food $it")
|
||||
nsIdUpdated.inc(Food::class.java.simpleName)
|
||||
|
@ -811,6 +816,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdTherapyEvents.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TherapyEvent $it")
|
||||
nsIdUpdated.inc(TherapyEvent::class.java.simpleName)
|
||||
|
@ -823,6 +829,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdBoluses.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Bolus $it")
|
||||
nsIdUpdated.inc(Bolus::class.java.simpleName)
|
||||
|
@ -835,6 +842,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdCarbs.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of Carbs $it")
|
||||
nsIdUpdated.inc(Carbs::class.java.simpleName)
|
||||
|
@ -847,6 +855,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdBolusCalculatorResults.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of BolusCalculatorResult $it")
|
||||
nsIdUpdated.inc(BolusCalculatorResult::class.java.simpleName)
|
||||
|
@ -859,6 +868,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdTemporaryBasals.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of TemporaryBasal $it")
|
||||
nsIdUpdated.inc(TemporaryBasal::class.java.simpleName)
|
||||
|
@ -871,6 +881,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdExtendedBoluses.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ExtendedBolus $it")
|
||||
nsIdUpdated.inc(ExtendedBolus::class.java.simpleName)
|
||||
|
@ -883,6 +894,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdProfileSwitches.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of ProfileSwitch $it")
|
||||
nsIdUpdated.inc(ProfileSwitch::class.java.simpleName)
|
||||
|
@ -895,6 +907,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdEffectiveProfileSwitches.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of EffectiveProfileSwitch $it")
|
||||
nsIdUpdated.inc(EffectiveProfileSwitch::class.java.simpleName)
|
||||
|
@ -907,6 +920,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdDeviceStatuses.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of DeviceStatus $it")
|
||||
nsIdUpdated.inc(DeviceStatus::class.java.simpleName)
|
||||
|
@ -919,6 +933,7 @@ class StoreDataForDbImpl @Inject constructor(
|
|||
}
|
||||
.blockingGet()
|
||||
.also { result ->
|
||||
nsIdOfflineEvents.clear()
|
||||
result.updatedNsId.forEach {
|
||||
aapsLogger.debug(LTag.DATABASE, "Updated nsId of OfflineEvent $it")
|
||||
nsIdUpdated.inc(OfflineEvent::class.java.simpleName)
|
||||
|
|
|
@ -14,6 +14,7 @@ fun NSBolus.toBolus(): Bolus =
|
|||
amount = insulin,
|
||||
type = type.toBolusType(),
|
||||
notes = notes,
|
||||
isBasalInsulin = isBasalInsulin,
|
||||
interfaceIDs_backing = InterfaceIDs(nightscoutId = identifier, pumpId = pumpId, pumpType = InterfaceIDs.PumpType.fromString(pumpType), pumpSerial = pumpSerial, endId = endId)
|
||||
)
|
||||
|
||||
|
@ -34,6 +35,7 @@ fun Bolus.toNSBolus(): NSBolus =
|
|||
insulin = amount,
|
||||
type = type.toBolusType(),
|
||||
notes = notes,
|
||||
isBasalInsulin = isBasalInsulin,
|
||||
identifier = interfaceIDs.nightscoutId,
|
||||
pumpId = interfaceIDs.pumpId,
|
||||
pumpType = interfaceIDs.pumpType?.name,
|
||||
|
|
|
@ -41,23 +41,22 @@ fun NSProfileSwitch.toProfileSwitch(activePlugin: ActivePlugin, dateUtil: DateUt
|
|||
fun ProfileSwitch.toNSProfileSwitch(dateUtil: DateUtil): NSProfileSwitch {
|
||||
val unmodifiedCustomizedName = getCustomizedName()
|
||||
// ProfileSealed.PS doesn't provide unmodified json -> reset it
|
||||
val unmodifiedTimeshift = timeshift
|
||||
val unmodifiedPercentage = percentage
|
||||
timeshift = 0
|
||||
percentage = 100
|
||||
val notCustomized = this.copy()
|
||||
notCustomized.timeshift = 0
|
||||
notCustomized.percentage = 100
|
||||
|
||||
return NSProfileSwitch(
|
||||
eventType = EventType.fromString(TherapyEvent.Type.PROFILE_SWITCH.text),
|
||||
isValid = isValid,
|
||||
date = timestamp,
|
||||
utcOffset = utcOffset,
|
||||
timeShift = unmodifiedTimeshift,
|
||||
percentage = unmodifiedPercentage,
|
||||
timeShift = timeshift,
|
||||
percentage = percentage,
|
||||
duration = T.mins(duration).msecs(),
|
||||
profile = unmodifiedCustomizedName,
|
||||
originalProfileName = profileName,
|
||||
originalDuration = duration,
|
||||
profileJson = ProfileSealed.PS(this).toPureNsJson(dateUtil),
|
||||
profileJson = ProfileSealed.PS(notCustomized).toPureNsJson(dateUtil),
|
||||
identifier = interfaceIDs.nightscoutId,
|
||||
pumpId = interfaceIDs.pumpId,
|
||||
pumpType = interfaceIDs.pumpType?.name,
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
package info.nightscout.androidaps
|
||||
|
||||
import android.content.Context
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.core.extensions.pureProfileFromJson
|
||||
import info.nightscout.core.profile.ProfileSealed
|
||||
import info.nightscout.core.utils.fabric.FabricPrivacy
|
||||
import info.nightscout.database.entities.EffectiveProfileSwitch
|
||||
import info.nightscout.database.entities.embedments.InsulinConfiguration
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.implementation.profile.ProfileFunctionImpl
|
||||
import info.nightscout.implementation.profile.ProfileStoreObject
|
||||
import info.nightscout.interfaces.Config
|
||||
import info.nightscout.interfaces.insulin.Insulin
|
||||
import info.nightscout.interfaces.iob.IobCobCalculator
|
||||
import info.nightscout.interfaces.nsclient.ProcessedDeviceStatusData
|
||||
import info.nightscout.interfaces.plugin.ActivePlugin
|
||||
import info.nightscout.interfaces.profile.ProfileFunction
|
||||
import info.nightscout.interfaces.profile.ProfileStore
|
||||
import info.nightscout.interfaces.utils.HardLimits
|
||||
import info.nightscout.rx.bus.RxBus
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import org.json.JSONObject
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.mockito.ArgumentMatchers.anyDouble
|
||||
import org.mockito.ArgumentMatchers.anyInt
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when`
|
||||
import org.mockito.invocation.InvocationOnMock
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
open class TestBaseWithProfile : TestBase() {
|
||||
|
||||
@Mock lateinit var activePlugin: ActivePlugin
|
||||
@Mock lateinit var rh: ResourceHelper
|
||||
@Mock lateinit var iobCobCalculator: IobCobCalculator
|
||||
@Mock lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Mock lateinit var config: Config
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var sp: SP
|
||||
@Mock lateinit var repository: AppRepository
|
||||
@Mock lateinit var hardLimits: HardLimits
|
||||
@Mock lateinit var processedDeviceStatusData: ProcessedDeviceStatusData
|
||||
@Mock lateinit var insulin: Insulin
|
||||
|
||||
lateinit var profileFunction: ProfileFunction
|
||||
lateinit var dateUtil: DateUtil
|
||||
var insulinConfiguration: InsulinConfiguration = InsulinConfiguration("Insulin", 360 * 60 * 1000, 60 * 60 * 1000)
|
||||
val rxBus = RxBus(aapsSchedulers, aapsLogger)
|
||||
|
||||
val profileInjector = HasAndroidInjector { AndroidInjector { } }
|
||||
|
||||
private lateinit var validProfileJSON: String
|
||||
lateinit var validProfile: ProfileSealed.Pure
|
||||
lateinit var effectiveProfileSwitch: EffectiveProfileSwitch
|
||||
|
||||
@Suppress("PropertyName") val TESTPROFILENAME = "someProfile"
|
||||
|
||||
@BeforeEach
|
||||
fun prepareMock() {
|
||||
validProfileJSON = "{\"dia\":\"5\",\"carbratio\":[{\"time\":\"00:00\",\"value\":\"30\"}],\"carbs_hr\":\"20\",\"delay\":\"20\",\"sens\":[{\"time\":\"00:00\",\"value\":\"3\"}," +
|
||||
"{\"time\":\"2:00\",\"value\":\"3.4\"}],\"timezone\":\"UTC\",\"basal\":[{\"time\":\"00:00\",\"value\":\"1\"}],\"target_low\":[{\"time\":\"00:00\",\"value\":\"4.5\"}]," +
|
||||
"\"target_high\":[{\"time\":\"00:00\",\"value\":\"7\"}],\"startDate\":\"1970-01-01T00:00:00.000Z\",\"units\":\"mmol\"}"
|
||||
dateUtil = Mockito.spy(DateUtil(context))
|
||||
`when`(dateUtil.now()).thenReturn(1656358822000)
|
||||
`when`(insulin.insulinConfiguration).thenReturn(insulinConfiguration)
|
||||
`when`(activePlugin.activeInsulin).thenReturn(insulin)
|
||||
profileFunction = ProfileFunctionImpl(aapsLogger, sp, rxBus, rh, activePlugin, repository, dateUtil, config, hardLimits, aapsSchedulers, fabricPrivacy, processedDeviceStatusData)
|
||||
validProfile = ProfileSealed.Pure(pureProfileFromJson(JSONObject(validProfileJSON), dateUtil)!!)
|
||||
effectiveProfileSwitch = EffectiveProfileSwitch(
|
||||
timestamp = dateUtil.now(),
|
||||
basalBlocks = validProfile.basalBlocks,
|
||||
isfBlocks = validProfile.isfBlocks,
|
||||
icBlocks = validProfile.icBlocks,
|
||||
targetBlocks = validProfile.targetBlocks,
|
||||
glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.MMOL,
|
||||
originalProfileName = "",
|
||||
originalCustomizedName = "",
|
||||
originalTimeshift = 0,
|
||||
originalPercentage = 100,
|
||||
originalDuration = 0,
|
||||
originalEnd = 0,
|
||||
insulinConfiguration = InsulinConfiguration("", 0, 0)
|
||||
)
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Int?>(1)
|
||||
String.format(rh.gs(string), arg1)
|
||||
}.`when`(rh).gs(anyInt(), anyInt())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Double?>(1)
|
||||
String.format(rh.gs(string), arg1)
|
||||
}.`when`(rh).gs(anyInt(), anyDouble())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<String?>(1)
|
||||
String.format(rh.gs(string), arg1)
|
||||
}.`when`(rh).gs(anyInt(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<String?>(1)
|
||||
val arg2 = invocation.getArgument<String?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyString(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<String?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyString(), anyInt())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Double?>(1)
|
||||
val arg2 = invocation.getArgument<String?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyDouble(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Double?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Int?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyInt(), anyInt())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Int?>(1)
|
||||
val arg2 = invocation.getArgument<String?>(2)
|
||||
String.format(rh.gs(string), arg1, arg2)
|
||||
}.`when`(rh).gs(anyInt(), anyInt(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Int?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
val arg3 = invocation.getArgument<String?>(3)
|
||||
String.format(rh.gs(string), arg1, arg2, arg3)
|
||||
}.`when`(rh).gs(anyInt(), anyInt(), anyInt(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Int?>(1)
|
||||
val arg2 = invocation.getArgument<String?>(2)
|
||||
val arg3 = invocation.getArgument<String?>(3)
|
||||
String.format(rh.gs(string), arg1, arg2, arg3)
|
||||
}.`when`(rh).gs(anyInt(), anyInt(), anyString(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<Double?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
val arg3 = invocation.getArgument<String?>(3)
|
||||
String.format(rh.gs(string), arg1, arg2, arg3)
|
||||
}.`when`(rh).gs(anyInt(), anyDouble(), anyInt(), anyString())
|
||||
|
||||
Mockito.doAnswer { invocation: InvocationOnMock ->
|
||||
val string = invocation.getArgument<Int>(0)
|
||||
val arg1 = invocation.getArgument<String?>(1)
|
||||
val arg2 = invocation.getArgument<Int?>(2)
|
||||
val arg3 = invocation.getArgument<String?>(3)
|
||||
String.format(rh.gs(string), arg1, arg2, arg3)
|
||||
}.`when`(rh).gs(anyInt(), anyString(), anyInt(), anyString())
|
||||
|
||||
}
|
||||
|
||||
fun getValidProfileStore(): ProfileStore {
|
||||
val json = JSONObject()
|
||||
val store = JSONObject()
|
||||
store.put(TESTPROFILENAME, JSONObject(validProfileJSON))
|
||||
json.put("defaultProfile", TESTPROFILENAME)
|
||||
json.put("store", store)
|
||||
return ProfileStoreObject(profileInjector, json, dateUtil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package info.nightscout.plugins.sync.nsclientV3.extensions
|
||||
|
||||
import info.nightscout.database.entities.Bolus
|
||||
import info.nightscout.database.entities.embedments.InterfaceIDs
|
||||
import info.nightscout.sdk.localmodel.treatment.NSBolus
|
||||
import info.nightscout.sdk.mapper.convertToRemoteAndBack
|
||||
import org.junit.jupiter.api.Assertions
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
internal class BolusExtensionKtTest {
|
||||
|
||||
@Test
|
||||
fun toBolus() {
|
||||
var bolus = Bolus(
|
||||
timestamp = 10000,
|
||||
isValid = true,
|
||||
amount = 1.0,
|
||||
type = Bolus.Type.SMB,
|
||||
notes = "aaaa",
|
||||
isBasalInsulin = false,
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
var bolus2 = (bolus.toNSBolus().convertToRemoteAndBack() as NSBolus).toBolus()
|
||||
Assertions.assertTrue(bolus.contentEqualsTo(bolus2))
|
||||
Assertions.assertTrue(bolus.interfaceIdsEqualsTo(bolus2))
|
||||
|
||||
bolus = Bolus(
|
||||
timestamp = 10000,
|
||||
isValid = false,
|
||||
amount = 1.0,
|
||||
type = Bolus.Type.NORMAL,
|
||||
notes = "aaaa",
|
||||
isBasalInsulin = true,
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
bolus2 = (bolus.toNSBolus().convertToRemoteAndBack() as NSBolus).toBolus()
|
||||
Assertions.assertTrue(bolus.contentEqualsTo(bolus2))
|
||||
Assertions.assertTrue(bolus.interfaceIdsEqualsTo(bolus2))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package info.nightscout.plugins.sync.nsclientV3.extensions
|
||||
|
||||
import info.nightscout.database.entities.Carbs
|
||||
import info.nightscout.database.entities.embedments.InterfaceIDs
|
||||
import info.nightscout.sdk.localmodel.treatment.NSCarbs
|
||||
import info.nightscout.sdk.mapper.convertToRemoteAndBack
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
internal class CarbsExtensionKtTest {
|
||||
|
||||
@Test
|
||||
fun toCarbs() {
|
||||
var carbs = Carbs(
|
||||
timestamp = 10000,
|
||||
isValid = true,
|
||||
amount = 1.0,
|
||||
duration = 0,
|
||||
notes = "aaaa",
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
var carbs2 = (carbs.toNSCarbs().convertToRemoteAndBack() as NSCarbs).toCarbs()
|
||||
Assertions.assertTrue(carbs.contentEqualsTo(carbs2))
|
||||
Assertions.assertTrue(carbs.interfaceIdsEqualsTo(carbs2))
|
||||
|
||||
carbs = Carbs(
|
||||
timestamp = 10000,
|
||||
isValid = false,
|
||||
amount = 1.0,
|
||||
duration = 60000,
|
||||
notes = null,
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
carbs2 = (carbs.toNSCarbs().convertToRemoteAndBack() as NSCarbs).toCarbs()
|
||||
Assertions.assertTrue(carbs.contentEqualsTo(carbs2))
|
||||
Assertions.assertTrue(carbs.interfaceIdsEqualsTo(carbs2))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package info.nightscout.plugins.sync.nsclientV3.extensions
|
||||
|
||||
import info.nightscout.androidaps.TestBaseWithProfile
|
||||
import info.nightscout.database.entities.EffectiveProfileSwitch
|
||||
import info.nightscout.database.entities.embedments.InterfaceIDs
|
||||
import info.nightscout.plugins.sync.nsclient.extensions.fromConstant
|
||||
import info.nightscout.sdk.localmodel.treatment.NSEffectiveProfileSwitch
|
||||
import info.nightscout.sdk.mapper.convertToRemoteAndBack
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
internal class EffectiveProfileSwitchExtensionKtTest : TestBaseWithProfile() {
|
||||
|
||||
@Test
|
||||
fun toEffectiveProfileSwitch() {
|
||||
val profileSwitch = EffectiveProfileSwitch(
|
||||
timestamp = 10000,
|
||||
isValid = true,
|
||||
basalBlocks = validProfile.basalBlocks,
|
||||
isfBlocks = validProfile.isfBlocks,
|
||||
icBlocks = validProfile.icBlocks,
|
||||
targetBlocks = validProfile.targetBlocks,
|
||||
glucoseUnit = EffectiveProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
|
||||
originalProfileName = "SomeProfile",
|
||||
originalCustomizedName = "SomeProfile (150%, 1h)",
|
||||
originalTimeshift = 3600000,
|
||||
originalPercentage = 150,
|
||||
originalDuration = 3600000,
|
||||
originalEnd = 0,
|
||||
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
|
||||
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
|
||||
},
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
val profileSwitch2 = (profileSwitch.toNSEffectiveProfileSwitch(dateUtil).convertToRemoteAndBack() as NSEffectiveProfileSwitch).toEffectiveProfileSwitch(dateUtil)!!
|
||||
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
|
||||
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package info.nightscout.plugins.sync.nsclientV3.extensions
|
||||
|
||||
import info.nightscout.androidaps.TestBaseWithProfile
|
||||
import info.nightscout.core.extensions.fromConstant
|
||||
import info.nightscout.database.entities.ProfileSwitch
|
||||
import info.nightscout.database.entities.embedments.InterfaceIDs
|
||||
import info.nightscout.sdk.localmodel.treatment.NSProfileSwitch
|
||||
import info.nightscout.sdk.mapper.convertToRemoteAndBack
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
internal class ProfileSwitchExtensionKtTest : TestBaseWithProfile() {
|
||||
|
||||
@Test
|
||||
fun toProfileSwitch() {
|
||||
var profileSwitch = ProfileSwitch(
|
||||
timestamp = 10000,
|
||||
isValid = true,
|
||||
basalBlocks = validProfile.basalBlocks,
|
||||
isfBlocks = validProfile.isfBlocks,
|
||||
icBlocks = validProfile.icBlocks,
|
||||
targetBlocks = validProfile.targetBlocks,
|
||||
glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
|
||||
profileName = "SomeProfile",
|
||||
timeshift = 0,
|
||||
percentage = 100,
|
||||
duration = 0,
|
||||
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
|
||||
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
|
||||
},
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
var profileSwitch2 = (profileSwitch.toNSProfileSwitch(dateUtil).convertToRemoteAndBack() as NSProfileSwitch).toProfileSwitch(activePlugin, dateUtil)!!
|
||||
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
|
||||
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
|
||||
|
||||
profileSwitch = ProfileSwitch(
|
||||
timestamp = 10000,
|
||||
isValid = true,
|
||||
basalBlocks = validProfile.basalBlocks,
|
||||
isfBlocks = validProfile.isfBlocks,
|
||||
icBlocks = validProfile.icBlocks,
|
||||
targetBlocks = validProfile.targetBlocks,
|
||||
glucoseUnit = ProfileSwitch.GlucoseUnit.fromConstant(validProfile.units),
|
||||
profileName = "SomeProfile",
|
||||
timeshift = -3600000,
|
||||
percentage = 150,
|
||||
duration = 3600000,
|
||||
insulinConfiguration = activePlugin.activeInsulin.insulinConfiguration.also {
|
||||
it.insulinEndTime = (validProfile.dia * 3600 * 1000).toLong()
|
||||
},
|
||||
interfaceIDs_backing = InterfaceIDs(
|
||||
nightscoutId = "nightscoutId",
|
||||
pumpId = 11000,
|
||||
pumpType = InterfaceIDs.PumpType.DANA_I,
|
||||
pumpSerial = "bbbb"
|
||||
)
|
||||
)
|
||||
|
||||
profileSwitch2 = (profileSwitch.toNSProfileSwitch(dateUtil).convertToRemoteAndBack() as NSProfileSwitch).toProfileSwitch(activePlugin, dateUtil)!!
|
||||
Assertions.assertTrue(profileSwitch.contentEqualsTo(profileSwitch2))
|
||||
Assertions.assertTrue(profileSwitch.interfaceIdsEqualsTo(profileSwitch2))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue